diff --git a/app/(app)/me/cancel-action.tsx b/app/(app)/me/cancel-action.tsx new file mode 100644 index 0000000..23ec021 --- /dev/null +++ b/app/(app)/me/cancel-action.tsx @@ -0,0 +1,63 @@ +'use client' + +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import { toast } from 'sonner' +import { Button } from '@/components/ui/button' +import { + AlertDialog, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} from '@/components/ui/alert-dialog' + +// Confirm-then-run wrapper for a self-serve cancel. The action returns the +// project-standard { error } | { data/success } shape. +export function CancelAction({ + title, + description, + action, + successMessage, +}: { + title: string + description: string + action: () => Promise<{ error?: string } | Record> + successMessage: string +}) { + const router = useRouter() + const [busy, setBusy] = useState(false) + + async function run() { + setBusy(true) + const res = await action() + setBusy(false) + if (res && 'error' in res && res.error) return toast.error(res.error as string) + toast.success(successMessage) + router.refresh() + } + + return ( + + + + + + + {title} + {description} + + + Keep it + Cancel it + + + + ) +} diff --git a/app/(app)/me/me-portal-client.tsx b/app/(app)/me/me-portal-client.tsx index 6e6fbea..ae9bff4 100644 --- a/app/(app)/me/me-portal-client.tsx +++ b/app/(app)/me/me-portal-client.tsx @@ -12,6 +12,8 @@ import { RESERVATION_STATUS_LABEL } from '@/lib/equipment-logic' import { presenceStatus } from '@/lib/presence-logic' import { DuesCard } from '@/components/billing/dues-card' import { ProfileForm, type ProfileInitial } from './profile-form' +import { CancelAction } from './cancel-action' +import { cancelMySignup, cancelReservation } from '@/lib/actions' const STATUS_VARIANT: Record = { active: 'default', @@ -35,8 +37,8 @@ export type ClassSignup = { attended: boolean signed_up_at: string class_sessions: - | { starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null } - | { starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null }[] + | { id: string; starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null } + | { id: string; starts_at: string; ends_at: string | null; location: string | null; status: string; classes: { title: string } | { title: string }[] | null }[] | null } export type Reservation = { @@ -235,6 +237,14 @@ export function MePortalClient({

)} + {sess && (cs.status === 'registered' || cs.status === 'waitlisted') && ( + cancelMySignup({ sessionId: sess.id })} + /> + )} ) })} @@ -268,6 +278,14 @@ export function MePortalClient({

{r.notes &&

{r.notes}

} + {r.status === 'reserved' && ( + cancelReservation({ reservationId: r.id })} + /> + )} ) })}