Compare commits
2 Commits
a65d03ccdd
...
ace90b9e10
| Author | SHA1 | Date | |
|---|---|---|---|
| ace90b9e10 | |||
|
|
2d1409682f |
@ -367,13 +367,18 @@ export default function AppointmentDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Selected Slots */}
|
{/* Selected Slots (replacing Matching Slots) */}
|
||||||
{appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
|
{appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
|
||||||
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
||||||
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
||||||
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-blue-400" : "text-blue-600"}`} />
|
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
||||||
Selected Time Slots
|
Selected Time Slots
|
||||||
|
{appointment.are_preferences_available !== undefined && (
|
||||||
|
<span className={`ml-auto px-3 py-1 text-xs font-medium rounded-full ${appointment.are_preferences_available ? (isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200") : (isDark ? "bg-yellow-500/20 text-yellow-300 border border-yellow-500/30" : "bg-yellow-50 text-yellow-700 border border-yellow-200")}`}>
|
||||||
|
{appointment.are_preferences_available ? "All Available" : "Partially Available"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
@ -397,87 +402,6 @@ export default function AppointmentDetailPage() {
|
|||||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
||||||
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`px-4 py-3 rounded-xl border ${isDark ? "bg-blue-500/10 border-blue-500/30" : "bg-blue-50 border-blue-200"}`}
|
|
||||||
>
|
|
||||||
<p className={`text-sm font-semibold ${isDark ? "text-blue-300" : "text-blue-700"}`}>
|
|
||||||
{dayName}
|
|
||||||
</p>
|
|
||||||
<p className={`text-xs mt-1 ${isDark ? "text-blue-400" : "text-blue-600"}`}>
|
|
||||||
{timeLabel}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Matching Slots */}
|
|
||||||
{(() => {
|
|
||||||
// Check if matching_availability is a MatchingAvailability object with matching_slots
|
|
||||||
const matchingAvailability = appointment.matching_availability as any;
|
|
||||||
const hasMatchingSlots = matchingAvailability && matchingAvailability.matching_slots && Array.isArray(matchingAvailability.matching_slots) && matchingAvailability.matching_slots.length > 0;
|
|
||||||
const isArrayFormat = Array.isArray(matchingAvailability) && matchingAvailability.length > 0;
|
|
||||||
|
|
||||||
if (!hasMatchingSlots && !isArrayFormat) return null;
|
|
||||||
|
|
||||||
const dayNames: Record<number, string> = {
|
|
||||||
0: "Monday",
|
|
||||||
1: "Tuesday",
|
|
||||||
2: "Wednesday",
|
|
||||||
3: "Thursday",
|
|
||||||
4: "Friday",
|
|
||||||
5: "Saturday",
|
|
||||||
6: "Sunday",
|
|
||||||
};
|
|
||||||
const timeSlotLabels: Record<string, string> = {
|
|
||||||
morning: "Morning",
|
|
||||||
afternoon: "Lunchtime",
|
|
||||||
evening: "Evening",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get matching slots from MatchingAvailability object
|
|
||||||
const matchingSlots = hasMatchingSlots ? matchingAvailability.matching_slots : null;
|
|
||||||
const totalMatchingSlots = hasMatchingSlots ? matchingAvailability.total_matching_slots : null;
|
|
||||||
const preferencesMatch = hasMatchingSlots ? matchingAvailability.preferences_match_availability : appointment.are_preferences_available;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
|
||||||
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
|
||||||
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
|
||||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
|
||||||
Matching Slots
|
|
||||||
{preferencesMatch !== undefined && (
|
|
||||||
<span className={`ml-auto px-3 py-1 text-xs font-medium rounded-full ${preferencesMatch ? (isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200") : (isDark ? "bg-yellow-500/20 text-yellow-300 border border-yellow-500/30" : "bg-yellow-50 text-yellow-700 border border-yellow-200")}`}>
|
|
||||||
{preferencesMatch ? "All Available" : "Partially Available"}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
{hasMatchingSlots && totalMatchingSlots && (
|
|
||||||
<p className={`text-sm mb-4 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
|
||||||
Found {totalMatchingSlots} matching time slot{totalMatchingSlots !== 1 ? 's' : ''} that match your selected preferences:
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{!hasMatchingSlots && (
|
|
||||||
<p className={`text-sm mb-4 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
|
||||||
These are the available time slots that match your selected preferences:
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hasMatchingSlots ? (
|
|
||||||
// Display matching_slots from MatchingAvailability object
|
|
||||||
<div className="flex flex-wrap gap-3">
|
|
||||||
{matchingSlots.map((slot: any, idx: number) => {
|
|
||||||
const dayName = dayNames[slot.day] || `Day ${slot.day}`;
|
|
||||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
|
||||||
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
@ -489,54 +413,18 @@ export default function AppointmentDetailPage() {
|
|||||||
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
||||||
{timeLabel}
|
{timeLabel}
|
||||||
</p>
|
</p>
|
||||||
|
{slot.date && (
|
||||||
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
{formatShortDate(slot.date)}
|
{formatShortDate(slot.date)}
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
// Display array format (legacy)
|
|
||||||
<div className="space-y-4">
|
|
||||||
{(matchingAvailability as any[]).map((match: any, idx: number) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-start justify-between mb-3">
|
|
||||||
<div>
|
|
||||||
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
|
|
||||||
{match.day_name || "Unknown Day"}
|
|
||||||
</p>
|
|
||||||
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
|
||||||
{formatShortDate(match.date || match.date_obj || "")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{match.available_slots && Array.isArray(match.available_slots) && match.available_slots.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2 mt-3">
|
|
||||||
{match.available_slots.map((slot: string, slotIdx: number) => {
|
|
||||||
const normalizedSlot = String(slot).toLowerCase().trim();
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
key={slotIdx}
|
|
||||||
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"}`}
|
|
||||||
>
|
|
||||||
{timeSlotLabels[normalizedSlot] || slot}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
|
|
||||||
{/* Reason */}
|
{/* Reason */}
|
||||||
{appointment.reason && (
|
{appointment.reason && (
|
||||||
|
|||||||
@ -306,13 +306,18 @@ export default function UserAppointmentDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Selected Slots */}
|
{/* Selected Slots (replacing Matching Slots) */}
|
||||||
{appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
|
{appointment.selected_slots && Array.isArray(appointment.selected_slots) && appointment.selected_slots.length > 0 && (
|
||||||
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
||||||
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
||||||
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
||||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-blue-400" : "text-blue-600"}`} />
|
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
||||||
Selected Time Slots
|
Selected Time Slots
|
||||||
|
{appointment.are_preferences_available !== undefined && (
|
||||||
|
<span className={`ml-auto px-3 py-1 text-xs font-medium rounded-full ${appointment.are_preferences_available ? (isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200") : (isDark ? "bg-yellow-500/20 text-yellow-300 border border-yellow-500/30" : "bg-yellow-50 text-yellow-700 border border-yellow-200")}`}>
|
||||||
|
{appointment.are_preferences_available ? "All Available" : "Partially Available"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
@ -336,87 +341,6 @@ export default function UserAppointmentDetailPage() {
|
|||||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
||||||
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`px-4 py-3 rounded-xl border ${isDark ? "bg-blue-500/10 border-blue-500/30" : "bg-blue-50 border-blue-200"}`}
|
|
||||||
>
|
|
||||||
<p className={`text-sm font-semibold ${isDark ? "text-blue-300" : "text-blue-700"}`}>
|
|
||||||
{dayName}
|
|
||||||
</p>
|
|
||||||
<p className={`text-xs mt-1 ${isDark ? "text-blue-400" : "text-blue-600"}`}>
|
|
||||||
{timeLabel}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Matching Slots */}
|
|
||||||
{(() => {
|
|
||||||
// Check if matching_availability is a MatchingAvailability object with matching_slots
|
|
||||||
const matchingAvailability = appointment.matching_availability as any;
|
|
||||||
const hasMatchingSlots = matchingAvailability && matchingAvailability.matching_slots && Array.isArray(matchingAvailability.matching_slots) && matchingAvailability.matching_slots.length > 0;
|
|
||||||
const isArrayFormat = Array.isArray(matchingAvailability) && matchingAvailability.length > 0;
|
|
||||||
|
|
||||||
if (!hasMatchingSlots && !isArrayFormat) return null;
|
|
||||||
|
|
||||||
const dayNames: Record<number, string> = {
|
|
||||||
0: "Monday",
|
|
||||||
1: "Tuesday",
|
|
||||||
2: "Wednesday",
|
|
||||||
3: "Thursday",
|
|
||||||
4: "Friday",
|
|
||||||
5: "Saturday",
|
|
||||||
6: "Sunday",
|
|
||||||
};
|
|
||||||
const timeSlotLabels: Record<string, string> = {
|
|
||||||
morning: "Morning",
|
|
||||||
afternoon: "Lunchtime",
|
|
||||||
evening: "Evening",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get matching slots from MatchingAvailability object
|
|
||||||
const matchingSlots = hasMatchingSlots ? matchingAvailability.matching_slots : null;
|
|
||||||
const totalMatchingSlots = hasMatchingSlots ? matchingAvailability.total_matching_slots : null;
|
|
||||||
const preferencesMatch = hasMatchingSlots ? matchingAvailability.preferences_match_availability : appointment.are_preferences_available;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`rounded-2xl border shadow-sm overflow-hidden ${isDark ? "bg-gray-800 border-gray-700" : "bg-white border-gray-200"}`}>
|
|
||||||
<div className={`px-6 py-4 border-b ${isDark ? "border-gray-700 bg-gray-800/50" : "border-gray-200 bg-gray-50/50"}`}>
|
|
||||||
<h2 className={`text-lg font-semibold flex items-center gap-2 ${isDark ? "text-white" : "text-gray-900"}`}>
|
|
||||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
|
||||||
Matching Slots
|
|
||||||
{preferencesMatch !== undefined && (
|
|
||||||
<span className={`ml-auto px-3 py-1 text-xs font-medium rounded-full ${preferencesMatch ? (isDark ? "bg-green-500/20 text-green-300 border border-green-500/30" : "bg-green-50 text-green-700 border border-green-200") : (isDark ? "bg-yellow-500/20 text-yellow-300 border border-yellow-500/30" : "bg-yellow-50 text-yellow-700 border border-yellow-200")}`}>
|
|
||||||
{preferencesMatch ? "All Available" : "Partially Available"}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div className="p-6">
|
|
||||||
{hasMatchingSlots && totalMatchingSlots && (
|
|
||||||
<p className={`text-sm mb-4 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
|
||||||
Found {totalMatchingSlots} matching time slot{totalMatchingSlots !== 1 ? 's' : ''} that match your selected preferences:
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{!hasMatchingSlots && (
|
|
||||||
<p className={`text-sm mb-4 ${isDark ? "text-gray-400" : "text-gray-600"}`}>
|
|
||||||
These are the available time slots that match your selected preferences:
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{hasMatchingSlots ? (
|
|
||||||
// Display matching_slots from MatchingAvailability object
|
|
||||||
<div className="flex flex-wrap gap-3">
|
|
||||||
{matchingSlots.map((slot: any, idx: number) => {
|
|
||||||
const dayName = dayNames[slot.day] || `Day ${slot.day}`;
|
|
||||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
|
||||||
const timeLabel = timeSlotLabels[timeSlot] || slot.time_slot;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
@ -428,54 +352,18 @@ export default function UserAppointmentDetailPage() {
|
|||||||
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
||||||
{timeLabel}
|
{timeLabel}
|
||||||
</p>
|
</p>
|
||||||
|
{slot.date && (
|
||||||
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||||
{formatShortDate(slot.date)}
|
{formatShortDate(slot.date)}
|
||||||
</p>
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
// Display array format (legacy)
|
|
||||||
<div className="space-y-4">
|
|
||||||
{(matchingAvailability as any[]).map((match: any, idx: number) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`p-4 rounded-xl border ${isDark ? "bg-gray-700/50 border-gray-600" : "bg-gray-50 border-gray-200"}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-start justify-between mb-3">
|
|
||||||
<div>
|
|
||||||
<p className={`text-base font-semibold ${isDark ? "text-white" : "text-gray-900"}`}>
|
|
||||||
{match.day_name || "Unknown Day"}
|
|
||||||
</p>
|
|
||||||
<p className={`text-sm mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
|
||||||
{formatShortDate(match.date || match.date_obj || "")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{match.available_slots && Array.isArray(match.available_slots) && match.available_slots.length > 0 && (
|
|
||||||
<div className="flex flex-wrap gap-2 mt-3">
|
|
||||||
{match.available_slots.map((slot: string, slotIdx: number) => {
|
|
||||||
const normalizedSlot = String(slot).toLowerCase().trim();
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
key={slotIdx}
|
|
||||||
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"}`}
|
|
||||||
>
|
|
||||||
{timeSlotLabels[normalizedSlot] || slot}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
|
|
||||||
{/* Reason */}
|
{/* Reason */}
|
||||||
{appointment.reason && (
|
{appointment.reason && (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user