Add reusable Skeleton components and route-level loading files across public pages and admin lists so layouts stay stable while data loads. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.4 KiB
TypeScript
133 lines
4.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { useLanguage } from '@/context/LanguageContext';
|
|
import { useAuth } from '@/context/AuthContext';
|
|
import {
|
|
dashboardApi,
|
|
NextEventInfo,
|
|
UserTicket,
|
|
UserPayment,
|
|
} from '@/lib/api';
|
|
import toast from 'react-hot-toast';
|
|
import { CardListSkeleton } from '@/components/ui/Skeleton';
|
|
|
|
import OverviewTab from './components/OverviewTab';
|
|
import TicketsTab from './components/TicketsTab';
|
|
import PaymentsTab from './components/PaymentsTab';
|
|
import AccountTab from './components/AccountTab';
|
|
|
|
type Tab = 'overview' | 'tickets' | 'payments' | 'account';
|
|
|
|
export default function DashboardPage() {
|
|
const router = useRouter();
|
|
const { locale } = useLanguage();
|
|
const { user, isLoading: authLoading, token } = useAuth();
|
|
|
|
const [activeTab, setActiveTab] = useState<Tab>('overview');
|
|
const [nextEvent, setNextEvent] = useState<NextEventInfo | null>(null);
|
|
const [tickets, setTickets] = useState<UserTicket[]>([]);
|
|
const [payments, setPayments] = useState<UserPayment[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (!authLoading && !user) {
|
|
router.push('/login');
|
|
return;
|
|
}
|
|
if (user && token) {
|
|
loadDashboardData();
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [user, authLoading, token]);
|
|
|
|
const loadDashboardData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const [nextEventRes, ticketsRes, paymentsRes] = await Promise.all([
|
|
dashboardApi.getNextEvent(),
|
|
dashboardApi.getTickets(),
|
|
dashboardApi.getPayments(),
|
|
]);
|
|
setNextEvent(nextEventRes.nextEvent);
|
|
setTickets(ticketsRes.tickets);
|
|
setPayments(paymentsRes.payments);
|
|
} catch (error) {
|
|
console.error('Failed to load dashboard:', error);
|
|
toast.error(locale === 'es' ? 'Error al cargar el panel' : 'Failed to load dashboard data');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const tabs: { id: Tab; label: { en: string; es: string } }[] = [
|
|
{ id: 'overview', label: { en: 'Overview', es: 'Resumen' } },
|
|
{ id: 'tickets', label: { en: 'Tickets', es: 'Entradas' } },
|
|
{ id: 'payments', label: { en: 'Payments', es: 'Pagos' } },
|
|
{ id: 'account', label: { en: 'Account', es: 'Cuenta' } },
|
|
];
|
|
|
|
if (authLoading || !user) {
|
|
return (
|
|
<div className="section-padding flex min-h-[70vh] items-center justify-center">
|
|
<div className="h-12 w-12 animate-spin rounded-full border-b-2 border-primary-yellow" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="section-padding min-h-[70vh]">
|
|
<div className="container-page">
|
|
{/* Welcome header */}
|
|
<h1 className="mb-6 text-3xl font-bold">
|
|
{locale === 'es' ? `¡Hola, ${user.name}!` : `Welcome, ${user.name}!`}
|
|
</h1>
|
|
|
|
{/* Tab navigation */}
|
|
<div className="mb-6 border-b border-secondary-light-gray">
|
|
<nav className="-mb-px flex gap-6 overflow-x-auto">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
className={`whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-semibold transition-colors ${
|
|
activeTab === tab.id
|
|
? 'border-primary-yellow text-primary-dark'
|
|
: 'border-transparent text-gray-500 hover:text-gray-700'
|
|
}`}
|
|
>
|
|
{locale === 'es' ? tab.label.es : tab.label.en}
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Tab content */}
|
|
{loading ? (
|
|
<CardListSkeleton count={3} />
|
|
) : (
|
|
<>
|
|
{activeTab === 'overview' && (
|
|
<OverviewTab
|
|
nextEvent={nextEvent}
|
|
tickets={tickets}
|
|
locale={locale}
|
|
userName={user.name}
|
|
onChange={loadDashboardData}
|
|
/>
|
|
)}
|
|
{activeTab === 'tickets' && (
|
|
<TicketsTab tickets={tickets} language={locale} onChange={loadDashboardData} />
|
|
)}
|
|
{activeTab === 'payments' && (
|
|
<PaymentsTab payments={payments} language={locale} onChange={loadDashboardData} />
|
|
)}
|
|
{activeTab === 'account' && <AccountTab onUpdate={loadDashboardData} />}
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|