TorbatYar/frontend/modules/hospitality/features/dashboard.tsx
Mortezakoohjani 31a0a34945 feat(loyalty): add complete Loyalty Platform Frontend module
Scaffold frontend/modules/loyalty with types, API client, design system, feature pages, and thin App Router routes. Wire all screens to backend Loyalty service via BFF proxy. Add loyalty docs and update progress.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 10:46:45 +03:30

89 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useQuery } from "@tanstack/react-query";
import Link from "next/link";
import { hospitalityApi } from "@/modules/hospitality/services/hospitality-api";
import { useTenantId } from "@/hooks/useTenantId";
import { useHospitalityCapabilities } from "@/modules/hospitality/hooks/useHospitalityCapabilities";
import { HospitalityPageLoader, HospitalityPageError } from "@/modules/hospitality/pages/shared";
import { PageHeader, StatCard, Card, CardContent, Button, Badge } from "@/components/ds";
import { HospitalityBreadcrumbs } from "@/modules/hospitality/components/HospitalityBreadcrumbs";
import { HospitalityStatGrid } from "@/modules/hospitality/design-system";
export const ExecutiveDashboard = function HospitalityDashboard() {
const { tenantId } = useTenantId();
const caps = useHospitalityCapabilities();
const countsQ = useQuery({
queryKey: ["hospitality", tenantId, "dashboard-counts"],
queryFn: async () => {
const [venues, reservations, tickets, kitchen, menus, qrSessions] = await Promise.all([
hospitalityApi.venues.list(tenantId!, { page: 1, page_size: 500 }),
hospitalityApi.reservations.list(tenantId!, { page: 1, page_size: 500 }),
hospitalityApi.posTickets.list(tenantId!, { page: 1, page_size: 500 }),
hospitalityApi.kitchenTickets.list(tenantId!, { page: 1, page_size: 500 }),
hospitalityApi.menus.list(tenantId!, { page: 1, page_size: 500 }),
hospitalityApi.qrOrderingSessions.list(tenantId!, { page: 1, page_size: 500 }),
]);
return {
venues: venues.length,
reservations: reservations.filter((r) => r.status === "confirmed" || r.status === "pending").length,
openOrders: tickets.filter((t) => t.status === "open" || t.status === "draft").length,
kitchenActive: kitchen.filter((k) => k.status !== "completed" && k.status !== "cancelled").length,
menus: menus.filter((m) => m.status === "published" || m.status === "active").length,
onlineOrders: qrSessions.filter((s) => s.status === "submitted" || s.status === "preparing").length,
};
},
enabled: !!tenantId,
refetchInterval: 30_000,
});
if (!tenantId || countsQ.isLoading || caps.isLoading) return <HospitalityPageLoader />;
if (countsQ.error) return <HospitalityPageError error={countsQ.error} onRetry={() => countsQ.refetch()} />;
const c = countsQ.data!;
return (
<div className="space-y-6">
<HospitalityBreadcrumbs current="داشبورد" />
<PageHeader
title="داشبورد مدیریتی"
description="نمای اجرایی فروش، سفارش‌ها و عملیات روزانه — داده زنده از API."
actions={
<div className="flex gap-2">
<Badge tone="success">Torbat Food v{caps.data?.version}</Badge>
<Button variant="outline" size="sm" onClick={() => countsQ.refetch()}>
بروزرسانی
</Button>
</div>
}
/>
<HospitalityStatGrid
stats={[
{ label: "مکان‌ها", value: c.venues, hint: "Venues" },
{ label: "رزرو فعال", value: c.reservations, hint: "Reservations" },
{ label: "سفارش باز POS", value: c.openOrders, hint: "Open tickets" },
{ label: "آشپزخانه فعال", value: c.kitchenActive, hint: "Kitchen queue" },
{ label: "منوهای منتشر", value: c.menus, hint: "Menus" },
{ label: "سفارش آنلاین", value: c.onlineOrders, hint: "QR ordering" },
]}
/>
<Card>
<CardContent className="space-y-3 p-4">
<p className="text-sm font-medium text-secondary">عملیات سریع</p>
<div className="flex flex-wrap gap-2">
<Link href="/hospitality/reservations"><Button variant="outline">رزروها</Button></Link>
<Link href="/hospitality/kitchen/display"><Button variant="outline">آشپزخانه</Button></Link>
<Link href="/hospitality/pos/lite"><Button variant="outline">POS Lite</Button></Link>
<Link href="/hospitality/menu"><Button variant="outline">منو</Button></Link>
<Link href="/hospitality/analytics"><Button variant="outline">آنالیتیکس</Button></Link>
<Link href="/hospitality/orders/online"><Button variant="outline">سفارش آنلاین</Button></Link>
</div>
</CardContent>
</Card>
</div>
);
};
export default ExecutiveDashboard;