Compare commits

...

2 Commits

3 changed files with 123 additions and 83 deletions

View File

@ -413,6 +413,13 @@ export default function AppointmentDetailPage() {
evening: "Evening", evening: "Evening",
}; };
// Time slot order: morning, afternoon (lunchtime), evening
const timeSlotOrder: Record<string, number> = {
morning: 0,
afternoon: 1,
evening: 2,
};
// Group slots by date // Group slots by date
const slotsByDate: Record<string, typeof appointment.selected_slots> = {}; const slotsByDate: Record<string, typeof appointment.selected_slots> = {};
appointment.selected_slots.forEach((slot: any) => { appointment.selected_slots.forEach((slot: any) => {
@ -423,36 +430,52 @@ export default function AppointmentDetailPage() {
slotsByDate[date].push(slot); slotsByDate[date].push(slot);
}); });
// Sort dates and slots within each date
const sortedDates = Object.keys(slotsByDate).sort((a, b) => {
return new Date(a).getTime() - new Date(b).getTime();
});
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{Object.entries(slotsByDate).map(([date, slots]) => ( {sortedDates.map((date) => {
<div key={date} className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}> // Sort slots within this date by time slot order
<div className="mb-3"> const slots = slotsByDate[date].sort((a: any, b: any) => {
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}> const aSlot = String(a.time_slot).toLowerCase().trim();
{formatShortDate(date)} const bSlot = String(b.time_slot).toLowerCase().trim();
</p> const aOrder = timeSlotOrder[aSlot] ?? 999;
{slots.length > 0 && slots[0]?.day !== undefined && ( const bOrder = timeSlotOrder[bSlot] ?? 999;
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}> return aOrder - bOrder;
{dayNames[slots[0].day] || `Day ${slots[0].day}`} });
return (
<div key={date} className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}>
<div className="mb-3">
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
{formatShortDate(date)}
</p> </p>
)} {slots.length > 0 && slots[0]?.day !== undefined && (
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
{dayNames[slots[0].day] || `Day ${slots[0].day}`}
</p>
)}
</div>
<div className="flex flex-wrap gap-2">
{slots.map((slot: any, idx: number) => {
const timeSlot = String(slot.time_slot).toLowerCase().trim();
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
return (
<span
key={idx}
className={`px-3 py-1.5 rounded-lg text-sm font-medium ${isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200"}`}
>
{timeLabel}
</span>
);
})}
</div>
</div> </div>
<div className="flex flex-wrap gap-2"> );
{slots.map((slot: any, idx: number) => { })}
const timeSlot = String(slot.time_slot).toLowerCase().trim();
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
return (
<span
key={idx}
className={`px-3 py-1.5 rounded-lg text-sm font-medium ${isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200"}`}
>
{timeLabel}
</span>
);
})}
</div>
</div>
))}
</div> </div>
); );
})()} })()}

View File

