Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ export const STATUS_SEVA_KUTIR = 'SEVA KUTIR';
export const STATUS_GUEST = 'GUEST';
export const AMT_TYPE_LATE_CHECKOUT_ROOM = 'late_checkout_room';


// ROOM
export const ROOM_DETAIL = 'Room Booking';
export const ROOM_WL = 'WL';
Expand Down Expand Up @@ -142,13 +141,18 @@ export const ERR_TRAVEL_ALREADY_BOOKED = 'Travel already booked';
export const ERR_FLAT_ALREADY_BOOKED =
'Flat already booked for one or more mumukshus during selected dates';
export const ERR_UTSAV_ALREADY_BOOKED = 'Utsav already booked';
export const ERR_UTSAV_NOT_FOUND = 'Utsav not found';
export const ERR_UTSAV_FEEDBACK_NOT_ALLOWED =
'You are not eligible to submit feedback for this utsav';
export const ERR_UTSAV_FEEDBACK_ALREADY_SUBMITTED =
'Feedback already submitted for this utsav';

export const ERR_FEEDBACK_ALREADY_SUBMITTED =
'Feedback already submitted for this adhyayan';
export const ERR_FEEDBACK_NOT_ALLOWED =
'You are not eligible to submit feedback for this adhyayan';
export const ERR_ADHYAYAN_NOT_COMPLETED =
'Cannot submit feedback for ongoing or future adhyayan';
'c';

export const MSG_BOOKING_SUCCESSFUL = 'Booking successful';
export const MSG_UPDATE_SUCCESSFUL = 'Update successful';
Expand Down
176 changes: 169 additions & 7 deletions controllers/client/utsavBooking.controller.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import {
ERR_BOOKING_NOT_FOUND,
MSG_CANCEL_SUCCESSFUL
MSG_CANCEL_SUCCESSFUL,
STATUS_CONFIRMED,
ROOM_STATUS_CHECKEDIN,
FEEDBACK_ELIGIBILITY_HOUR
} from '../../config/constants.js';
import {
UtsavBooking,
UtsavDb,
UtsavPackagesDb
UtsavFeedback
} from '../../models/associations.js';
import { userCancelBooking } from '../../helpers/transactions.helper.js';
import { openUtsavSeat, sendUtsavBookingUpdateEmail } from '../../helpers/utsavBooking.helper.js';
import moment from 'moment';
import {
openUtsavSeat,
sendUtsavBookingUpdateEmail,
validateFeedbackEligibility
} from '../../helpers/utsavBooking.helper.js';
import moment from 'moment-timezone';
import database from '../../config/database.js';
import ApiError from '../../utils/ApiError.js';

Expand Down Expand Up @@ -83,6 +90,57 @@ export const FetchUpcoming = async (req, res) => {
return res.status(200).send(formattedResponse);
};

// export const ViewUtsavBookings = async (req, res) => {
// const page = parseInt(req.query.page) || 1;
// const pageSize = parseInt(req.query.page_size) || 10;
// const offset = (page - 1) * pageSize;

// const utsavs = await database.query(
// `
// SELECT t1.bookingid,
// t1.utsavid,
// t2.name AS utsav_name,
// t2.start_date AS utsav_start_date,
// t2.end_date AS utsav_end_date,
// t2.month,
// t2.location AS utsav_location,
// t1.packageid,
// t3.name AS package_name,
// t3.start_date AS package_start,
// t3.end_date AS package_end,
// t1.volunteer,
// t1.cardno,
// t1.bookedBy,
// t1.roomno as stay,
// t5.issuedto AS user_name,
// t1.status,
// t4.status AS transaction_status,
// t4.amount,
// t2.createdAt AS created_at
// FROM utsav_booking t1
// LEFT JOIN utsav_db t2 ON t1.utsavid = t2.id
// LEFT JOIN utsav_packages_db t3 ON t3.id = t1.packageid
// LEFT JOIN card_db t5 ON t5.cardno = t1.cardno
// LEFT JOIN transactions t4 ON t4.bookingid = t1.bookingid
// WHERE t1.cardno = :cardno OR t1.bookedBy = :cardno
// ORDER BY created_at DESC
// LIMIT :limit
// OFFSET :offset;
// `,
// {
// replacements: {
// cardno: req.user.cardno,
// limit: pageSize,
// offset: offset
// },
// type: database.QueryTypes.SELECT,
// raw: true
// }
// );

// return res.status(200).send({ data: utsavs });
// };

export const ViewUtsavBookings = async (req, res) => {
const page = parseInt(req.query.page) || 1;
const pageSize = parseInt(req.query.page_size) || 10;
Expand Down Expand Up @@ -131,7 +189,30 @@ export const ViewUtsavBookings = async (req, res) => {
}
);

// ✅ ADD THIS BLOCK (feedback eligibility)

const now = moment().tz('Asia/Kolkata');

utsavs.forEach((utsav) => {
const feedbackStartDate = moment(utsav.utsav_start_date)
.tz('Asia/Kolkata')
.hour(FEEDBACK_ELIGIBILITY_HOUR)
.minute(0)
.second(0);

const daysSinceStart = now.diff(feedbackStartDate, 'days');

const normalizedStatus = (utsav.status || '').toLowerCase();

utsav.showFeedback =
!now.isBefore(feedbackStartDate) && // after start time
daysSinceStart <= 8 && // SAME window as validator
['confirmed', 'checkedin'].includes(normalizedStatus);
});
console.log('DEBUG SHOW FEEDBACK:', utsavs[0]);

