import { useMemo, useState } from 'react'; import { Link, useParams } from 'react-router-dom'; import { Badge } from '@astryxdesign/core/Badge'; import { Button } from '@astryxdesign/core/Button'; import { Card } from '@astryxdesign/core/Card'; import { HStack } from '@astryxdesign/core/HStack'; import { Table, type TableColumn } from '@astryxdesign/core/Table'; import { Text } from '@astryxdesign/core/Text'; import { VStack } from '@astryxdesign/core/VStack'; import { IndianRupee, QrCode, ShoppingCart, Store, TriangleAlert } from 'lucide-react'; import { DataState } from '@/components/DataState'; import { Freshness } from '@/components/Freshness'; import { KpiCard } from '@/components/KpiCard'; import { PageHeader } from '@/components/PageHeader'; import { SectionHeader } from '@/components/SectionHeader'; import { useLocationSummary, useOrderSummary, useTenantLocations, useTenants } from '@/queries/hooks'; import type { LocationOrderSummary, TenantInfo, TenantLocation } from '@/api/types'; import { TablePager } from '@/components/TablePager'; import { usePaged } from '@/components/usePaged'; import { Drawer } from '@/features/store-admin/Drawer'; import { StoreQrPanel } from '@/features/qr/StoreQrPanel'; interface BranchRow extends Record { locationid: number; locationname: string; city: string; hours: string; radius: string; status: string; orders: number; revenue: number; } function money(value: number | undefined): string { if (typeof value !== 'number' || Number.isNaN(value)) return '₹0'; return `₹${value.toLocaleString('en-IN')}`; } /** * One tenant: its branches, and how each is performing. * * Order figures come from `/orders/getlocationsummary`, which is scoped to the * tenant and returns one row per branch. Counter sales are NOT folded in here: * the POS endpoints take a single required locationid, so a tenant-wide till * figure would mean one request per branch, and a blended number would be * eventually consistent in a way this page cannot honestly caption. Branch-level * POS lives on the branch screen instead. */ export function StoreDetailPage() { const { tenantId } = useParams<{ tenantId: string }>(); const tenantid = Number(tenantId ?? 0) || undefined; const { data: tenants } = useTenants(); const { data: locations, isLoading, error } = useTenantLocations(tenantid); const summary = useLocationSummary(tenantid); const orders = useOrderSummary(tenantid); const tenant = useMemo( () => (tenants as TenantInfo[] | undefined)?.find((entry) => entry.tenantid === tenantid), [tenants, tenantid], ); const rows = useMemo(() => { const perLocation = new Map(); for (const entry of summary.data ?? []) { if (typeof entry.locationid === 'number') perLocation.set(entry.locationid, entry); } return ((locations ?? []) as TenantLocation[]).map((branch) => { const stats = perLocation.get(branch.locationid); return { locationid: branch.locationid, locationname: branch.locationname, city: [branch.suburb, branch.city].filter(Boolean).join(', '), hours: branch.opentime && branch.closetime ? `${branch.opentime}–${branch.closetime}` : '—', radius: branch.deliveryradius ? `${(branch.deliveryradius / 1000).toFixed(1)} km` : '—', status: branch.status ?? 'Unknown', orders: Number(stats?.totalorders ?? 0), revenue: Number(stats?.revenue ?? 0), }; }); }, [locations, summary.data]); const paged = usePaged(rows); const totals = useMemo(() => { const branches = rows.length; const active = rows.filter((row) => row.status.toLowerCase() === 'active').length; const totalOrders = rows.reduce((sum, row) => sum + row.orders, 0); const totalRevenue = rows.reduce((sum, row) => sum + row.revenue, 0); const cancelled = Number(orders.data?.cancelled ?? 0); return { branches, active, totalOrders, totalRevenue, cancelled }; }, [rows, orders.data]); /** The branch whose code is on screen, if any. */ const [qrFor, setQrFor] = useState(null); const columns: TableColumn[] = [ { key: 'locationname', header: 'Branch', width: { type: 'proportional', value: 3 }, renderCell: (row) => ( {row.locationname} {row.city || '—'} ), }, { key: 'hours', header: 'Hours', width: { type: 'pixel', value: 130 }, renderCell: (row) => ( {row.hours} ), }, { key: 'radius', header: 'Radius', align: 'end', width: { type: 'pixel', value: 90 }, renderCell: (row) => ( {row.radius} ), }, { key: 'orders', header: 'Orders', align: 'end', width: { type: 'pixel', value: 100 }, renderCell: (row) => ( {row.orders} ), }, { key: 'revenue', header: 'Revenue', align: 'end', width: { type: 'pixel', value: 120 }, renderCell: (row) => ( {money(row.revenue)} ), }, { key: 'status', header: 'Status', align: 'end', width: { type: 'pixel', value: 110 }, renderCell: (row) => ( ), }, { /* The storefront code, per branch, after the day it was commissioned. Shown at creation too — but a poster gets lost, a shop reopens, a merchant asks for it again a month later, and the person they ask is whoever is looking at this page. The code is derived from the two ids on this row, so there is nothing to look up and nothing to reissue. */ key: 'qr', header: 'QR', align: 'end', width: { type: 'pixel', value: 84 }, renderCell: (row) => (