@ -154,12 +154,23 @@ export default function BookNowPage() {
} }
}); });
// Convert Map values to array and sort by day number // Time slot order: morning, afternoon (lunchtime), evening
const timeSlotOrder: Record<string, number> = {
morning: 0,
afternoon: 1,
evening: 2,
};
// Convert Map values to array, sort slots, and sort by day number
return Array.from(dayMap.values()) return Array.from(dayMap.values())
.map(day => ({ .map(day => ({
day: day.day, day: day.day,
dayName: day.dayName, dayName: day.dayName,
availableSlots: Array.from(day.availableSlots), availableSlots: Array.from(day.availableSlots).sort((a, b) => {
const aOrder = timeSlotOrder[a.toLowerCase().trim()] ?? 999;
const bOrder = timeSlotOrder[b.toLowerCase().trim()] ?? 999;
return aOrder - bOrder;
}),
})) }))
.sort((a, b) => a.day - b.day); .sort((a, b) => a.day - b.day);
} }
@ -172,6 +183,13 @@ export default function BookNowPage() {
: (weeklyAvailability as any)?.week; : (weeklyAvailability as any)?.week;
if (weekArray && Array.isArray(weekArray)) { if (weekArray && Array.isArray(weekArray)) {
// Time slot order: morning, afternoon (lunchtime), evening
const timeSlotOrder: Record<string, number> = {
morning: 0,
afternoon: 1,
evening: 2,
};
return weekArray return weekArray
.filter(day => { .filter(day => {
const dayNum = Number(day.day); const dayNum = Number(day.day);
@ -186,7 +204,11 @@ export default function BookNowPage() {
.map(day => ({ .map(day => ({
day: Number(day.day), day: Number(day.day),
dayName: day.day_name || 'Unknown', dayName: day.day_name || 'Unknown',
availableSlots: day.available_slots || [], availableSlots: (day.available_slots || []).sort((a: string, b: string) => {
const aOrder = timeSlotOrder[String(a).toLowerCase().trim()] ?? 999;
const bOrder = timeSlotOrder[String(b).toLowerCase().trim()] ?? 999;
return aOrder - bOrder;
}),
})) }))
.sort((a, b) => a.day - b.day); .sort((a, b) => a.day - b.day);
} }
@ -752,34 +774,6 @@ export default function BookNowPage() {
<p className={`text-xs ${isDark ? 'text-gray-400' : 'text-gray-500'} mb-3`}> <p className={`text-xs ${isDark ? 'text-gray-400' : 'text-gray-500'} mb-3`}>
Select one or more day-time combinations that work for you. You can select multiple time slots for the same day (e.g., Monday Morning and Monday Evening). Select one or more day-time combinations that work for you. You can select multiple time slots for the same day (e.g., Monday Morning and Monday Evening).
</p> </p>
{/* Selected Slots Summary */}
{formData.selectedSlots && formData.selectedSlots.length > 0 && (
<div className={`mb-4 p-3 rounded-lg border ${isDark ? 'bg-gray-800/50 border-gray-700' : 'bg-rose-50/50 border-rose-200'}`}>
<p className={`text-xs font-medium mb-2 ${isDark ? 'text-gray-300' : 'text-gray-700'}`}>
Selected: {formData.selectedSlots.length} time slot{formData.selectedSlots.length !== 1 ? 's' : ''}
</p>
<div className="flex flex-wrap gap-1.5">
{formData.selectedSlots.map((slot, idx) => {
const dayNames = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
const timeSlotLabels: Record<string, string> = {
morning: "Morning",
afternoon: "Lunchtime",
evening: "Evening",
};
const dayName = dayNames[slot.day] || `Day ${slot.day}`;
const timeLabel = timeSlotLabels[String(slot.time_slot).toLowerCase()] || slot.time_slot;
return (
<span
key={idx}
className={`px-2 py-1 rounded text-xs font-medium ${isDark ? 'bg-rose-600/30 text-rose-300 border border-rose-500/30' : 'bg-rose-100 text-rose-700 border border-rose-200'}`}
>
{dayName} {timeLabel}
</span>
);
})}
</div>
</div>
)}
<div className="space-y-4"> <div className="space-y-4">
{availableDaysOfWeek.map((dayInfo, dayIndex) => { {availableDaysOfWeek.map((dayInfo, dayIndex) => {
// Ensure day is always a valid number (already validated in useMemo) // Ensure day is always a valid number (already validated in useMemo)

View File

@ -352,6 +352,13 @@ export default function UserAppointmentDetailPage() {
evening: "Evening", evening: "Evening",
}; };
// Time slot order: morning, afternoon (lunchtime), evening
const timeSlotOrder: Record<string, number> = {
morning: 0,
afternoon: 1,
evening: 2,
};
// Group slots by date // Group slots by date
const slotsByDate: Record<string, typeof appointment.selected_slots> = {}; const slotsByDate: Record<string, typeof appointment.selected_slots> = {};
appointment.selected_slots.forEach((slot: any) => { appointment.selected_slots.forEach((slot: any) => {
@ -362,36 +369,52 @@ export default function UserAppointmentDetailPage() {
slotsByDate[date].push(slot); slotsByDate[date].push(slot);
}); });
// Sort dates and slots within each date
const sortedDates = Object.keys(slotsByDate).sort((a, b) => {
return new Date(a).getTime() - new Date(b).getTime();
});
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{Object.entries(slotsByDate).map(([date, slots]) => ( {sortedDates.map((date) => {
<div key={date} className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}> // Sort slots within this date by time slot order
<div className="mb-3"> const slots = slotsByDate[date].sort((a: any, b: any) => {
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}> const aSlot = String(a.time_slot).toLowerCase().trim();
{formatShortDate(date)} const bSlot = String(b.time_slot).toLowerCase().trim();
</p> const aOrder = timeSlotOrder[aSlot] ?? 999;
{slots.length > 0 && slots[0]?.day !== undefined && ( const bOrder = timeSlotOrder[bSlot] ?? 999;
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}> return aOrder - bOrder;
{dayNames[slots[0].day] || `Day ${slots[0].day}`} });
return (
<div key={date} className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}>
<div className="mb-3">
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
{formatShortDate(date)}
</p> </p>
)} {slots.length > 0 && slots[0]?.day !== undefined && (
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
{dayNames[slots[0].day] || `Day ${slots[0].day}`}
</p>
)}
</div>
<div className="flex flex-wrap gap-2">
{slots.map((slot: any, idx: number) => {
const timeSlot = String(slot.time_slot).toLowerCase().trim();
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
return (
<span
key={idx}
className={`px-3 py-1.5 rounded-lg text-sm font-medium ${isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200"}`}
>
{timeLabel}
</span>
);
})}
</div>
</div> </div>
<div className="flex flex-wrap gap-2"> );
{slots.map((slot: any, idx: number) => { })}
const timeSlot = String(slot.time_slot).toLowerCase().trim();
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
return (
<span
key={idx}
className={`px-3 py-1.5 rounded-lg text-sm font-medium ${isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200"}`}
>
{timeLabel}
</span>
);
})}
</div>
</div>
))}
</div> </div>
); );
})()} })()}