Redesign user dashboard with overview tab and i18n payment copy.
Consolidate profile and security into AccountTab, add shared dashboard components, and move awaiting-approval payment messages to translations. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
'use client';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import type { UserTicket, Payment } from '@/lib/api';
|
||||
|
||||
// One logical status per meaning, mapped to a single pale colour everywhere.
|
||||
// confirmed -> pale green
|
||||
// awaiting -> pale amber (user said they paid, admin checking)
|
||||
// unpaid -> pale red
|
||||
// attended -> pale blue (replaces the raw "checked_in" value)
|
||||
// cancelled -> pale gray
|
||||
export type DashStatus =
|
||||
| 'confirmed'
|
||||
| 'awaiting'
|
||||
| 'unpaid'
|
||||
| 'attended'
|
||||
| 'cancelled';
|
||||
|
||||
/**
|
||||
* Collapse a ticket status + payment status into a single user-facing status.
|
||||
* Never surface raw values like "checked_in" or "pending_approval".
|
||||
*/
|
||||
export function deriveTicketStatus(
|
||||
ticketStatus?: string,
|
||||
paymentStatus?: string
|
||||
): DashStatus {
|
||||
if (ticketStatus === 'checked_in') return 'attended';
|
||||
if (ticketStatus === 'cancelled') return 'cancelled';
|
||||
if (paymentStatus === 'paid' || ticketStatus === 'confirmed') return 'confirmed';
|
||||
if (paymentStatus === 'pending_approval') return 'awaiting';
|
||||
return 'unpaid';
|
||||
}
|
||||
|
||||
/** Status for a standalone payment record (Payments tab). */
|
||||
export function derivePaymentStatus(paymentStatus?: string): DashStatus {
|
||||
if (paymentStatus === 'paid') return 'confirmed';
|
||||
if (paymentStatus === 'pending_approval') return 'awaiting';
|
||||
if (paymentStatus === 'refunded') return 'cancelled';
|
||||
return 'unpaid';
|
||||
}
|
||||
|
||||
export function statusLabel(status: DashStatus, locale: string): string {
|
||||
const labels: Record<DashStatus, { en: string; es: string }> = {
|
||||
confirmed: { en: 'Confirmed', es: 'Confirmado' },
|
||||
awaiting: { en: 'Awaiting approval', es: 'Esperando aprobación' },
|
||||
unpaid: { en: 'Unpaid', es: 'No pagado' },
|
||||
attended: { en: 'Attended', es: 'Asistió' },
|
||||
cancelled: { en: 'Cancelled', es: 'Cancelado' },
|
||||
};
|
||||
return locale === 'es' ? labels[status].es : labels[status].en;
|
||||
}
|
||||
|
||||
const PILL_STYLES: Record<DashStatus, string> = {
|
||||
confirmed: 'bg-green-100 text-green-800',
|
||||
awaiting: 'bg-amber-100 text-amber-800',
|
||||
unpaid: 'bg-red-100 text-red-700',
|
||||
attended: 'bg-blue-100 text-blue-800',
|
||||
cancelled: 'bg-gray-100 text-gray-600',
|
||||
};
|
||||
|
||||
export function StatusPill({
|
||||
status,
|
||||
locale,
|
||||
className,
|
||||
}: {
|
||||
status: DashStatus;
|
||||
locale: string;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={clsx(
|
||||
'inline-flex items-center rounded-full px-3 py-1 text-xs font-medium whitespace-nowrap',
|
||||
PILL_STYLES[status],
|
||||
className
|
||||
)}
|
||||
>
|
||||
{statusLabel(status, locale)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
/** Convenience: derive + render a pill straight from a ticket. */
|
||||
export function TicketStatusPill({
|
||||
ticket,
|
||||
locale,
|
||||
className,
|
||||
}: {
|
||||
ticket: Pick<UserTicket, 'status'> & { payment?: Pick<Payment, 'status'> | null };
|
||||
locale: string;
|
||||
className?: string;
|
||||
}) {
|
||||
const status = deriveTicketStatus(ticket.status, ticket.payment?.status);
|
||||
return <StatusPill status={status} locale={locale} className={className} />;
|
||||
}
|
||||
Reference in New Issue
Block a user