Refactor appointment detail rendering to replace "Matching Slots" with "Selected Slots" for improved clarity. Update UI elements to reflect changes in color scheme and enhance the display of availability preferences. Remove legacy matching slots logic … #41
@ -367,13 +367,18 @@ export default function AppointmentDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Selected Slots */}
|
||||
{/* Selected Slots (replacing Matching Slots) */}
|
||||
{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={`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-blue-400" : "text-blue-600"}`} />
|
||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
||||
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>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
@ -397,87 +402,6 @@ export default function AppointmentDetailPage() {
|
||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
||||
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 (
|
||||
<div
|
||||
key={idx}
|
||||
@ -489,54 +413,18 @@ export default function AppointmentDetailPage() {
|
||||
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
||||
{timeLabel}
|
||||
</p>
|
||||
{slot.date && (
|
||||
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||
{formatShortDate(slot.date)}
|
||||
</p>
|
||||
)}
|
||||
</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>
|
||||
{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 */}
|
||||
{appointment.reason && (
|
||||
|
||||
@ -306,13 +306,18 @@ export default function UserAppointmentDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Selected Slots */}
|
||||
{/* Selected Slots (replacing Matching Slots) */}
|
||||
{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={`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-blue-400" : "text-blue-600"}`} />
|
||||
<CalendarCheck className={`w-5 h-5 ${isDark ? "text-green-400" : "text-green-600"}`} />
|
||||
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>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
@ -336,87 +341,6 @@ export default function UserAppointmentDetailPage() {
|
||||
const timeSlot = String(slot.time_slot).toLowerCase().trim();
|
||||
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 (
|
||||
<div
|
||||
key={idx}
|
||||
@ -428,54 +352,18 @@ export default function UserAppointmentDetailPage() {
|
||||
<p className={`text-xs mt-1 ${isDark ? "text-green-400" : "text-green-600"}`}>
|
||||
{timeLabel}
|
||||
</p>
|
||||
{slot.date && (
|
||||
<p className={`text-xs mt-1 ${isDark ? "text-gray-400" : "text-gray-500"}`}>
|
||||
{formatShortDate(slot.date)}
|
||||
</p>
|
||||
)}
|
||||
</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>
|
||||
{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 */}
|
||||
{appointment.reason && (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user