return res.status(200).send({ data: utsavs });

};

export const CancelUtsavBooking = async (req, res) => {
Expand Down Expand Up @@ -162,9 +243,8 @@ export const CancelUtsavBooking = async (req, res) => {
where: { id: booking.utsavid }
});
await openUtsavSeat(utsav, booking.cardno, req.user.username, t);

await t.commit();


if (booking.bookedBy) {
const other = getOtherBookingUser(booking, req.user.cardno);
Expand All @@ -181,7 +261,6 @@ export const CancelUtsavBooking = async (req, res) => {
});
}
}


await sendUtsavBookingUpdateEmail(booking, utsav);

Expand Down Expand Up @@ -233,3 +312,86 @@ export const FetchUtsavById = async (req, res) => {

return res.status(200).send({ data: utsav[0] });
};

export const validateUtsavFeedback = async (req, res) => {
const { utsav_id } = req.query;

if (!utsav_id) {
throw new ApiError(400, 'Utsav ID is required');
}

await validateFeedbackEligibility(req.user.cardno, utsav_id);

return res.status(200).send({
message: 'Feedback is allowed'
});
};

export const submitUtsavFeedback = async (req, res) => {
const { utsav_id } = req.body;

if (!utsav_id) {
throw new ApiError(400, 'utsav_id is required');
}

await validateFeedbackEligibility(req.user.cardno, utsav_id);

const {
accommodation_rating,
qr_rating,
food_rating,
program_rating,
volunteer_rating,
infrastructure_rating,
decor_rating,
internal_transport_rating,
raj_pravas_rating,
sparsh_rating,
av_rating,
loved_most,
improvement_suggestions
} = req.body;

const requiredFields = [
accommodation_rating,
qr_rating,
food_rating,
program_rating,
volunteer_rating,
infrastructure_rating,
decor_rating,
internal_transport_rating,
raj_pravas_rating,
sparsh_rating,
av_rating,
loved_most,
improvement_suggestions
];

if (requiredFields.some((f) => f === null || f === undefined || f === '')) {
throw new ApiError(400, 'All feedback fields are required');
}

await UtsavFeedback.create({
cardno: req.user.cardno,
utsav_id,
accommodation_rating,
qr_rating,
food_rating,
program_rating,
volunteer_rating,
infrastructure_rating,
decor_rating,
internal_transport_rating,
raj_pravas_rating,
sparsh_rating,
av_rating,
loved_most,
improvement_suggestions
});

return res.status(201).json({
success: true,
message: 'Utsav feedback submitted successfully'
});
};
69 changes: 67 additions & 2 deletions helpers/utsavBooking.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import {
ERR_UTSAV_ALREADY_BOOKED,
STATUS_AVAILABLE,
STATUS_CANCELLED,
STATUS_ADMIN_CANCELLED
STATUS_ADMIN_CANCELLED,
ERR_UTSAV_NOT_FOUND,
FEEDBACK_ELIGIBILITY_HOUR,
ERR_UTSAV_FEEDBACK_NOT_ALLOWED,
STATUS_CASH_COMPLETED,
ERR_UTSAV_FEEDBACK_ALREADY_SUBMITTED,
ROOM_STATUS_CHECKEDIN
} from '../config/constants.js';
import {
UtsavDb,
UtsavPackagesDb,
UtsavBooking,
CardDb
CardDb,
UtsavFeedback
} from '../models/associations.js';
import {
createPendingTransaction,
Expand All @@ -28,6 +35,7 @@ import {
isDateRangeOverlapping,
validateBlockedDates
} from '../controllers/helper.js';
import moment from 'moment';
import database from '../config/database.js';
import sendMail from '../utils/sendMail.js';
const SAMVATSARI_PACKAGE_ID = 21;
Expand Down Expand Up @@ -533,3 +541,60 @@ export async function findUtsavOnBoundaryDates(checkin, checkout) {

return utsav;
}

export async function validateFeedbackEligibility(cardno, utsav_id) {
const utsav = await UtsavDb.findOne({
where: { id: utsav_id }
});

if (!utsav) {
throw new ApiError(404, ERR_UTSAV_NOT_FOUND);
}

const now = moment().tz('Asia/Kolkata');
const feedbackStartDate = moment(utsav.start_date)
.tz('Asia/Kolkata')
.hour(FEEDBACK_ELIGIBILITY_HOUR)
.minute(0)
.second(0);

// Check if feedback period has started
if (now.isBefore(feedbackStartDate)) {
throw new ApiError(400, ERR_UTSAV_FEEDBACK_NOT_ALLOWED);
}

// Check if more than 15 days have passed since adhyayan ended
const daysSinceEnd = now.diff(feedbackStartDate, 'days');
if (daysSinceEnd > 8) {
throw new ApiError(
400,
'Feedback submission is only allowed within 8 days after the utsav ends'
);
}

// Check if user has a confirmed booking for this adhyayan
const booking = await UtsavBooking.findOne({
where: {
cardno,
utsavid: utsav_id,
status: [STATUS_CONFIRMED, STATUS_CASH_COMPLETED, ROOM_STATUS_CHECKEDIN]
}
});

if (!booking) {
throw new ApiError(403, ERR_UTSAV_FEEDBACK_NOT_ALLOWED);
}

const existingFeedback = await UtsavFeedback.findOne({
where: {
cardno,
utsav_id
}
});

if (existingFeedback) {
throw new ApiError(400, ERR_UTSAV_FEEDBACK_ALREADY_SUBMITTED);
}

return { utsav, booking };
}
Loading