- Replace the three Attendees-tab modals (Manual Ticket / Add at Door / Invite Guest) with a single Add Ticket modal: Paid/Unpaid/Guest segmented control, shared fields, "Check in now" for all types, and a live "what happens" preview, backed by one POST /api/tickets/admin/add. - Add tickets.payment_status (paid | unpaid | comp) with a backfill migration; keep it in sync on every payment-settlement path (mark-paid, admin approval, Lightning, free bookings, hold recovery). - Show Paid/Unpaid/Comp badges in the attendee list, count only paid tickets toward revenue, let unpaid tickets be resolved via Mark Paid, and flag unpaid tickets with their balance due in the door scanner. - Replace the per-page useStatsPrivacy hook with an admin-wide PrivacyContext + SensitiveValue mask, toggled from the admin layout. - Add server-side pagination with page-size options to the users page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
import clsx from 'clsx';
|
|
|
|
export function StatusBadge({ status, compact = false }: { status: string; compact?: boolean }) {
|
|
const styles: Record<string, string> = {
|
|
pending: 'bg-yellow-100 text-yellow-800',
|
|
confirmed: 'bg-green-100 text-green-800',
|
|
cancelled: 'bg-red-100 text-red-800',
|
|
checked_in: 'bg-blue-100 text-blue-800',
|
|
on_hold: 'bg-slate-100 text-slate-600',
|
|
};
|
|
return (
|
|
<span className={clsx(
|
|
'inline-flex items-center rounded-full font-medium',
|
|
compact ? 'px-1.5 py-0.5 text-[10px]' : 'px-2 py-0.5 text-xs',
|
|
styles[status] || 'bg-gray-100 text-gray-800'
|
|
)}>
|
|
{status.replace('_', ' ')}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
// Ticket payment status: Paid (revenue), Unpaid (balance due at door), Comp (free guest).
|
|
// Tickets created before the payment_status column default to unpaid via backfill.
|
|
export function PaymentBadge({ paymentStatus, compact = false }: { paymentStatus?: string; compact?: boolean }) {
|
|
if (!paymentStatus) return null;
|
|
const styles: Record<string, string> = {
|
|
paid: 'bg-emerald-100 text-emerald-700',
|
|
unpaid: 'bg-orange-100 text-orange-700',
|
|
comp: 'bg-amber-100 text-amber-700',
|
|
};
|
|
return (
|
|
<span className={clsx(
|
|
'inline-flex items-center rounded-full font-medium',
|
|
compact ? 'px-1.5 py-0.5 text-[10px]' : 'px-2 py-0.5 text-xs',
|
|
styles[paymentStatus] || 'bg-gray-100 text-gray-800'
|
|
)}>
|
|
{paymentStatus === 'comp' ? 'Comp' : paymentStatus === 'unpaid' ? 'Unpaid' : 'Paid'}
|
|
</span>
|
|
);
|
|
}
|