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>
137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import Link from 'next/link';
|
|
import toast from 'react-hot-toast';
|
|
import { CheckCircleIcon } from '@heroicons/react/24/outline';
|
|
import Button from '@/components/ui/Button';
|
|
import { ticketsApi } from '@/lib/api';
|
|
import { useLanguage } from '@/context/LanguageContext';
|
|
import { pyg } from './helpers';
|
|
|
|
interface PayActionsProps {
|
|
ticketId: string;
|
|
/** Amount owed, used in the confirm copy. */
|
|
amount: number;
|
|
currency?: string;
|
|
/** Where the money is going — event name or account label. */
|
|
destination: string;
|
|
locale: string;
|
|
/** Called after the payment is successfully marked as sent. */
|
|
onPaid?: () => void;
|
|
size?: 'sm' | 'md' | 'lg';
|
|
/** Stack the two buttons full-width (cards) vs inline (rows). */
|
|
layout?: 'stack' | 'inline';
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* The two unpaid-ticket actions used across Overview, Tickets and Payments:
|
|
* • "Pay now" -> opens the existing manual-payment page (TPago / bank details)
|
|
* • "I've paid" -> short confirm step, then marks the payment as sent
|
|
* (moves the ticket to "Awaiting approval").
|
|
*/
|
|
export default function PayActions({
|
|
ticketId,
|
|
amount,
|
|
currency = 'PYG',
|
|
destination,
|
|
locale,
|
|
onPaid,
|
|
size = 'sm',
|
|
layout = 'stack',
|
|
className,
|
|
}: PayActionsProps) {
|
|
const { t } = useLanguage();
|
|
const [confirming, setConfirming] = useState(false);
|
|
const [marking, setMarking] = useState(false);
|
|
|
|
const handleConfirm = async () => {
|
|
setMarking(true);
|
|
try {
|
|
await ticketsApi.markPaymentSent(ticketId);
|
|
toast.success(t('dashboard.payment.receivedConfirmSoon'));
|
|
setConfirming(false);
|
|
onPaid?.();
|
|
} catch (error: any) {
|
|
// Idempotent backend: if already marked, treat as success.
|
|
if (error?.message?.includes('already')) {
|
|
setConfirming(false);
|
|
onPaid?.();
|
|
return;
|
|
}
|
|
toast.error(
|
|
error?.message ||
|
|
(locale === 'es' ? 'No se pudo marcar el pago' : 'Could not mark payment')
|
|
);
|
|
} finally {
|
|
setMarking(false);
|
|
}
|
|
};
|
|
|
|
const containerClass =
|
|
layout === 'stack' ? 'flex flex-col gap-2' : 'flex flex-wrap gap-2';
|
|
const btnWidth = layout === 'stack' ? 'w-full' : '';
|
|
|
|
return (
|
|
<>
|
|
<div className={`${containerClass} ${className || ''}`}>
|
|
<Link href={`/booking/${ticketId}`} className={btnWidth}>
|
|
<Button size={size} className={btnWidth}>
|
|
{locale === 'es' ? 'Pagar ahora' : 'Pay now'}
|
|
</Button>
|
|
</Link>
|
|
<Button
|
|
variant="outline"
|
|
size={size}
|
|
className={btnWidth}
|
|
onClick={() => setConfirming(true)}
|
|
>
|
|
{locale === 'es' ? 'Ya pagué' : "I've paid"}
|
|
</Button>
|
|
</div>
|
|
|
|
{confirming && (
|
|
<div
|
|
className="fixed inset-0 z-[60] flex items-center justify-center bg-black/50 p-4"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
onClick={() => !marking && setConfirming(false)}
|
|
>
|
|
<div
|
|
className="w-full max-w-sm rounded-card bg-white p-6 shadow-card-hover"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<div className="mb-4 flex justify-center">
|
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-amber-100">
|
|
<CheckCircleIcon className="h-7 w-7 text-amber-600" />
|
|
</div>
|
|
</div>
|
|
<h3 className="mb-2 text-center text-lg font-semibold text-primary-dark">
|
|
{locale === 'es' ? '¿Confirmar pago?' : 'Confirm payment?'}
|
|
</h3>
|
|
<p className="mb-6 text-center text-sm text-gray-600">
|
|
{locale === 'es'
|
|
? `¿Ya enviaste los ${pyg(amount, currency)} para ${destination}?`
|
|
: `Did you already send the ${pyg(amount, currency)} for ${destination}?`}
|
|
</p>
|
|
<div className="flex flex-col gap-2">
|
|
<Button isLoading={marking} className="w-full" onClick={handleConfirm}>
|
|
{locale === 'es' ? 'Sí, ya pagué' : "Yes, I've paid"}
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
className="w-full"
|
|
disabled={marking}
|
|
onClick={() => setConfirming(false)}
|
|
>
|
|
{locale === 'es' ? 'Todavía no' : 'Not yet'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|