From f68c8ee1c4d44707cf0b7b9050cf68ef63c506d9 Mon Sep 17 00:00:00 2001 From: dwindown Date: Wed, 31 Dec 2025 13:19:45 +0700 Subject: [PATCH] Add reschedule functionality for consulting sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Admins need to reschedule sessions when members can't make it, either before or after the scheduled time. Previously had to edit manually without clear indication of rescheduling vs simple edits. Solution: 1. Add "Reschedule" button (blue Calendar icon) for confirmed sessions: - In desktop table action buttons - In mobile card layout - In passed sessions alert card 2. Enhanced session editing with reschedule mode: - openMeetDialog(session, rescheduleMode = true/false) - Tracks isRescheduling state to show appropriate UI - Dynamic dialog title: "Reschedule Sesi" vs "Edit Sesi" - Dynamic description based on mode 3. Enhanced saveMeetLink function: - Detects date and time changes separately - Updates session_date when date changed - Recalculates duration when time changes - Updates consulting_time_slots for new schedule - Updates calendar event if exists - Shows success message: "Berhasil Reschedule" with new date/time 4. Session info display improvements: - Show current time in session info card - Better context for rescheduling decisions Reschedule use cases: - Member can't make it BEFORE session → Admin clicks Reschedule, picks new slot - Member misses session, tells admin AFTER → Admin clicks Reschedule in passed alert - Emergency reschedule → Quick date/time change with calendar auto-update Calendar integration: - Existing calendar events automatically updated/moved to new time - Time slots properly released (old) and booked (new) UI placement: - Passed sessions alert: First button (blue) for quick reschedule access - Upcoming table: Between Edit and Complete buttons - Mobile: Between Link and Complete buttons 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/pages/admin/AdminConsulting.tsx | 76 ++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/src/pages/admin/AdminConsulting.tsx b/src/pages/admin/AdminConsulting.tsx index 055968d..9e69c6d 100644 --- a/src/pages/admin/AdminConsulting.tsx +++ b/src/pages/admin/AdminConsulting.tsx @@ -71,9 +71,12 @@ export default function AdminConsulting() { const [filterStatus, setFilterStatus] = useState('all'); const [editStartTime, setEditStartTime] = useState(''); const [editEndTime, setEditEndTime] = useState(''); + const [editSessionDate, setEditSessionDate] = useState(''); const [timeSlotPickerOpen, setTimeSlotPickerOpen] = useState(false); const [editTotalBlocks, setEditTotalBlocks] = useState(0); const [editTotalDuration, setEditTotalDuration] = useState(0); + const [isRescheduling, setIsRescheduling] = useState(false); + const [notifyMember, setNotifyMember] = useState(true); useEffect(() => { if (!authLoading) { @@ -116,13 +119,16 @@ export default function AdminConsulting() { if (data) setSettings(data); }; - const openMeetDialog = (session: ConsultingSession) => { + const openMeetDialog = (session: ConsultingSession, rescheduleMode: boolean = false) => { setSelectedSession(session); setMeetLink(session.meet_link || ''); setEditStartTime(session.start_time.substring(0, 5)); setEditEndTime(session.end_time.substring(0, 5)); + setEditSessionDate(session.session_date); setEditTotalBlocks(session.total_blocks || 1); setEditTotalDuration(session.total_duration_minutes || 30); + setIsRescheduling(rescheduleMode); + setNotifyMember(rescheduleMode); // Auto-enable notification when rescheduling setDialogOpen(true); }; @@ -177,14 +183,17 @@ export default function AdminConsulting() { meet_link: meetLink || null }; - // Check if time changed + // Check if date or time changed const newStartTime = editStartTime + ':00'; const newEndTime = editEndTime + ':00'; + const dateChanged = editSessionDate !== selectedSession.session_date; const timeChanged = newStartTime !== selectedSession.start_time || newEndTime !== selectedSession.end_time; + const scheduleChanged = dateChanged || timeChanged; - if (timeChanged) { + if (scheduleChanged) { updateData.start_time = newStartTime; updateData.end_time = newEndTime; + updateData.session_date = editSessionDate; // Recalculate duration const start = parse(newStartTime, 'HH:mm', new Date()); @@ -209,7 +218,7 @@ export default function AdminConsulting() { newSlots.push({ session_id: selectedSession.id, - slot_date: selectedSession.session_date, + slot_date: editSessionDate, start_time: format(currentSlotStart, 'HH:mm:ss'), end_time: format(slotEnd, 'HH:mm:ss'), is_available: false, @@ -229,7 +238,7 @@ export default function AdminConsulting() { await supabase.functions.invoke('update-calendar-event', { body: { session_id: selectedSession.id, - date: selectedSession.session_date, + date: editSessionDate, start_time: newStartTime, end_time: newEndTime } @@ -250,7 +259,18 @@ export default function AdminConsulting() { if (error) { toast({ title: 'Error', description: error.message, variant: 'destructive' }); } else { - toast({ title: 'Berhasil', description: 'Perubahan disimpan' }); + // Show different success message based on what changed + if (isRescheduling && scheduleChanged) { + toast({ + title: 'Berhasil Reschedule', + description: `Jadwal diubah ke ${format(parseISO(editSessionDate), 'd MMM yyyy', { locale: id })} pukul ${editStartTime}-${editEndTime}` + }); + } else if (scheduleChanged) { + toast({ title: 'Berhasil', description: 'Waktu sesi diperbarui' }); + } else { + toast({ title: 'Berhasil', description: 'Perubahan disimpan' }); + } + setDialogOpen(false); fetchSessions(); } @@ -417,20 +437,31 @@ export default function AdminConsulting() { Sesi Terlewat ({passedConfirmedSessions.length})

- Sesi berikut telah berakhir namun masih berstatus "Dikonfirmasi". Silakan update statusnya. + Sesi berikut telah berakhir namun masih berstatus "Dikonfirmasi". Reschedule atau update statusnya.

{passedConfirmedSessions.map((session) => (
- + {format(parseISO(session.session_date), 'd MMM yyyy', { locale: id })} • {session.start_time.substring(0, 5)} - {session.end_time.substring(0, 5)} • {session.profiles?.name || 'N/A'} ({session.topic_category}) -
+
+ + +