Implement availability management in Scheduling component

- Added functionality to load user availability from the database on component mount.
- Integrated create and delete availability features with the backend.
- Refactored event handling to manage availability slots dynamically.
- Updated supabase.ts to include methods for fetching, creating, and deleting availability records.
This commit is contained in:
salirezav
2025-09-22 11:34:58 -04:00
parent 44c8c3f6dd
commit 08538c87c3
2 changed files with 121 additions and 44 deletions

View File

@@ -1,8 +1,9 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
// @ts-ignore - react-big-calendar types not available
import { Calendar, momentLocalizer, Views } from 'react-big-calendar'
import moment from 'moment'
import type { User } from '../lib/supabase'
import { availabilityManagement } from '../lib/supabase'
import 'react-big-calendar/lib/css/react-big-calendar.css'
import './CalendarStyles.css'
@@ -331,36 +332,7 @@ function ScheduleExperiment({ user, onBack }: { user: User; onBack: () => void }
function AvailabilityCalendar({ user }: { user: User }) {
// User context available for future features like saving preferences
const localizer = momentLocalizer(moment)
const [events, setEvents] = useState<CalendarEvent[]>([
{
id: 1,
title: 'Available - Morning',
start: new Date(2024, 11, 15, 9, 0), // December 15, 2024, 9:00 AM
end: new Date(2024, 11, 15, 12, 0), // December 15, 2024, 12:00 PM
resource: 'available'
},
{
id: 2,
title: 'Available - Afternoon',
start: new Date(2024, 11, 15, 14, 0), // December 15, 2024, 2:00 PM
end: new Date(2024, 11, 15, 17, 0), // December 15, 2024, 5:00 PM
resource: 'available'
},
{
id: 3,
title: 'Available - Full Day',
start: new Date(2024, 11, 16, 9, 0), // December 16, 2024, 9:00 AM
end: new Date(2024, 11, 16, 17, 0), // December 16, 2024, 5:00 PM
resource: 'available'
},
{
id: 4,
title: 'Unavailable - Personal',
start: new Date(2024, 11, 20, 0, 0), // December 20, 2024
end: new Date(2024, 11, 20, 23, 59), // December 20, 2024
resource: 'unavailable'
}
])
const [events, setEvents] = useState<CalendarEvent[]>([])
const [selectedDate, setSelectedDate] = useState<Date | null>(null)
const [showTimeSlotForm, setShowTimeSlotForm] = useState(false)
@@ -370,6 +342,26 @@ function AvailabilityCalendar({ user }: { user: User }) {
})
const [currentView, setCurrentView] = useState(Views.MONTH)
// Load availability from DB on mount
useEffect(() => {
const loadAvailability = async () => {
try {
const records = await availabilityManagement.getMyAvailability()
const mapped: CalendarEvent[] = records.map(r => ({
id: r.id,
title: 'Available',
start: new Date(r.available_from),
end: new Date(r.available_to),
resource: 'available'
}))
setEvents(mapped)
} catch (e) {
console.error('Failed to load availability', e)
}
}
loadAvailability()
}, [])
const eventStyleGetter = (event: CalendarEvent) => {
return {
style: {
@@ -397,13 +389,22 @@ function AvailabilityCalendar({ user }: { user: User }) {
})
}
const handleSelectEvent = (event: CalendarEvent) => {
if (window.confirm('Do you want to remove this availability?')) {
const handleSelectEvent = async (event: CalendarEvent) => {
if (!window.confirm('Do you want to remove this availability?')) {
return
}
try {
if (typeof event.id === 'string') {
await availabilityManagement.deleteAvailability(event.id)
}
setEvents(events.filter(e => e.id !== event.id))
} catch (e: any) {
alert(e?.message || 'Failed to delete availability.')
console.error('Failed to delete availability', e)
}
}
const handleAddTimeSlot = () => {
const handleAddTimeSlot = async () => {
if (!selectedDate) return
const [startHour, startMinute] = newTimeSlot.startTime.split(':').map(Number)
@@ -432,17 +433,28 @@ function AvailabilityCalendar({ user }: { user: User }) {
return
}
const newEvent = {
id: Date.now(),
title: 'Available',
start: startDateTime,
end: endDateTime,
resource: 'available'
}
try {
// Persist to DB first to get real ID and server validation
const created = await availabilityManagement.createAvailability({
available_from: startDateTime.toISOString(),
available_to: endDateTime.toISOString()
})
setEvents([...events, newEvent])
setShowTimeSlotForm(false)
setSelectedDate(null)
const newEvent: CalendarEvent = {
id: created.id,
title: 'Available',
start: new Date(created.available_from),
end: new Date(created.available_to),
resource: 'available'
}
setEvents([...events, newEvent])
setShowTimeSlotForm(false)
setSelectedDate(null)
} catch (e: any) {
alert(e?.message || 'Failed to save availability. Please try again.')
console.error('Failed to create availability', e)
}
}
const handleCancelTimeSlot = () => {

View File

@@ -976,3 +976,68 @@ export const phaseDraftManagement = {
}
}
}
// Conductor Availability Management
export interface ConductorAvailability {
id: string
user_id: string
available_from: string
available_to: string
notes?: string | null
status: 'active' | 'cancelled'
created_at: string
updated_at: string
created_by: string
}
export interface CreateAvailabilityRequest {
available_from: string
available_to: string
notes?: string
}
export const availabilityManagement = {
async getMyAvailability(): Promise<ConductorAvailability[]> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('conductor_availability')
.select('*')
.eq('user_id', user.id)
.eq('status', 'active')
.order('available_from', { ascending: true })
if (error) throw error
return data
},
async createAvailability(request: CreateAvailabilityRequest): Promise<ConductorAvailability> {
const { data: { user }, error: authError } = await supabase.auth.getUser()
if (authError || !user) throw new Error('User not authenticated')
const { data, error } = await supabase
.from('conductor_availability')
.insert({
user_id: user.id,
available_from: request.available_from,
available_to: request.available_to,
notes: request.notes,
created_by: user.id
})
.select()
.single()
if (error) throw error
return data
},
async deleteAvailability(id: string): Promise<void> {
const { error } = await supabase
.from('conductor_availability')
.update({ status: 'cancelled' })
.eq('id', id)
if (error) throw error
}
}