import { useState } from 'react' import { createFileRoute, useNavigate, Link } from '@tanstack/react-router' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { sessionDetailOptions, sessionMutations, sessionKeys, sessionPlanItemsOptions, enrollmentDetailOptions, instructorDetailOptions, instructorListOptions, lessonPlanListOptions, } from '@/api/lessons' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { ArrowLeft, CheckSquare, Square } from 'lucide-react' import { toast } from 'sonner' import { useAuthStore } from '@/stores/auth.store' import type { LessonPlan, LessonPlanSection } from '@/types/lesson' export const Route = createFileRoute('/_authenticated/lessons/sessions/$sessionId')({ component: SessionDetailPage, }) const STATUS_ACTIONS: Record = { scheduled: [ { label: 'Mark Attended', next: 'attended', variant: 'default' }, { label: 'Mark Missed', next: 'missed', variant: 'destructive' }, { label: 'Cancel', next: 'cancelled', variant: 'secondary' }, ], attended: [], missed: [], makeup: [ { label: 'Mark Attended', next: 'attended', variant: 'default' }, { label: 'Cancel', next: 'cancelled', variant: 'secondary' }, ], cancelled: [], } function sessionStatusBadge(status: string) { const variants: Record = { scheduled: 'outline', attended: 'default', missed: 'destructive', makeup: 'secondary', cancelled: 'secondary', } return {status} } function formatTime(t: string) { const [h, m] = t.split(':').map(Number) const ampm = h >= 12 ? 'PM' : 'AM' const hour = h % 12 || 12 return `${hour}:${String(m).padStart(2, '0')} ${ampm}` } function SessionDetailPage() { const { sessionId } = Route.useParams() const navigate = useNavigate() const queryClient = useQueryClient() const hasPermission = useAuthStore((s) => s.hasPermission) const canEdit = hasPermission('lessons.edit') const { data: session, isLoading } = useQuery(sessionDetailOptions(sessionId)) const { data: enrollment } = useQuery({ ...enrollmentDetailOptions(session?.enrollmentId ?? ''), enabled: !!session?.enrollmentId, }) useQuery({ ...instructorDetailOptions(session?.substituteInstructorId ?? enrollment?.instructorId ?? ''), enabled: !!(session?.substituteInstructorId ?? enrollment?.instructorId), }) const { data: instructorsList } = useQuery(instructorListOptions({ page: 1, limit: 100, order: 'asc' })) const { data: planItems } = useQuery(sessionPlanItemsOptions(sessionId)) const { data: plansData } = useQuery({ ...lessonPlanListOptions({ enrollmentId: session?.enrollmentId ?? '', isActive: true }), enabled: !!session?.enrollmentId, }) const activePlan: LessonPlan | undefined = plansData?.data?.[0] const statusMutation = useMutation({ mutationFn: (status: string) => sessionMutations.updateStatus(sessionId, status), onSuccess: () => { queryClient.invalidateQueries({ queryKey: sessionKeys.detail(sessionId) }) toast.success('Status updated') }, onError: (err) => toast.error(err.message), }) const notesMutation = useMutation({ mutationFn: (data: Record) => sessionMutations.updateNotes(sessionId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: sessionKeys.detail(sessionId) }) toast.success('Notes saved') }, onError: (err) => toast.error(err.message), }) const subMutation = useMutation({ mutationFn: (subId: string | null) => sessionMutations.update(sessionId, { substituteInstructorId: subId }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: sessionKeys.detail(sessionId) }) toast.success('Substitute updated') }, onError: (err) => toast.error(err.message), }) const linkPlanItemsMutation = useMutation({ mutationFn: (ids: string[]) => sessionMutations.linkPlanItems(sessionId, ids), onSuccess: () => { queryClient.invalidateQueries({ queryKey: sessionKeys.planItems(sessionId) }) toast.success('Plan items linked') }, onError: (err) => toast.error(err.message), }) if (isLoading) return
Loading...
if (!session) return
Session not found.
const linkedItemIds = new Set(planItems?.map((pi) => pi.lessonPlanItemId) ?? []) return (

{new Date(session.scheduledDate + 'T00:00:00').toLocaleDateString()} · {formatTime(session.scheduledTime)}

{enrollment && ( View Enrollment )}
{sessionStatusBadge(session.status)}
{/* Status Actions */} {canEdit && (STATUS_ACTIONS[session.status]?.length ?? 0) > 0 && (
{STATUS_ACTIONS[session.status].map((action) => ( ))}
)} {/* Substitute Instructor */} {canEdit && ( Substitute Instructor )} {/* Post-lesson Notes */} {/* Plan Items */} {activePlan && ( linkPlanItemsMutation.mutate(ids)} linking={linkPlanItemsMutation.isPending} /> )}
) } // ─── Notes Card ─────────────────────────────────────────────────────────────── function NotesCard({ session, canEdit, onSave, saving }: any) { const [instructorNotes, setInstructorNotes] = useState(session.instructorNotes ?? '') const [memberNotes, setMemberNotes] = useState(session.memberNotes ?? '') const [homeworkAssigned, setHomeworkAssigned] = useState(session.homeworkAssigned ?? '') const [nextLessonGoals, setNextLessonGoals] = useState(session.nextLessonGoals ?? '') const [topicsCovered, setTopicsCovered] = useState((session.topicsCovered ?? []).join(', ')) return (
Post-lesson Notes {session.notesCompletedAt && ( Saved {new Date(session.notesCompletedAt).toLocaleString()} )}