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:
@@ -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 = () => {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user