import { fetchApi } from './client'; // ─── Door check-in screen API ──────────────────────────────── // Every write is idempotent on a client-generated key so the door screen can // fire actions optimistically and retry on flaky venue wifi without ever // creating a duplicate ticket, payment or check-in. export const DOOR_PAYMENT_METHODS = ['cash', 'bitcoin', 'transfer', 'guest'] as const; export type DoorPaymentMethod = (typeof DOOR_PAYMENT_METHODS)[number]; export type DoorEntryMethod = 'scan' | 'search' | 'walkin'; export interface DoorAttendee { ticketId: string; firstName: string; lastName: string | null; fullName: string; email: string | null; phone: string | null; status: 'pending' | 'confirmed' | 'cancelled' | 'checked_in' | 'on_hold'; paymentStatus: 'paid' | 'unpaid' | 'comp'; isGuest: boolean; checkedIn: boolean; checkinAt: string | null; checkedInBy: string | null; bookingId: string | null; isGroupBooking: boolean; amountDue: number; doorMethod: DoorPaymentMethod | null; qrCode: string | null; createdAt: string | null; } export interface DoorAttendeesResponse { event: { id: string; title: string; price: number; currency: string; capacity: number }; attendees: DoorAttendee[]; stats: { checkedIn: number; totalActive: number; capacity: number }; } export interface DoorCheckinRequest { ticketId?: string; attendee?: { firstName: string; lastName?: string; phone?: string; email?: string; ruc?: string; }; payment?: { method: DoorPaymentMethod; amount?: number }; entryMethod?: DoorEntryMethod; idempotencyKey: string; } export interface DoorCheckinResponse { ok: true; action: 'checkin' | 'walkin'; attendee: DoorAttendee; payment: { id: string; method: DoorPaymentMethod; amount: number; currency: string } | null; /** 'at_capacity' — the event is full; the attendee was added anyway. */ warnings: string[]; idempotencyKey: string; processedAt: string; /** True when this response was replayed from an already-processed key. */ replayed?: boolean; undone?: boolean; } export interface DoorMethodTotal { count: number; total: number; } export interface DoorSummary { eventId: string; currency: string; price: number; door: { count: number; total: number; byMethod: Record; lines: { paymentId: string; ticketId: string; name: string; method: DoorPaymentMethod; amount: number; paidAt: string | null; }[]; }; presale: { count: number; total: number }; total: number; } export const doorApi = { // Preloaded once per event, then searched entirely in memory. attendees: (eventId: string) => fetchApi(`/api/events/${eventId}/door-attendees`), checkin: (eventId: string, body: DoorCheckinRequest) => fetchApi(`/api/events/${eventId}/door-checkin`, { method: 'POST', body: JSON.stringify(body), }), undo: (eventId: string, idempotencyKey: string) => fetchApi<{ ok: true; ticketId?: string; reverted?: string; alreadyUndone?: boolean }>( `/api/events/${eventId}/door-checkin/undo`, { method: 'POST', body: JSON.stringify({ idempotencyKey }) } ), summary: (eventId: string) => fetchApi(`/api/events/${eventId}/door-summary`), };