import { useMemo, useState } from "react"; import { Check, ShieldCheck } from "lucide-react"; import { BookingConfirmDialog } from "./BookingConfirmDialog"; import { useAuth } from "@/auth/useAuth"; import { formatApprovalProgress } from "@/features/bookings/approval-progress"; import { buildApproveActionForStep, canActOnApprovalStep, getNextPendingApprovalStep, } from "@/features/bookings/booking-actions.config"; import type { useBookingMutations } from "@/hooks/bookings/useBookings"; import type { BookingApprovalStep, BookingDetail } from "@/types/booking"; import { bookingGlass, bookingSurface } from "./booking-ui.styles"; import { Badge, Button } from "@edr/ui-common"; import { cn } from "@/lib/utils"; type Mutations = ReturnType; interface ApprovalStepsCardProps { booking: BookingDetail; mutations: Mutations; } /** Approval chain with inline approve on the current pending step. */ export function ApprovalStepsCard({ booking, mutations }: ApprovalStepsCardProps) { const { user } = useAuth(); const [confirmOpen, setConfirmOpen] = useState(false); const [pendingStep, setPendingStep] = useState( null, ); const steps = useMemo( () => [...(booking.approvalSteps ?? [])].sort( (a, b) => a.stepOrder - b.stepOrder, ), [booking.approvalSteps], ); const nextPending = getNextPendingApprovalStep(steps); const summary = formatApprovalProgress(booking.status, steps); const pendingAction = pendingStep ? buildApproveActionForStep(pendingStep) : null; const openApprove = (step: BookingApprovalStep) => { setPendingStep(step); setConfirmOpen(true); }; const closeApprove = () => { setConfirmOpen(false); setPendingStep(null); }; const runApprove = () => { if (!pendingStep) return; mutations.approveStep.mutate( { stepId: pendingStep.id, requiredRole: pendingStep.requiredRole }, { onSuccess: () => closeApprove() }, ); }; return ( <>

Approval chain

{summary.detail || (nextPending ? `Next: ${nextPending.requiredRole} ยท step ${nextPending.stepOrder}` : steps.length ? "All steps complete" : "Accept submission to begin")}

{steps.length === 0 ? (

Use{" "} Accept for approval {" "} in staff actions to instantiate steps.

) : (
    {steps.map((step) => ( ))}
)}
{ if (!open) closeApprove(); else setConfirmOpen(true); }} action={pendingAction} reference={booking.reference} inputValue="" onInputChange={() => {}} onConfirm={runApprove} isPending={mutations.approveStep.isPending} /> ); } function StepRow({ step, steps, user, isNext, isPending, onApprove, }: { step: BookingApprovalStep; steps: BookingApprovalStep[]; user: ReturnType["user"]; isNext: boolean; isPending: boolean; onApprove: (step: BookingApprovalStep) => void; }) { const canApprove = canActOnApprovalStep(user, step, steps); const statusStyles = step.status === "APPROVED" ? "border-emerald-500/25 bg-emerald-500/10 text-black" : step.status === "REJECTED" ? "bg-red-500/10 text-red-800 dark:text-red-300" : isNext ? "border-emerald-500/25 bg-emerald-500/10 text-black" : "bg-muted/40 text-muted-foreground"; return (
  • {step.stepOrder}

    {step.requiredRole}

    {step.remarks && (

    {step.remarks}

    )}
    {canApprove && ( )} {step.status}
  • ); }