Files
hackerspace.sh/app/(app)/me/cancel-action.tsx
T
Moheeb Zara 6189e2023d feat(me): self-serve cancel for class signups + reservations (P3c)
Activity tab: confirm-dialog Cancel on registered/waitlisted class
signups (cancelMySignup) and reserved equipment reservations
(cancelReservation) via the existing actions. Reusable CancelAction
(AlertDialog + toast + refresh). Buttons only shown on cancellable
states; the actions still enforce ownership/space scoping server-side.
521 tests; build clean (UI not browser-tested from here).
2026-05-18 15:48:24 -07:00

64 lines
1.7 KiB
TypeScript

'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<string, unknown>>
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 (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button size="sm" variant="outline" disabled={busy}>
{busy ? 'Cancelling…' : 'Cancel'}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{title}</AlertDialogTitle>
<AlertDialogDescription>{description}</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Keep it</AlertDialogCancel>
<AlertDialogAction onClick={run}>Cancel it</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}