Add calendar event lifecycle management and "Add to Calendar" feature
- Migrate consulting_slots to consulting_sessions structure - Add calendar_event_id to track Google Calendar events - Create delete-calendar-event edge function for auto-cleanup - Add "Tambah ke Kalender" button for members (OrderDetail, ConsultingHistory) - Update create-google-meet-event to store calendar event ID - Update handle-order-paid to use consulting_sessions table - Remove deprecated create-meet-link function - Add comprehensive documentation (CALENDAR_INTEGRATION.md, MIGRATION_GUIDE.md) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,8 +31,8 @@ interface Workhour {
|
||||
end_time: string;
|
||||
}
|
||||
|
||||
interface ConfirmedSlot {
|
||||
date: string;
|
||||
interface ConfirmedSession {
|
||||
session_date: string;
|
||||
start_time: string;
|
||||
end_time: string;
|
||||
}
|
||||
@@ -107,9 +107,9 @@ export default function ConsultingBooking() {
|
||||
const fetchConfirmedSlots = async (date: Date) => {
|
||||
const dateStr = format(date, 'yyyy-MM-dd');
|
||||
const { data } = await supabase
|
||||
.from('consulting_slots')
|
||||
.select('date, start_time, end_time')
|
||||
.eq('date', dateStr)
|
||||
.from('consulting_sessions')
|
||||
.select('session_date, start_time, end_time')
|
||||
.eq('session_date', dateStr)
|
||||
.in('status', ['pending_payment', 'confirmed']);
|
||||
|
||||
if (data) setConfirmedSlots(data);
|
||||
@@ -331,26 +331,55 @@ export default function ConsultingBooking() {
|
||||
|
||||
if (orderError) throw orderError;
|
||||
|
||||
// Create consulting slots
|
||||
const slotsToInsert = getSlotsInRange.map(slotStart => {
|
||||
// Create consulting session and time slots
|
||||
const firstSlotStart = getSlotsInRange[0];
|
||||
const lastSlotEnd = format(
|
||||
addMinutes(parse(getSlotsInRange[getSlotsInRange.length - 1], 'HH:mm', new Date()), settings.consulting_block_duration_minutes),
|
||||
'HH:mm'
|
||||
);
|
||||
|
||||
// Calculate session duration in minutes
|
||||
const sessionDurationMinutes = totalBlocks * settings.consulting_block_duration_minutes;
|
||||
|
||||
// Create the session record (ONE row per booking)
|
||||
const { data: session, error: sessionError } = await supabase
|
||||
.from('consulting_sessions')
|
||||
.insert({
|
||||
user_id: user.id,
|
||||
order_id: order.id,
|
||||
session_date: format(selectedDate, 'yyyy-MM-dd'),
|
||||
start_time: firstSlotStart + ':00',
|
||||
end_time: lastSlotEnd + ':00',
|
||||
total_duration_minutes: sessionDurationMinutes,
|
||||
topic_category: selectedCategory,
|
||||
notes: notes,
|
||||
status: 'pending_payment',
|
||||
total_blocks: totalBlocks,
|
||||
total_price: totalPrice,
|
||||
})
|
||||
.select()
|
||||
.single();
|
||||
|
||||
if (sessionError) throw sessionError;
|
||||
|
||||
// Create time slots for availability tracking (MULTIPLE rows per booking)
|
||||
const timeSlotsToInsert = getSlotsInRange.map(slotStart => {
|
||||
const slotEnd = format(
|
||||
addMinutes(parse(slotStart, 'HH:mm', new Date()), settings.consulting_block_duration_minutes),
|
||||
'HH:mm'
|
||||
);
|
||||
return {
|
||||
user_id: user.id,
|
||||
order_id: order.id,
|
||||
date: format(selectedDate, 'yyyy-MM-dd'),
|
||||
session_id: session.id,
|
||||
slot_date: format(selectedDate, 'yyyy-MM-dd'),
|
||||
start_time: slotStart + ':00',
|
||||
end_time: slotEnd + ':00',
|
||||
status: 'pending_payment',
|
||||
topic_category: selectedCategory,
|
||||
notes: notes,
|
||||
is_available: false,
|
||||
booked_at: new Date().toISOString(),
|
||||
};
|
||||
});
|
||||
|
||||
const { error: slotsError } = await supabase.from('consulting_slots').insert(slotsToInsert);
|
||||
if (slotsError) throw slotsError;
|
||||
const { error: timeSlotsError } = await supabase.from('consulting_time_slots').insert(timeSlotsToInsert);
|
||||
if (timeSlotsError) throw timeSlotsError;
|
||||
|
||||
// Call edge function to create payment with QR code
|
||||
const { data: paymentData, error: paymentError } = await supabase.functions.invoke('create-payment', {
|
||||
|
||||
Reference in New Issue
Block a user