Ship the delivery platform UI under modules/delivery with thin app routes, BFF proxy, CRUD for phase 10.0-10.1 APIs, and capability-gated future screens. Co-authored-by: Cursor <cursoragent@cursor.com>
75 lines
3.3 KiB
TypeScript
75 lines
3.3 KiB
TypeScript
"use client";
|
||
|
||
import { useQuery } from "@tanstack/react-query";
|
||
import Link from "next/link";
|
||
import { deliveryApi } from "@/modules/delivery/services/delivery-api";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { useDeliveryCapabilities, useDeliveryMetrics } from "@/modules/delivery/hooks/useDeliveryCapabilities";
|
||
import { DeliveryPageLoader, DeliveryPageError } from "@/modules/delivery/pages/shared";
|
||
import { PageHeader, StatCard, Card, CardContent, Button, Badge } from "@/components/ds";
|
||
import { DeliveryBreadcrumbs } from "@/modules/delivery/components/DeliveryBreadcrumbs";
|
||
|
||
export const ExecutiveDashboard = function DeliveryExecutiveDashboard() {
|
||
const { tenantId } = useTenantId();
|
||
const caps = useDeliveryCapabilities();
|
||
const metrics = useDeliveryMetrics();
|
||
|
||
const countsQ = useQuery({
|
||
queryKey: ["delivery", tenantId, "dashboard-counts"],
|
||
queryFn: async () => {
|
||
const [orgs, hubs, drivers] = await Promise.all([
|
||
deliveryApi.organizations.list(tenantId!, { page: 1, page_size: 500 }),
|
||
deliveryApi.hubs.list(tenantId!, { page: 1, page_size: 500 }),
|
||
deliveryApi.drivers.list(tenantId!, { page: 1, page_size: 500 }),
|
||
]);
|
||
const driverItems = Array.isArray(drivers) ? drivers : drivers.items;
|
||
return {
|
||
organizations: orgs.length,
|
||
hubs: hubs.length,
|
||
drivers: driverItems.length,
|
||
driversActive: driverItems.filter((d) => d.status === "active").length,
|
||
};
|
||
},
|
||
enabled: !!tenantId,
|
||
});
|
||
|
||
if (!tenantId || countsQ.isLoading || caps.isLoading) return <DeliveryPageLoader />;
|
||
if (countsQ.error) return <DeliveryPageError error={countsQ.error} onRetry={() => countsQ.refetch()} />;
|
||
|
||
const c = countsQ.data!;
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<DeliveryBreadcrumbs current="داشبورد اجرایی" />
|
||
<PageHeader
|
||
title="داشبورد اجرایی"
|
||
description="نمای کلی عملیات لجستیک، رانندگان و زیرساخت."
|
||
actions={<Badge tone="success">Torbat Driver v{caps.data?.version}</Badge>}
|
||
/>
|
||
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||
<StatCard label="سازمانها" value={String(c.organizations)} hint="Organizations" />
|
||
<StatCard label="شعب / هاب" value={String(c.hubs)} hint="Hubs" />
|
||
<StatCard label="رانندگان" value={String(c.drivers)} hint="Drivers" />
|
||
<StatCard label="راننده فعال" value={String(c.driversActive)} hint="Active" />
|
||
</div>
|
||
<Card>
|
||
<CardContent className="flex flex-wrap gap-2 p-4">
|
||
<Link href="/delivery/drivers"><Button variant="outline">رانندگان</Button></Link>
|
||
<Link href="/delivery/branches"><Button variant="outline">شعب</Button></Link>
|
||
<Link href="/delivery/maps/view"><Button variant="outline">نقشه</Button></Link>
|
||
<Link href="/delivery/capabilities"><Button variant="outline">قابلیتها</Button></Link>
|
||
</CardContent>
|
||
</Card>
|
||
{metrics.data ? (
|
||
<Card>
|
||
<CardContent className="p-4 text-xs text-[var(--muted)]">
|
||
فاز {metrics.data.phase} — {metrics.data.note}
|
||
</CardContent>
|
||
</Card>
|
||
) : null}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default ExecutiveDashboard;
|