Informational updates

This commit is contained in:
Stephanos A
2026-07-22 16:18:58 +03:00
parent c6f6616218
commit e5be205bd8
5 changed files with 48 additions and 21 deletions

View File

@@ -234,7 +234,7 @@ function DashboardPageContent() {
)}
{/* Stat cards */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
<StatCard
icon={
<BookOpen className="h-4 w-4 text-blue-600 dark:text-blue-400" />
@@ -257,7 +257,28 @@ function DashboardPageContent() {
},
]}
/>
{/* Tickets card hidden temporarily */}
<StatCard
icon={
<Ticket className="h-4 w-4 text-emerald-600 dark:text-emerald-400" />
}
iconBg="bg-emerald-100 dark:bg-emerald-900/30"
label="Tickets"
total={stats?.totalTickets ?? 0}
loading={statsLoading}
href="/tickets"
rows={[
{
label: "Regular",
value: stats?.totalNormalTickets ?? 0,
href: "/tickets",
},
{
label: "Package",
value: stats?.totalPackageTickets ?? 0,
href: "/tickets",
},
]}
/>
{/* Revenue card */}
<div className="card flex flex-col gap-3">

View File

@@ -10,7 +10,7 @@ import ActionButton from '@/components/ui/ActionButton';
import ConfirmDialog from '@/components/ui/ConfirmDialog';
import Modal from '@/components/ui/Modal';
import Image from 'next/image';
import { ticketsApi, apiClient, stationsApi, excessBaggageApi } from '@/lib/api';
import { ticketsApi, apiClient, stationsApi, excessBaggageApi, bookingsApi } from '@/lib/api';
import Pagination from '@/components/ui/Pagination';
import { formatDateTime, formatCurrency, formatDateTimeShort } from '@/lib/utils';
import { useAuthStore } from '@/lib/auth-store';
@@ -100,10 +100,21 @@ export default function TicketsPage() {
const [generateMissingResult, setGenerateMissingResult] = useState<any>(null);
const { data: hasMissingTickets } = useQuery({
queryKey: ['bookings-missing-tickets'],
queryFn: async () => {
const res = await bookingsApi.getAll({ status: 'CONFIRMED', paymentStatus: 'SUCCEEDED', take: 50 });
const items: any[] = (res as any)?.items ?? (Array.isArray(res) ? res : []);
return items.some((b: any) => !b.tickets?.length);
},
refetchInterval: 30_000,
});
const generateMissingMutation = useMutation({
mutationFn: () => ticketsApi.generateMissing(),
onSuccess: (result: any) => {
queryClient.invalidateQueries({ queryKey: ['tickets'] });
queryClient.invalidateQueries({ queryKey: ['bookings-missing-tickets'] });
setGenerateMissingResult(result);
setSuccessMessage(`Generated ${result.generated} ticket(s) for ${result.processed} booking(s)${result.failed ? ` (${result.failed} failed)` : ''}`);
setTimeout(() => setSuccessMessage(''), 6000);
@@ -562,14 +573,16 @@ export default function TicketsPage() {
<p className="text-muted-foreground">Manage tickets and validations</p>
</div>
<div className="flex items-center gap-2">
<ActionButton
icon={Download}
variant="secondary"
loading={generateMissingMutation.isPending}
onClick={() => generateMissingMutation.mutate()}
>
Generate Missing
</ActionButton>
{hasMissingTickets && (
<ActionButton
icon={Download}
variant="secondary"
loading={generateMissingMutation.isPending}
onClick={() => generateMissingMutation.mutate()}
>
Generate Missing
</ActionButton>
)}
<ActionButton icon={Download} variant="export" onClick={() => setExportModalOpen(true)}>Export</ActionButton>
</div>
</div>