@burdenoff/microfe-kadaikodi 2026.710.1 → 2026.716.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kadaikodi/pages/HomePage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/HomePage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/CartManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/admin/CartManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/MerchantOnboardingPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDetailPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDetailPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CheckoutPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/FavoritesPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/FavoritesPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/ReviewsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/ReviewsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/shared.d.ts.map +1 -1
- package/dist/kadaikodi/pages/customer/shared.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js +3 -3
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorPortfolioPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerDashboard.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.d.ts.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.d.ts +8 -27
- package/dist/services/createSandboxAssistantTransport.d.ts.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js +7 -10
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/stories/EmptyState.stories.d.ts +10 -0
- package/dist/stories/EmptyState.stories.d.ts.map +1 -0
- package/dist/stories/MetricsCard.stories.d.ts +10 -0
- package/dist/stories/MetricsCard.stories.d.ts.map +1 -0
- package/dist/stories/StatusBadge.stories.d.ts +12 -0
- package/dist/stories/StatusBadge.stories.d.ts.map +1 -0
- package/package.json +15 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminDashboard.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminDashboard.tsx"],"sourcesContent":["/**\n * Admin Dashboard Page\n * @module kadaikodi/pages/admin\n *\n * Platform control room wired to the live Kadaikodi backend. Every stat,\n * chart and list is derived from real hook data (useDashboardStats +\n * useProviders + useOrders + useAdminAnalytics). Revenue-by-category, order\n * trends and top-merchant revenue are DERIVED by joining real orders to their\n * providers — nothing is fabricated. Where a metric has no honest backend\n * source it is omitted rather than invented.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n Store,\n ShoppingBag,\n IndianRupee,\n ArrowRight,\n Star,\n Settings,\n BarChart3,\n UserCheck,\n ShieldCheck,\n Loader2,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart, AreaChart } from '../../components/viz';\nimport type { LabeledValue, Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useDashboardStats,\n useProviders,\n useOrders,\n useAdminAnalytics,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatRelativeTime } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nconst titleCase = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\nconst dayKey = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\n\nconst dayLabel = (t: number) =>\n new Date(t).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' });\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminDashboard() {\n const { navigateTo, currentUser } = useKadaikodi();\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'Admin';\n\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const { data: orders = [], isLoading: ordersLoading } = useOrders({});\n const { data: analytics } = useAdminAnalytics();\n\n const isLoading = statsLoading || providersLoading || ordersLoading;\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n // Pending merchant approvals — providers not yet VERIFIED.\n const pendingProviders = useMemo(() => providers.filter((p) => !p.isVerified), [providers]);\n\n // Most recent orders across the platform → honest \"recent activity\" feed.\n const recentOrders = useMemo(\n () =>\n [...orders]\n .sort((a, b) => new Date(b.orderDate).getTime() - new Date(a.orderDate).getTime())\n .slice(0, 6),\n [orders]\n );\n\n // ── Order trend (last 30 days) — real per-day order counts + GMV. ──────────\n const { orderTrend, gmvTrend, ordersToday } = useMemo(() => {\n const today = dayKey(new Date());\n const start = today - 29 * DAY_MS;\n const counts = new Map<number, number>();\n const gmv = new Map<number, number>();\n for (let t = start; t <= today; t += DAY_MS) {\n counts.set(t, 0);\n gmv.set(t, 0);\n }\n let todayCount = 0;\n for (const o of orders) {\n const t = dayKey(new Date(o.orderDate));\n if (counts.has(t)) {\n counts.set(t, (counts.get(t) ?? 0) + 1);\n gmv.set(t, (gmv.get(t) ?? 0) + (o.total ?? 0));\n }\n if (t === today) todayCount += 1;\n }\n const oTrend: LabeledValue[] = [...counts.entries()].map(([t, v]) => ({\n label: dayLabel(t),\n value: v,\n }));\n const gTrend: LabeledValue[] = [...gmv.entries()].map(([t, v]) => ({\n label: dayLabel(t),\n value: v,\n }));\n return { orderTrend: oTrend, gmvTrend: gTrend, ordersToday: todayCount };\n }, [orders]);\n\n // ── Revenue by category — derived by joining real orders → provider. ───────\n const revenueSegments = useMemo<Segment[]>(() => {\n const catOf = new Map(providers.map((p) => [p.id, p.category]));\n const byCat = new Map<string, number>();\n for (const o of orders) {\n const cat = catOf.get(o.cartId) ?? 'OTHER';\n byCat.set(cat, (byCat.get(cat) ?? 0) + (o.total ?? 0));\n }\n let entries = [...byCat.entries()].filter(([, v]) => v > 0).sort((a, b) => b[1] - a[1]);\n // Fall back to real category *counts* if no order revenue is available yet.\n if (entries.length === 0 && analytics?.categoryDistribution) {\n entries = Object.entries(analytics.categoryDistribution).sort((a, b) => b[1] - a[1]);\n }\n const top = entries.slice(0, 5);\n const restTotal = entries.slice(5).reduce((s, [, v]) => s + v, 0);\n const segs: Segment[] = top.map(([c, v]) => ({ label: titleCase(c), value: v }));\n if (restTotal > 0) segs.push({ label: 'Others', value: restTotal });\n return segs;\n }, [orders, providers, analytics]);\n\n const revenueIsCurrency = useMemo(() => {\n // If we derived from orders (real ₹), format as currency; if we fell back to\n // category counts, the donut represents cart counts.\n const catOf = new Set(providers.map((p) => p.id));\n return orders.some((o) => catOf.has(o.cartId) && (o.total ?? 0) > 0);\n }, [orders, providers]);\n\n const revenueTotal = useMemo(\n () => revenueSegments.reduce((s, seg) => s + seg.value, 0),\n [revenueSegments]\n );\n\n // ── Top merchants — backend ranking + derived real revenue when available. ─\n const topMerchants = useMemo(() => {\n const revByProvider = new Map<string, number>();\n for (const o of orders) revByProvider.set(o.cartId, (revByProvider.get(o.cartId) ?? 0) + (o.total ?? 0));\n const ranked = analytics?.topProviders ?? [];\n return ranked.slice(0, 5).map((p) => ({\n id: p.id,\n name: p.name,\n category: titleCase(p.category),\n rating: p.rating ?? 0,\n revenue: revByProvider.get(p.id) ?? 0,\n }));\n }, [analytics, orders]);\n\n const orderStatusBadge = (status: string) => {\n if (status === 'delivered') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'cancelled') return 'bg-status-error-bg-subtle text-status-error-text';\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n };\n\n /* ---- Loading state ---- */\n if (isLoading) {\n return (\n <PageLayout title=\"Platform Overview\" description=\"Loading platform control room...\">\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-32 animate-pulse rounded-[var(--radius-card)] border border-border-subtle bg-bg-sunken/40\"\n />\n ))}\n </div>\n <div className=\"mt-6 flex items-center gap-2 text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading platform data…\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Platform Overview\"\n description={`Welcome, ${firstName}! Monitor and manage the whole Kadaikodi marketplace.`}\n actions={\n pendingProviders.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/carts')}\n className=\"flex items-center gap-2 rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle px-3 py-2 text-sm font-medium text-status-warning-text transition-colors hover:bg-status-warning-bg-subtle/70\"\n >\n <ShieldCheck className=\"h-4 w-4\" />\n {pendingProviders.length} pending approval{pendingProviders.length === 1 ? '' : 's'}\n </button>\n ) : undefined\n }\n >\n <PagePurpose className=\"mb-6\">\n The control room for the whole Kadaikodi marketplace — track GMV, merchants, orders and\n customers at a glance, watch how demand is trending, and clear pending merchant approvals\n first. Everything below is live platform data.\n </PagePurpose>\n\n {/* KPI row — real dashboard stats + real trend sparklines */}\n <div className=\"mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"GMV (Total Revenue)\"\n value={fmtCur(stats?.totalRevenue ?? 0)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Active Merchants\"\n value={(stats?.activeProviders ?? 0).toLocaleString('en-IN')}\n icon={<Store className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Orders\"\n value={(stats?.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n sparkline={orderTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Active Customers\"\n value={(stats?.activeCustomers ?? 0).toLocaleString('en-IN')}\n icon={<Users className=\"h-4 w-4\" />}\n />\n </div>\n\n {/* Main analytics grid: Revenue donut · Order-trend area · side column */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Revenue by Category */}\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">\n {revenueIsCurrency ? 'Revenue by Category' : 'Carts by Category'}\n </h3>\n {revenueSegments.length > 0 ? (\n <DonutChart\n segments={revenueSegments}\n centerLabel={\n revenueIsCurrency ? fmtCur(revenueTotal) : revenueTotal.toLocaleString('en-IN')\n }\n centerCaption={revenueIsCurrency ? 'revenue' : 'carts'}\n valueFormatter={\n revenueIsCurrency ? (v) => fmtCur(v) : (v) => v.toLocaleString('en-IN')\n }\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Category revenue will appear here as orders come in.\n </p>\n )}\n </GlassCard>\n\n {/* Order Trends */}\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold text-text-primary\">Order Trends</h3>\n <span className=\"rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\">\n Today: {ordersToday.toLocaleString('en-IN')}\n </span>\n </div>\n {orders.length > 0 ? (\n <AreaChart\n series={orderTrend}\n height={200}\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n title=\"Orders per day over the last 30 days\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Order volume will chart here once orders are placed.\n </p>\n )}\n </GlassCard>\n\n {/* Side column: Top Merchants + Pending Approvals */}\n <div className=\"flex flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Top Merchants</h3>\n {topMerchants.length > 0 ? (\n <ul className=\"space-y-3\">\n {topMerchants.map((m, i) => (\n <li key={m.id} className=\"flex items-center gap-3\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-bold text-text-secondary\">\n {i + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{m.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{m.category}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n {m.revenue > 0 ? (\n <p className=\"text-sm font-semibold text-text-primary\">{fmtCur(m.revenue)}</p>\n ) : null}\n <p className=\"flex items-center justify-end gap-1 text-xs text-text-secondary\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n {m.rating.toFixed(1)}\n </p>\n </div>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Top merchants will be ranked here once orders start flowing.\n </p>\n )}\n </GlassCard>\n\n <GlassCard treatment={pendingProviders.length > 0 ? 'glass' : 'plain'} className=\"p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n Pending Merchant Approvals\n </h3>\n <p className=\"mt-1 text-3xl font-bold tabular-nums text-text-primary\">\n {pendingProviders.length.toLocaleString('en-IN')}\n <span className=\"ml-1 text-base font-normal text-text-secondary\">pending</span>\n </p>\n </div>\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <UserCheck className=\"h-5 w-5\" />\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/carts')}\n className=\"mt-4 w-full rounded-lg bg-action-primary-bg py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n Review\n </button>\n </GlassCard>\n </div>\n </div>\n\n {/* Recent Orders */}\n <div className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recent Orders</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/orders')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n View all orders\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentOrders.length > 0 ? (\n <div className=\"divide-y divide-border rounded-lg border border-border-subtle bg-bg-surface\">\n {recentOrders.map((order) => (\n <div key={order.id} className=\"flex items-start gap-3 px-4 py-3\">\n <div className=\"mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-col justify-between gap-2 sm:flex-row sm:items-center\">\n <p className=\"text-sm font-medium text-text-primary\">\n Order from {order.cartName}\n </p>\n <span\n className={cn(\n 'inline-flex w-fit items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize',\n orderStatusBadge(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span>{formatRelativeTime(order.orderDate)}</span>\n <span className=\"flex items-center gap-1\">\n <IndianRupee className=\"h-3 w-3\" />\n {(order.total ?? 0).toLocaleString('en-IN')}\n </span>\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No orders yet\"\n description=\"Orders placed across the platform will appear here as they come in.\"\n />\n )}\n </div>\n\n {/* Management Links */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[\n {\n icon: Store,\n label: 'Merchant Management',\n desc: 'Approve, verify and monitor carts',\n path: '/admin/carts',\n },\n {\n icon: Users,\n label: 'User Management',\n desc: 'Manage users and permissions',\n path: '/admin/users',\n },\n {\n icon: BarChart3,\n label: 'Analytics & Reports',\n desc: 'View detailed platform analytics',\n path: '/admin/analytics',\n },\n {\n icon: IndianRupee,\n label: 'Financial Reports',\n desc: 'Revenue, categories and trends',\n path: '/admin/financial',\n },\n {\n icon: ShoppingBag,\n label: 'Order Management',\n desc: 'Handle orders and customer issues',\n path: '/admin/orders',\n },\n {\n icon: Settings,\n label: 'Platform Settings',\n desc: 'Configure platform-wide settings',\n path: '/admin/settings',\n },\n ].map((link) => {\n const LinkIcon = link.icon;\n return (\n <button\n key={link.label}\n type=\"button\"\n onClick={() => navigateTo(link.path)}\n className={cn(\n 'flex items-center gap-4 rounded-lg border border-border-subtle bg-bg-surface p-4 text-left shadow-sm transition-all sm:p-6',\n 'hover:bg-accent hover:shadow-md'\n )}\n >\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n <LinkIcon className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary sm:text-base\">\n {link.label}\n </h3>\n <p className=\"text-xs text-text-secondary sm:text-sm\">{link.desc}</p>\n </div>\n </button>\n );\n })}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminDashboard.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminDashboard.tsx"],"sourcesContent":["/**\n * Admin Dashboard Page\n * @module kadaikodi/pages/admin\n *\n * Platform control room wired to the live Kadaikodi backend. Every stat,\n * chart and list is derived from real hook data (useDashboardStats +\n * useProviders + useOrders + useAdminAnalytics). Revenue-by-category, order\n * trends and top-merchant revenue are DERIVED by joining real orders to their\n * providers — nothing is fabricated. Where a metric has no honest backend\n * source it is omitted rather than invented.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n Store,\n ShoppingBag,\n IndianRupee,\n ArrowRight,\n Star,\n Settings,\n BarChart3,\n UserCheck,\n ShieldCheck,\n Loader2,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart, AreaChart } from '../../components/viz';\nimport type { LabeledValue, Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useDashboardStats,\n useProviders,\n useOrders,\n useAdminAnalytics,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency, formatRelativeTime } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Helpers */\n/* ------------------------------------------------------------------ */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\nconst titleCase = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\nconst dayKey = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\n\nconst dayLabel = (t: number) =>\n new Date(t).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' });\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminDashboard() {\n const { navigateTo, currentUser } = useKadaikodi();\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'Admin';\n\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const { data: orders = [], isLoading: ordersLoading } = useOrders({});\n const { data: analytics } = useAdminAnalytics();\n\n const isLoading = statsLoading || providersLoading || ordersLoading;\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n // Pending merchant approvals — providers not yet VERIFIED.\n const pendingProviders = useMemo(() => providers.filter((p) => !p.isVerified), [providers]);\n\n // Most recent orders across the platform → honest \"recent activity\" feed.\n const recentOrders = useMemo(\n () =>\n [...orders]\n .sort((a, b) => new Date(b.orderDate).getTime() - new Date(a.orderDate).getTime())\n .slice(0, 6),\n [orders]\n );\n\n // ── Order trend (last 30 days) — real per-day order counts + GMV. ──────────\n const { orderTrend, gmvTrend, ordersToday } = useMemo(() => {\n const today = dayKey(new Date());\n const start = today - 29 * DAY_MS;\n const counts = new Map<number, number>();\n const gmv = new Map<number, number>();\n for (let t = start; t <= today; t += DAY_MS) {\n counts.set(t, 0);\n gmv.set(t, 0);\n }\n let todayCount = 0;\n for (const o of orders) {\n const t = dayKey(new Date(o.orderDate));\n if (counts.has(t)) {\n counts.set(t, (counts.get(t) ?? 0) + 1);\n gmv.set(t, (gmv.get(t) ?? 0) + (o.total ?? 0));\n }\n if (t === today) todayCount += 1;\n }\n const oTrend: LabeledValue[] = [...counts.entries()].map(([t, v]) => ({\n label: dayLabel(t),\n value: v,\n }));\n const gTrend: LabeledValue[] = [...gmv.entries()].map(([t, v]) => ({\n label: dayLabel(t),\n value: v,\n }));\n return { orderTrend: oTrend, gmvTrend: gTrend, ordersToday: todayCount };\n }, [orders]);\n\n // ── Revenue by category — derived by joining real orders → provider. ───────\n const revenueSegments = useMemo<Segment[]>(() => {\n const catOf = new Map(providers.map((p) => [p.id, p.category]));\n const byCat = new Map<string, number>();\n for (const o of orders) {\n const cat = catOf.get(o.cartId) ?? 'OTHER';\n byCat.set(cat, (byCat.get(cat) ?? 0) + (o.total ?? 0));\n }\n let entries = [...byCat.entries()].filter(([, v]) => v > 0).sort((a, b) => b[1] - a[1]);\n // Fall back to real category *counts* if no order revenue is available yet.\n if (entries.length === 0 && analytics?.categoryDistribution) {\n entries = Object.entries(analytics.categoryDistribution).sort((a, b) => b[1] - a[1]);\n }\n const top = entries.slice(0, 5);\n const restTotal = entries.slice(5).reduce((s, [, v]) => s + v, 0);\n const segs: Segment[] = top.map(([c, v]) => ({ label: titleCase(c), value: v }));\n if (restTotal > 0) segs.push({ label: 'Others', value: restTotal });\n return segs;\n }, [orders, providers, analytics]);\n\n const revenueIsCurrency = useMemo(() => {\n // If we derived from orders (real ₹), format as currency; if we fell back to\n // category counts, the donut represents cart counts.\n const catOf = new Set(providers.map((p) => p.id));\n return orders.some((o) => catOf.has(o.cartId) && (o.total ?? 0) > 0);\n }, [orders, providers]);\n\n const revenueTotal = useMemo(\n () => revenueSegments.reduce((s, seg) => s + seg.value, 0),\n [revenueSegments]\n );\n\n // ── Top merchants — backend ranking + derived real revenue when available. ─\n const topMerchants = useMemo(() => {\n const revByProvider = new Map<string, number>();\n for (const o of orders)\n revByProvider.set(o.cartId, (revByProvider.get(o.cartId) ?? 0) + (o.total ?? 0));\n const ranked = analytics?.topProviders ?? [];\n return ranked.slice(0, 5).map((p) => ({\n id: p.id,\n name: p.name,\n category: titleCase(p.category),\n rating: p.rating ?? 0,\n revenue: revByProvider.get(p.id) ?? 0,\n }));\n }, [analytics, orders]);\n\n const orderStatusBadge = (status: string) => {\n if (status === 'delivered') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'cancelled') return 'bg-status-error-bg-subtle text-status-error-text';\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n };\n\n /* ---- Loading state ---- */\n if (isLoading) {\n return (\n <PageLayout title=\"Platform Overview\" description=\"Loading platform control room...\">\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {Array.from({ length: 4 }).map((_, i) => (\n <div\n key={i}\n className=\"h-32 animate-pulse rounded-[var(--radius-card)] border border-border-subtle bg-bg-sunken/40\"\n />\n ))}\n </div>\n <div className=\"mt-6 flex items-center gap-2 text-text-secondary\">\n <Loader2 className=\"h-4 w-4 animate-spin\" /> Loading platform data…\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Platform Overview\"\n description={`Welcome, ${firstName}! Monitor and manage the whole Kadaikodi marketplace.`}\n actions={\n pendingProviders.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/carts')}\n className=\"flex items-center gap-2 rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle px-3 py-2 text-sm font-medium text-status-warning-text transition-colors hover:bg-status-warning-bg-subtle/70\"\n >\n <ShieldCheck className=\"h-4 w-4\" />\n {pendingProviders.length} pending approval{pendingProviders.length === 1 ? '' : 's'}\n </button>\n ) : undefined\n }\n >\n <PagePurpose className=\"mb-6\">\n The control room for the whole Kadaikodi marketplace — track GMV, merchants, orders and\n customers at a glance, watch how demand is trending, and clear pending merchant approvals\n first. Everything below is live platform data.\n </PagePurpose>\n\n {/* KPI row — real dashboard stats + real trend sparklines */}\n <div className=\"mb-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"GMV (Total Revenue)\"\n value={fmtCur(stats?.totalRevenue ?? 0)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Active Merchants\"\n value={(stats?.activeProviders ?? 0).toLocaleString('en-IN')}\n icon={<Store className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Total Orders\"\n value={(stats?.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n sparkline={orderTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Active Customers\"\n value={(stats?.activeCustomers ?? 0).toLocaleString('en-IN')}\n icon={<Users className=\"h-4 w-4\" />}\n />\n </div>\n\n {/* Main analytics grid: Revenue donut · Order-trend area · side column */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Revenue by Category */}\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">\n {revenueIsCurrency ? 'Revenue by Category' : 'Carts by Category'}\n </h3>\n {revenueSegments.length > 0 ? (\n <DonutChart\n segments={revenueSegments}\n centerLabel={\n revenueIsCurrency ? fmtCur(revenueTotal) : revenueTotal.toLocaleString('en-IN')\n }\n centerCaption={revenueIsCurrency ? 'revenue' : 'carts'}\n valueFormatter={\n revenueIsCurrency ? (v) => fmtCur(v) : (v) => v.toLocaleString('en-IN')\n }\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Category revenue will appear here as orders come in.\n </p>\n )}\n </GlassCard>\n\n {/* Order Trends */}\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold text-text-primary\">Order Trends</h3>\n <span className=\"rounded-full bg-[var(--color-accent-soft)] px-2.5 py-0.5 text-xs font-medium text-text-primary\">\n Today: {ordersToday.toLocaleString('en-IN')}\n </span>\n </div>\n {orders.length > 0 ? (\n <AreaChart\n series={orderTrend}\n height={200}\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n title=\"Orders per day over the last 30 days\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Order volume will chart here once orders are placed.\n </p>\n )}\n </GlassCard>\n\n {/* Side column: Top Merchants + Pending Approvals */}\n <div className=\"flex flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Top Merchants</h3>\n {topMerchants.length > 0 ? (\n <ul className=\"space-y-3\">\n {topMerchants.map((m, i) => (\n <li key={m.id} className=\"flex items-center gap-3\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-xs font-bold text-text-secondary\">\n {i + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{m.name}</p>\n <p className=\"truncate text-xs text-text-secondary\">{m.category}</p>\n </div>\n <div className=\"shrink-0 text-right\">\n {m.revenue > 0 ? (\n <p className=\"text-sm font-semibold text-text-primary\">\n {fmtCur(m.revenue)}\n </p>\n ) : null}\n <p className=\"flex items-center justify-end gap-1 text-xs text-text-secondary\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n {m.rating.toFixed(1)}\n </p>\n </div>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Top merchants will be ranked here once orders start flowing.\n </p>\n )}\n </GlassCard>\n\n <GlassCard treatment={pendingProviders.length > 0 ? 'glass' : 'plain'} className=\"p-6\">\n <div className=\"flex items-start justify-between gap-3\">\n <div>\n <h3 className=\"text-base font-semibold text-text-primary\">\n Pending Merchant Approvals\n </h3>\n <p className=\"mt-1 text-3xl font-bold tabular-nums text-text-primary\">\n {pendingProviders.length.toLocaleString('en-IN')}\n <span className=\"ml-1 text-base font-normal text-text-secondary\">pending</span>\n </p>\n </div>\n <span className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <UserCheck className=\"h-5 w-5\" />\n </span>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/carts')}\n className=\"mt-4 w-full rounded-lg bg-action-primary-bg py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover\"\n >\n Review\n </button>\n </GlassCard>\n </div>\n </div>\n\n {/* Recent Orders */}\n <div className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recent Orders</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin/orders')}\n className=\"flex items-center gap-1 text-sm font-medium text-text-secondary transition-colors hover:text-text-primary\"\n >\n View all orders\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentOrders.length > 0 ? (\n <div className=\"divide-y divide-border rounded-lg border border-border-subtle bg-bg-surface\">\n {recentOrders.map((order) => (\n <div key={order.id} className=\"flex items-start gap-3 px-4 py-3\">\n <div className=\"mt-0.5 flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken text-text-secondary\">\n <ShoppingBag className=\"h-4 w-4\" />\n </div>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-col justify-between gap-2 sm:flex-row sm:items-center\">\n <p className=\"text-sm font-medium text-text-primary\">\n Order from {order.cartName}\n </p>\n <span\n className={cn(\n 'inline-flex w-fit items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize',\n orderStatusBadge(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-3 text-xs text-text-secondary\">\n <span>{formatRelativeTime(order.orderDate)}</span>\n <span className=\"flex items-center gap-1\">\n <IndianRupee className=\"h-3 w-3\" />\n {(order.total ?? 0).toLocaleString('en-IN')}\n </span>\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No orders yet\"\n description=\"Orders placed across the platform will appear here as they come in.\"\n />\n )}\n </div>\n\n {/* Management Links */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {[\n {\n icon: Store,\n label: 'Merchant Management',\n desc: 'Approve, verify and monitor carts',\n path: '/admin/carts',\n },\n {\n icon: Users,\n label: 'User Management',\n desc: 'Manage users and permissions',\n path: '/admin/users',\n },\n {\n icon: BarChart3,\n label: 'Analytics & Reports',\n desc: 'View detailed platform analytics',\n path: '/admin/analytics',\n },\n {\n icon: IndianRupee,\n label: 'Financial Reports',\n desc: 'Revenue, categories and trends',\n path: '/admin/financial',\n },\n {\n icon: ShoppingBag,\n label: 'Order Management',\n desc: 'Handle orders and customer issues',\n path: '/admin/orders',\n },\n {\n icon: Settings,\n label: 'Platform Settings',\n desc: 'Configure platform-wide settings',\n path: '/admin/settings',\n },\n ].map((link) => {\n const LinkIcon = link.icon;\n return (\n <button\n key={link.label}\n type=\"button\"\n onClick={() => navigateTo(link.path)}\n className={cn(\n 'flex items-center gap-4 rounded-lg border border-border-subtle bg-bg-surface p-4 text-left shadow-sm transition-all sm:p-6',\n 'hover:bg-accent hover:shadow-md'\n )}\n >\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n <LinkIcon className=\"h-5 w-5\" />\n </div>\n <div className=\"min-w-0\">\n <h3 className=\"text-sm font-semibold text-text-primary sm:text-base\">\n {link.label}\n </h3>\n <p className=\"text-xs text-text-secondary sm:text-sm\">{link.desc}</p>\n </div>\n </button>\n );\n })}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA6CA,IAAM,IAAS,OAAU,KAAK,KAExB,KAAa,MACjB,EACG,QAAQ,MAAM,IAAI,CAClB,aAAa,CACb,QAAQ,UAAU,MAAM,EAAE,aAAa,CAAC,EAEvC,KAAU,MAAY;CAC1B,IAAM,IAAI,IAAI,KAAK,EAAE;AAErB,QADA,EAAE,SAAS,GAAG,GAAG,GAAG,EAAE,EACf,EAAE,SAAS;GAGd,KAAY,MAChB,IAAI,KAAK,EAAE,CAAC,mBAAmB,SAAS;CAAE,KAAK;CAAW,OAAO;CAAS,CAAC;AAM7E,SAAgB,IAAiB;CAC/B,IAAM,EAAE,eAAY,mBAAgB,GAAc,EAC5C,IAAY,GAAa,aAAa,GAAa,QAAQ,SAE3D,EAAE,MAAM,GAAO,WAAW,MAAiB,GAAmB,EAC9D,EAAE,MAAM,IAAY,EAAE,EAAE,WAAW,MAAqB,GAAc,EACtE,EAAE,MAAM,IAAS,EAAE,EAAE,WAAW,MAAkB,EAAU,EAAE,CAAC,EAC/D,EAAE,MAAM,MAAc,GAAmB,EAEzC,IAAY,KAAgB,KAAoB,GAEhD,KAAU,MAAmB,EAAe,GAAQ,EAAE,uBAAuB,GAAG,CAAC,EAGjF,IAAmB,QAAc,EAAU,QAAQ,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,EAAU,CAAC,EAGrF,IAAe,QAEjB,CAAC,GAAG,EAAO,CACR,MAAM,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,CAAC,CACjF,MAAM,GAAG,EAAE,EAChB,CAAC,EAAO,CACT,EAGK,EAAE,eAAY,aAAU,mBAAgB,QAAc;EAC1D,IAAM,IAAQ,kBAAO,IAAI,MAAM,CAAC,EAC1B,IAAQ,IAAQ,KAAK,GACrB,oBAAS,IAAI,KAAqB,EAClC,oBAAM,IAAI,KAAqB;AACrC,OAAK,IAAI,IAAI,GAAO,KAAK,GAAO,KAAK,EAEnC,CADA,EAAO,IAAI,GAAG,EAAE,EAChB,EAAI,IAAI,GAAG,EAAE;EAEf,IAAI,IAAa;AACjB,OAAK,IAAM,KAAK,GAAQ;GACtB,IAAM,IAAI,EAAO,IAAI,KAAK,EAAE,UAAU,CAAC;AAKvC,GAJI,EAAO,IAAI,EAAE,KACf,EAAO,IAAI,IAAI,EAAO,IAAI,EAAE,IAAI,KAAK,EAAE,EACvC,EAAI,IAAI,IAAI,EAAI,IAAI,EAAE,IAAI,MAAM,EAAE,SAAS,GAAG,GAE5C,MAAM,MAAO,KAAc;;AAUjC,SAAO;GAAE,YARsB,CAAC,GAAG,EAAO,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,QAAQ;IACpE,OAAO,EAAS,EAAE;IAClB,OAAO;IACR,EAAE;GAK0B,UAJE,CAAC,GAAG,EAAI,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,QAAQ;IACjE,OAAO,EAAS,EAAE;IAClB,OAAO;IACR,EAAE;GAC4C,aAAa;GAAY;IACvE,CAAC,EAAO,CAAC,EAGN,IAAkB,QAAyB;EAC/C,IAAM,IAAQ,IAAI,IAAI,EAAU,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,EACzD,oBAAQ,IAAI,KAAqB;AACvC,OAAK,IAAM,KAAK,GAAQ;GACtB,IAAM,IAAM,EAAM,IAAI,EAAE,OAAO,IAAI;AACnC,KAAM,IAAI,IAAM,EAAM,IAAI,EAAI,IAAI,MAAM,EAAE,SAAS,GAAG;;EAExD,IAAI,IAAU,CAAC,GAAG,EAAM,SAAS,CAAC,CAAC,QAAQ,GAAG,OAAO,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;AAEvF,EAAI,EAAQ,WAAW,KAAK,GAAW,yBACrC,IAAU,OAAO,QAAQ,EAAU,qBAAqB,CAAC,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG;EAEtF,IAAM,IAAM,EAAQ,MAAM,GAAG,EAAE,EACzB,IAAY,EAAQ,MAAM,EAAE,CAAC,QAAQ,GAAG,GAAG,OAAO,IAAI,GAAG,EAAE,EAC3D,IAAkB,EAAI,KAAK,CAAC,GAAG,QAAQ;GAAE,OAAO,EAAU,EAAE;GAAE,OAAO;GAAG,EAAE;AAEhF,SADI,IAAY,KAAG,EAAK,KAAK;GAAE,OAAO;GAAU,OAAO;GAAW,CAAC,EAC5D;IACN;EAAC;EAAQ;EAAW;EAAU,CAAC,EAE5B,IAAoB,QAAc;EAGtC,IAAM,IAAQ,IAAI,IAAI,EAAU,KAAK,MAAM,EAAE,GAAG,CAAC;AACjD,SAAO,EAAO,MAAM,MAAM,EAAM,IAAI,EAAE,OAAO,KAAK,EAAE,SAAS,KAAK,EAAE;IACnE,CAAC,GAAQ,EAAU,CAAC,EAEjB,IAAe,QACb,EAAgB,QAAQ,GAAG,MAAQ,IAAI,EAAI,OAAO,EAAE,EAC1D,CAAC,EAAgB,CAClB,EAGK,IAAe,QAAc;EACjC,IAAM,oBAAgB,IAAI,KAAqB;AAC/C,OAAK,IAAM,KAAK,EACd,GAAc,IAAI,EAAE,SAAS,EAAc,IAAI,EAAE,OAAO,IAAI,MAAM,EAAE,SAAS,GAAG;AAElF,UADe,GAAW,gBAAgB,EAAE,EAC9B,MAAM,GAAG,EAAE,CAAC,KAAK,OAAO;GACpC,IAAI,EAAE;GACN,MAAM,EAAE;GACR,UAAU,EAAU,EAAE,SAAS;GAC/B,QAAQ,EAAE,UAAU;GACpB,SAAS,EAAc,IAAI,EAAE,GAAG,IAAI;GACrC,EAAE;IACF,CAAC,GAAW,EAAO,CAAC,EAEjB,MAAoB,MACpB,MAAW,cAAoB,yDAC/B,MAAW,cAAoB,qDAC5B;AAsBT,QAlBI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAoB,aAAY;YAAlD,CACE,kBAAC,OAAD;GAAK,WAAU;aACZ,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,KAAK,GAAG,MACjC,kBAAC,OAAD,EAEE,WAAU,+FACV,EAFK,EAEL,CACF;GACE,CAAA,EACN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAS,WAAU,wBAAyB,CAAA,EAAA,0BACxC;KACK;MAKf,kBAAC,GAAD;EACE,OAAM;EACN,aAAa,YAAY,EAAU;EACnC,SACE,EAAiB,SAAS,IACxB,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,eAAe;GACzC,WAAU;aAHZ;IAKE,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;IAClC,EAAiB;IAAO;IAAkB,EAAiB,WAAW,IAAI,KAAK;IACzE;OACP,KAAA;YAbR;GAgBE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAIhB,CAAA;GAGd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAO,GAAO,gBAAgB,EAAE;MACvC,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MACzC,WAAW,EAAS,KAAK,MAAM,EAAE,MAAM;MACvC,kBAAiB;MACjB,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,GAAO,mBAAmB,GAAG,eAAe,QAAQ;MAC5D,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,GAAO,eAAe,GAAG,eAAe,QAAQ;MACxD,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MACzC,WAAW,EAAW,KAAK,MAAM,EAAE,MAAM;MACzC,kBAAiB;MACjB,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,GAAO,mBAAmB,GAAG,eAAe,QAAQ;MAC5D,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACE;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,MAAD;OAAI,WAAU;iBACX,IAAoB,wBAAwB;OAC1C,CAAA,EACJ,EAAgB,SAAS,IACxB,kBAAC,GAAD;OACE,UAAU;OACV,aACE,IAAoB,EAAO,EAAa,GAAG,EAAa,eAAe,QAAQ;OAEjF,eAAe,IAAoB,YAAY;OAC/C,gBACE,KAAqB,MAAM,EAAO,EAAE,IAAI,MAAM,EAAE,eAAe,QAAQ;OAEzE,CAAA,GAEF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAEvC,CAAA,CAEI;;KAGZ,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA4C;QAAiB,CAAA,EAC3E,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAAiH,WACvG,EAAY,eAAe,QAAQ,CACtC;UACH;UACL,EAAO,SAAS,IACf,kBAAC,GAAD;OACE,QAAQ;OACR,QAAQ;OACR,iBAAiB,MAAM,EAAE,eAAe,QAAQ;OAChD,OAAM;OACN,CAAA,GAEF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAEvC,CAAA,CAEI;;KAGZ,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAW,WAAU;OAAQ,WAAU;iBAAvC,CACE,kBAAC,MAAD;QAAI,WAAU;kBAAiD;QAAkB,CAAA,EAChF,EAAa,SAAS,IACrB,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAa,KAAK,GAAG,MACpB,kBAAC,MAAD;SAAe,WAAU;mBAAzB;UACE,kBAAC,QAAD;WAAM,WAAU;qBACb,IAAI;WACA,CAAA;UACP,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,KAAD;YAAG,WAAU;sBAAkD,EAAE;YAAS,CAAA,EAC1E,kBAAC,KAAD;YAAG,WAAU;sBAAwC,EAAE;YAAa,CAAA,CAChE;;UACN,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACG,EAAE,UAAU,IACX,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAO,EAAE,QAAQ;YAChB,CAAA,GACF,MACJ,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACE,kBAAC,GAAD,EAAM,WAAU,6DAA8D,CAAA,EAC7E,EAAE,OAAO,QAAQ,EAAE,CAClB;cACA;;UACH;WAnBI,EAAE,GAmBN,CACL;QACC,CAAA,GAEL,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAEvC,CAAA,CAEI;UAEZ,kBAAC,GAAD;OAAW,WAAW,EAAiB,SAAS,IAAI,UAAU;OAAS,WAAU;iBAAjF,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA4C;SAErD,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAiB,OAAO,eAAe,QAAQ,EAChD,kBAAC,QAAD;UAAM,WAAU;oBAAiD;UAAc,CAAA,CAC7E;WACA,EAAA,CAAA,EACN,kBAAC,QAAD;SAAM,WAAU;mBACd,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;SAC5B,CAAA,CACH;WACN,kBAAC,UAAD;QACE,MAAK;QACL,eAAe,EAAW,eAAe;QACzC,WAAU;kBACX;QAEQ,CAAA,CACC;SACR;;KACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAkB,CAAA,EAC1E,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAW,gBAAgB;MAC1C,WAAU;gBAHZ,CAIC,mBAEC,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA,CAC3B;QACL;QACL,EAAa,SAAS,IACrB,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAa,KAAK,MACjB,kBAAC,OAAD;MAAoB,WAAU;gBAA9B,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OAC/B,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAb,CAAqD,eACvC,EAAM,SAChB;YACJ,kBAAC,QAAD;SACE,WAAW,EACT,0FACA,GAAiB,EAAM,OAAO,CAC/B;mBAEA,EAAM,OAAO,QAAQ,MAAM,IAAI;SAC3B,CAAA,CACH;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAO,EAAmB,EAAM,UAAU,EAAQ,CAAA,EAClD,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,GACjC,EAAM,SAAS,GAAG,eAAe,QAAQ,CACtC;WACH;UACF;SACF;QA1BI,EAAM,GA0BV,CACN;KACE,CAAA,GAEN,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;KACZ,CAAA,CAEA;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACD;MACE,MAAM;MACN,OAAO;MACP,MAAM;MACN,MAAM;MACP;KACF,CAAC,KAAK,MAAS;KACd,IAAM,IAAW,EAAK;AACtB,YACE,kBAAC,UAAD;MAEE,MAAK;MACL,eAAe,EAAW,EAAK,KAAK;MACpC,WAAW,EACT,8HACA,kCACD;gBAPH,CASE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OAC5B,CAAA,EACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAK;QACH,CAAA,EACL,kBAAC,KAAD;QAAG,WAAU;kBAA0C,EAAK;QAAS,CAAA,CACjE;SACC;QAjBF,EAAK,MAiBH;MAEX;IACE,CAAA;GACK"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminFinancialPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/AdminFinancialPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA6CH,wBAAgB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"AdminFinancialPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/AdminFinancialPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA6CH,wBAAgB,kBAAkB,gCAkYjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminFinancialPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminFinancialPage.tsx"],"sourcesContent":["/**\n * Admin Financial Page\n * @module kadaikodi/pages/admin\n *\n * Financial reporting wired to the live backend (useDashboardStats +\n * useAdminAnalytics). Revenue, top carts, category breakdown and order\n * trends are all real; the export produces a CSV of the real figures.\n * Metrics with no honest backend source (commission, payouts) are omitted\n * rather than fabricated.\n */\n\nimport { useMemo } from 'react';\nimport {\n IndianRupee,\n TrendingUp,\n Download,\n Calendar,\n BarChart3,\n ShoppingCart,\n ArrowLeft,\n Loader2,\n Award,\n Store,\n ShoppingBag,\n Clock,\n Star,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart, AreaChart } from '../../components/viz';\nimport type { LabeledValue, Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useDashboardStats, useAdminAnalytics } from '../../hooks/useKadaikodiApi';\nimport { formatCurrency } from '../../utils/formatters';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst dayKeyOf = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\nconst dayLbl = (t: number) =>\n new Date(t).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' });\nconst titleCaseFin = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminFinancialPage() {\n const { navigateTo } = useKadaikodi();\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: analytics, isLoading: analyticsLoading } = useAdminAnalytics();\n\n const loading = statsLoading || analyticsLoading;\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n const averageOrderValue = useMemo(() => {\n if (stats && stats.totalOrders > 0) return stats.totalRevenue / stats.totalOrders;\n return 0;\n }, [stats]);\n\n // Cart distribution per category with a real, derived share percentage.\n // The backend exposes counts per category, not revenue per category.\n const categoryRows = useMemo(() => {\n const entries = Object.entries(analytics?.categoryDistribution ?? {});\n const total = entries.reduce((sum, [, count]) => sum + count, 0);\n return entries\n .map(([category, count]) => ({\n category,\n count,\n percentage: total > 0 ? (count / total) * 100 : 0,\n }))\n .sort((a, b) => b.count - a.count);\n }, [analytics]);\n\n const topCarts = analytics?.topProviders ?? [];\n const orderTrends = analytics?.orderTrends ?? [];\n\n // Real GMV over time — bucket the backend's recent-order list by day.\n const gmvTrend = useMemo<LabeledValue[]>(() => {\n if (orderTrends.length === 0) return [];\n const g = new Map<number, number>();\n for (const o of orderTrends) {\n const t = dayKeyOf(new Date(o.createdAt));\n g.set(t, (g.get(t) ?? 0) + (o.total ?? 0));\n }\n return [...g.entries()].sort((a, b) => a[0] - b[0]).map(([t, v]) => ({ label: dayLbl(t), value: v }));\n }, [orderTrends]);\n\n // Real category-count donut.\n const categorySegments = useMemo<Segment[]>(\n () =>\n Object.entries(analytics?.categoryDistribution ?? {})\n .sort((a, b) => b[1] - a[1])\n .map(([c, count]) => ({ label: titleCaseFin(c), value: count })),\n [analytics]\n );\n const totalCarts = useMemo(\n () => categorySegments.reduce((s, seg) => s + seg.value, 0),\n [categorySegments]\n );\n\n const prettyCategory = (c: string) => c.replace(/_/g, ' ').toLowerCase();\n\n const orderStatusBadge = (status: string) => {\n if (status === 'delivered') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'cancelled') return 'bg-status-error-bg-subtle text-status-error-text';\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n };\n\n const handleExportReport = () => {\n const lines: string[] = [];\n lines.push('Kadaikodi Financial Report');\n lines.push('');\n lines.push('Metric,Value');\n lines.push(`Total Revenue,${stats?.totalRevenue ?? 0}`);\n lines.push(`Total Orders,${stats?.totalOrders ?? 0}`);\n lines.push(`Average Order Value,${Math.round(averageOrderValue)}`);\n lines.push(`Pending Orders,${stats?.pendingOrders ?? 0}`);\n lines.push('');\n lines.push('Cart Category Distribution');\n lines.push('Category,Carts,Share %');\n categoryRows.forEach((r) => {\n lines.push(`${r.category},${r.count},${(r.percentage ?? 0).toFixed(1)}`);\n });\n lines.push('');\n lines.push('Top Performing Carts');\n lines.push('Cart,Category,Rating');\n topCarts.forEach((c) => {\n lines.push(`${c.name},${c.category},${c.rating ?? 0}`);\n });\n lines.push('');\n lines.push('Recent Orders');\n lines.push('Date,Status,Amount');\n orderTrends.forEach((o) => {\n lines.push(`${new Date(o.createdAt).toISOString().slice(0, 10)},${o.status},${o.total ?? 0}`);\n });\n\n const blob = new Blob([lines.join('\\n')], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `kadaikodi-financial-report-${new Date().toISOString().slice(0, 10)}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n const medalIcon = (index: number) => {\n if (index < 3) return <Award className=\"h-5 w-5 text-status-warning-text\" />;\n return (\n <span className=\"flex h-5 w-5 items-center justify-center rounded-full bg-bg-sunken text-xs font-bold text-text-secondary\">\n {index + 1}\n </span>\n );\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Financial Reports\" description=\"Loading financial data...\">\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mb-4\" />\n <p className=\"text-text-secondary\">Loading financial data...</p>\n </div>\n </PageLayout>\n );\n }\n\n if (!stats) {\n return (\n <PageLayout title=\"Financial Reports\" description=\"Unable to load financial data\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No financial data available\"\n description=\"We couldn't load the financial figures right now. Please check back shortly.\"\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Financial Reports\"\n description=\"Revenue, top carts and financial trends\"\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin')}\n className=\"flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={handleExportReport}\n className=\"flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Download className=\"h-4 w-4\" /> Export Report\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n The money view of Kadaikodi. Track total revenue and order volume, see which carts and\n categories drive the most business, and watch how orders trend over time — then export a CSV\n for accounting or investor updates.\n </PagePurpose>\n\n {/* Key Metrics */}\n <div className=\"mb-8 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Revenue\"\n value={fmtCur(stats.totalRevenue)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Total Orders\"\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Avg Order Value\"\n value={fmtCur(averageOrderValue)}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Pending Orders\"\n value={(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </div>\n\n {/* GMV over time + Category mix — real charts */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-6 lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold text-text-primary\">GMV over time</h3>\n <span className=\"text-xs text-text-muted\">Recent orders</span>\n </div>\n {gmvTrend.length > 0 ? (\n <AreaChart\n series={gmvTrend}\n height={220}\n valueFormatter={(v) => fmtCur(v)}\n title=\"Gross merchandise value over recent orders\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n GMV will chart here once orders are placed.\n </p>\n )}\n </GlassCard>\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Carts by Category</h3>\n {categorySegments.length > 0 ? (\n <DonutChart\n segments={categorySegments}\n size={150}\n centerLabel={totalCarts.toLocaleString('en-IN')}\n centerCaption=\"carts\"\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Categories will appear here as carts are onboarded.\n </p>\n )}\n </GlassCard>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8\">\n {/* Top Performing Carts */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-4\">\n <ShoppingCart className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Top Performing Carts</h3>\n </div>\n {topCarts.length > 0 ? (\n <div className=\"space-y-3\">\n {topCarts.map((cart, index) => (\n <div\n key={cart.id}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\n <div className=\"flex items-center gap-3\">\n {medalIcon(index)}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{cart.name}</p>\n <p className=\"text-xs text-text-secondary capitalize\">\n {prettyCategory(cart.category)}\n </p>\n </div>\n </div>\n <p className=\"text-sm font-semibold text-text-primary flex items-center gap-1\">\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n {(cart.rating ?? 0).toFixed(1)}\n </p>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Top-performing carts will be ranked here once orders start flowing.\n </p>\n )}\n </div>\n\n {/* Cart Category Distribution */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-4\">\n <BarChart3 className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Cart Category Distribution</h3>\n </div>\n {categoryRows.length > 0 ? (\n <div className=\"space-y-3\">\n {categoryRows.map((category) => (\n <div key={category.category}>\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-sm font-medium text-text-primary capitalize\">\n {prettyCategory(category.category)}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {(category.percentage ?? 0).toFixed(1)}%\n </span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all duration-300\"\n style={{ width: `${category.percentage}%` }}\n />\n </div>\n <div className=\"text-right mt-1\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {(category.count ?? 0).toLocaleString('en-IN')}{' '}\n <span className=\"text-xs font-normal text-text-secondary\">\n {category.count === 1 ? 'cart' : 'carts'}\n </span>\n </span>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Cart categories will appear here as carts are onboarded.\n </p>\n )}\n </div>\n </div>\n\n {/* Recent Orders Table */}\n {orderTrends.length > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 mb-8\">\n <div className=\"flex items-center gap-2 mb-4\">\n <Calendar className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Recent Orders</h3>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Date\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Status\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Amount\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {orderTrends.map((order) => (\n <tr key={order.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3 text-sm font-medium text-text-primary\">\n {new Date(order.createdAt).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })}\n </td>\n <td className=\"px-4 py-3 text-sm\">\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize ${orderStatusBadge(\n order.status\n )}`}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </td>\n <td className=\"px-4 py-3 text-sm text-right text-text-primary\">\n {fmtCur(order.total ?? 0)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* 3 Additional Cards — all real */}\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <ShoppingBag className=\"h-8 w-8 text-primary mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Total Orders</h4>\n <p className=\"text-2xl font-bold text-primary\">\n {(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">All-time orders</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <BarChart3 className=\"h-8 w-8 text-status-success-text mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Average Order Value</h4>\n <p className=\"text-2xl font-bold text-status-success-text\">{fmtCur(averageOrderValue)}</p>\n <p className=\"text-sm text-text-secondary mt-1\">Per order average</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <Store className=\"h-8 w-8 text-text-primary mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Active Carts</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {(stats.activeProviders ?? 0).toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Currently live on the platform</p>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminFinancialPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminFinancialPage.tsx"],"sourcesContent":["/**\n * Admin Financial Page\n * @module kadaikodi/pages/admin\n *\n * Financial reporting wired to the live backend (useDashboardStats +\n * useAdminAnalytics). Revenue, top carts, category breakdown and order\n * trends are all real; the export produces a CSV of the real figures.\n * Metrics with no honest backend source (commission, payouts) are omitted\n * rather than fabricated.\n */\n\nimport { useMemo } from 'react';\nimport {\n IndianRupee,\n TrendingUp,\n Download,\n Calendar,\n BarChart3,\n ShoppingCart,\n ArrowLeft,\n Loader2,\n Award,\n Store,\n ShoppingBag,\n Clock,\n Star,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart, AreaChart } from '../../components/viz';\nimport type { LabeledValue, Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useDashboardStats, useAdminAnalytics } from '../../hooks/useKadaikodiApi';\nimport { formatCurrency } from '../../utils/formatters';\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst dayKeyOf = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\nconst dayLbl = (t: number) =>\n new Date(t).toLocaleDateString('en-IN', { day: 'numeric', month: 'short' });\nconst titleCaseFin = (s: string) =>\n s\n .replace(/_/g, ' ')\n .toLowerCase()\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminFinancialPage() {\n const { navigateTo } = useKadaikodi();\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: analytics, isLoading: analyticsLoading } = useAdminAnalytics();\n\n const loading = statsLoading || analyticsLoading;\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n const averageOrderValue = useMemo(() => {\n if (stats && stats.totalOrders > 0) return stats.totalRevenue / stats.totalOrders;\n return 0;\n }, [stats]);\n\n // Cart distribution per category with a real, derived share percentage.\n // The backend exposes counts per category, not revenue per category.\n const categoryRows = useMemo(() => {\n const entries = Object.entries(analytics?.categoryDistribution ?? {});\n const total = entries.reduce((sum, [, count]) => sum + count, 0);\n return entries\n .map(([category, count]) => ({\n category,\n count,\n percentage: total > 0 ? (count / total) * 100 : 0,\n }))\n .sort((a, b) => b.count - a.count);\n }, [analytics]);\n\n const topCarts = analytics?.topProviders ?? [];\n const orderTrends = analytics?.orderTrends ?? [];\n\n // Real GMV over time — bucket the backend's recent-order list by day.\n const gmvTrend = useMemo<LabeledValue[]>(() => {\n if (orderTrends.length === 0) return [];\n const g = new Map<number, number>();\n for (const o of orderTrends) {\n const t = dayKeyOf(new Date(o.createdAt));\n g.set(t, (g.get(t) ?? 0) + (o.total ?? 0));\n }\n return [...g.entries()]\n .sort((a, b) => a[0] - b[0])\n .map(([t, v]) => ({ label: dayLbl(t), value: v }));\n }, [orderTrends]);\n\n // Real category-count donut.\n const categorySegments = useMemo<Segment[]>(\n () =>\n Object.entries(analytics?.categoryDistribution ?? {})\n .sort((a, b) => b[1] - a[1])\n .map(([c, count]) => ({ label: titleCaseFin(c), value: count })),\n [analytics]\n );\n const totalCarts = useMemo(\n () => categorySegments.reduce((s, seg) => s + seg.value, 0),\n [categorySegments]\n );\n\n const prettyCategory = (c: string) => c.replace(/_/g, ' ').toLowerCase();\n\n const orderStatusBadge = (status: string) => {\n if (status === 'delivered') return 'bg-status-success-bg-subtle text-status-success-text';\n if (status === 'cancelled') return 'bg-status-error-bg-subtle text-status-error-text';\n return 'bg-status-warning-bg-subtle text-status-warning-text';\n };\n\n const handleExportReport = () => {\n const lines: string[] = [];\n lines.push('Kadaikodi Financial Report');\n lines.push('');\n lines.push('Metric,Value');\n lines.push(`Total Revenue,${stats?.totalRevenue ?? 0}`);\n lines.push(`Total Orders,${stats?.totalOrders ?? 0}`);\n lines.push(`Average Order Value,${Math.round(averageOrderValue)}`);\n lines.push(`Pending Orders,${stats?.pendingOrders ?? 0}`);\n lines.push('');\n lines.push('Cart Category Distribution');\n lines.push('Category,Carts,Share %');\n categoryRows.forEach((r) => {\n lines.push(`${r.category},${r.count},${(r.percentage ?? 0).toFixed(1)}`);\n });\n lines.push('');\n lines.push('Top Performing Carts');\n lines.push('Cart,Category,Rating');\n topCarts.forEach((c) => {\n lines.push(`${c.name},${c.category},${c.rating ?? 0}`);\n });\n lines.push('');\n lines.push('Recent Orders');\n lines.push('Date,Status,Amount');\n orderTrends.forEach((o) => {\n lines.push(`${new Date(o.createdAt).toISOString().slice(0, 10)},${o.status},${o.total ?? 0}`);\n });\n\n const blob = new Blob([lines.join('\\n')], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `kadaikodi-financial-report-${new Date().toISOString().slice(0, 10)}.csv`;\n a.click();\n URL.revokeObjectURL(url);\n };\n\n const medalIcon = (index: number) => {\n if (index < 3) return <Award className=\"h-5 w-5 text-status-warning-text\" />;\n return (\n <span className=\"flex h-5 w-5 items-center justify-center rounded-full bg-bg-sunken text-xs font-bold text-text-secondary\">\n {index + 1}\n </span>\n );\n };\n\n if (loading) {\n return (\n <PageLayout title=\"Financial Reports\" description=\"Loading financial data...\">\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mb-4\" />\n <p className=\"text-text-secondary\">Loading financial data...</p>\n </div>\n </PageLayout>\n );\n }\n\n if (!stats) {\n return (\n <PageLayout title=\"Financial Reports\" description=\"Unable to load financial data\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No financial data available\"\n description=\"We couldn't load the financial figures right now. Please check back shortly.\"\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Financial Reports\"\n description=\"Revenue, top carts and financial trends\"\n actions={\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/admin')}\n className=\"flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={handleExportReport}\n className=\"flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <Download className=\"h-4 w-4\" /> Export Report\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n The money view of Kadaikodi. Track total revenue and order volume, see which carts and\n categories drive the most business, and watch how orders trend over time — then export a CSV\n for accounting or investor updates.\n </PagePurpose>\n\n {/* Key Metrics */}\n <div className=\"mb-8 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Total Revenue\"\n value={fmtCur(stats.totalRevenue)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvTrend.map((d) => d.value)}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Total Orders\"\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Avg Order Value\"\n value={fmtCur(averageOrderValue)}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Pending Orders\"\n value={(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </div>\n\n {/* GMV over time + Category mix — real charts */}\n <div className=\"mb-8 grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <GlassCard treatment=\"plain\" className=\"p-6 lg:col-span-2\">\n <div className=\"mb-4 flex items-center justify-between gap-2\">\n <h3 className=\"text-base font-semibold text-text-primary\">GMV over time</h3>\n <span className=\"text-xs text-text-muted\">Recent orders</span>\n </div>\n {gmvTrend.length > 0 ? (\n <AreaChart\n series={gmvTrend}\n height={220}\n valueFormatter={(v) => fmtCur(v)}\n title=\"Gross merchandise value over recent orders\"\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n GMV will chart here once orders are placed.\n </p>\n )}\n </GlassCard>\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Carts by Category</h3>\n {categorySegments.length > 0 ? (\n <DonutChart\n segments={categorySegments}\n size={150}\n centerLabel={totalCarts.toLocaleString('en-IN')}\n centerCaption=\"carts\"\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Categories will appear here as carts are onboarded.\n </p>\n )}\n </GlassCard>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8\">\n {/* Top Performing Carts */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-4\">\n <ShoppingCart className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Top Performing Carts</h3>\n </div>\n {topCarts.length > 0 ? (\n <div className=\"space-y-3\">\n {topCarts.map((cart, index) => (\n <div\n key={cart.id}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\n <div className=\"flex items-center gap-3\">\n {medalIcon(index)}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{cart.name}</p>\n <p className=\"text-xs text-text-secondary capitalize\">\n {prettyCategory(cart.category)}\n </p>\n </div>\n </div>\n <p className=\"text-sm font-semibold text-text-primary flex items-center gap-1\">\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n {(cart.rating ?? 0).toFixed(1)}\n </p>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Top-performing carts will be ranked here once orders start flowing.\n </p>\n )}\n </div>\n\n {/* Cart Category Distribution */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-4\">\n <BarChart3 className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Cart Category Distribution</h3>\n </div>\n {categoryRows.length > 0 ? (\n <div className=\"space-y-3\">\n {categoryRows.map((category) => (\n <div key={category.category}>\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-sm font-medium text-text-primary capitalize\">\n {prettyCategory(category.category)}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {(category.percentage ?? 0).toFixed(1)}%\n </span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full transition-all duration-300\"\n style={{ width: `${category.percentage}%` }}\n />\n </div>\n <div className=\"text-right mt-1\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {(category.count ?? 0).toLocaleString('en-IN')}{' '}\n <span className=\"text-xs font-normal text-text-secondary\">\n {category.count === 1 ? 'cart' : 'carts'}\n </span>\n </span>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Cart categories will appear here as carts are onboarded.\n </p>\n )}\n </div>\n </div>\n\n {/* Recent Orders Table */}\n {orderTrends.length > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 mb-8\">\n <div className=\"flex items-center gap-2 mb-4\">\n <Calendar className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Recent Orders</h3>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Date\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Status\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Amount\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {orderTrends.map((order) => (\n <tr key={order.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3 text-sm font-medium text-text-primary\">\n {new Date(order.createdAt).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })}\n </td>\n <td className=\"px-4 py-3 text-sm\">\n <span\n className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize ${orderStatusBadge(\n order.status\n )}`}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </td>\n <td className=\"px-4 py-3 text-sm text-right text-text-primary\">\n {fmtCur(order.total ?? 0)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* 3 Additional Cards — all real */}\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-6\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <ShoppingBag className=\"h-8 w-8 text-primary mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Total Orders</h4>\n <p className=\"text-2xl font-bold text-primary\">\n {(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">All-time orders</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <BarChart3 className=\"h-8 w-8 text-status-success-text mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Average Order Value</h4>\n <p className=\"text-2xl font-bold text-status-success-text\">{fmtCur(averageOrderValue)}</p>\n <p className=\"text-sm text-text-secondary mt-1\">Per order average</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-center\">\n <Store className=\"h-8 w-8 text-text-primary mx-auto mb-3\" />\n <h4 className=\"text-lg font-semibold text-text-primary mb-2\">Active Carts</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {(stats.activeProviders ?? 0).toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Currently live on the platform</p>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAqCA,IAAM,KAAY,MAAY;CAC5B,IAAM,IAAI,IAAI,KAAK,EAAE;AAErB,QADA,EAAE,SAAS,GAAG,GAAG,GAAG,EAAE,EACf,EAAE,SAAS;GAEd,KAAU,MACd,IAAI,KAAK,EAAE,CAAC,mBAAmB,SAAS;CAAE,KAAK;CAAW,OAAO;CAAS,CAAC,EACvE,KAAgB,MACpB,EACG,QAAQ,MAAM,IAAI,CAClB,aAAa,CACb,QAAQ,UAAU,MAAM,EAAE,aAAa,CAAC;AAM7C,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,GAAc,EAC/B,EAAE,MAAM,GAAO,WAAW,MAAiB,GAAmB,EAC9D,EAAE,MAAM,GAAW,WAAW,MAAqB,GAAmB,EAEtE,IAAU,KAAgB,GAE1B,KAAU,MAAmB,EAAe,GAAQ,EAAE,uBAAuB,GAAG,CAAC,EAEjF,IAAoB,QACpB,KAAS,EAAM,cAAc,IAAU,EAAM,eAAe,EAAM,cAC/D,GACN,CAAC,EAAM,CAAC,EAIL,IAAe,QAAc;EACjC,IAAM,IAAU,OAAO,QAAQ,GAAW,wBAAwB,EAAE,CAAC,EAC/D,IAAQ,EAAQ,QAAQ,GAAK,GAAG,OAAW,IAAM,GAAO,EAAE;AAChE,SAAO,EACJ,KAAK,CAAC,GAAU,QAAY;GAC3B;GACA;GACA,YAAY,IAAQ,IAAK,IAAQ,IAAS,MAAM;GACjD,EAAE,CACF,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM;IACnC,CAAC,EAAU,CAAC,EAET,IAAW,GAAW,gBAAgB,EAAE,EACxC,IAAc,GAAW,eAAe,EAAE,EAG1C,IAAW,QAA8B;AAC7C,MAAI,EAAY,WAAW,EAAG,QAAO,EAAE;EACvC,IAAM,oBAAI,IAAI,KAAqB;AACnC,OAAK,IAAM,KAAK,GAAa;GAC3B,IAAM,IAAI,EAAS,IAAI,KAAK,EAAE,UAAU,CAAC;AACzC,KAAE,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,MAAM,EAAE,SAAS,GAAG;;AAE5C,SAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CACpB,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAC3B,KAAK,CAAC,GAAG,QAAQ;GAAE,OAAO,EAAO,EAAE;GAAE,OAAO;GAAG,EAAE;IACnD,CAAC,EAAY,CAAC,EAGX,IAAmB,QAErB,OAAO,QAAQ,GAAW,wBAAwB,EAAE,CAAC,CAClD,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAC3B,KAAK,CAAC,GAAG,QAAY;EAAE,OAAO,EAAa,EAAE;EAAE,OAAO;EAAO,EAAE,EACpE,CAAC,EAAU,CACZ,EACK,IAAa,QACX,EAAiB,QAAQ,GAAG,MAAQ,IAAI,EAAI,OAAO,EAAE,EAC3D,CAAC,EAAiB,CACnB,EAEK,KAAkB,MAAc,EAAE,QAAQ,MAAM,IAAI,CAAC,aAAa,EAElE,KAAoB,MACpB,MAAW,cAAoB,yDAC/B,MAAW,cAAoB,qDAC5B,wDAGH,UAA2B;EAC/B,IAAM,IAAkB,EAAE;AAuB1B,EAtBA,EAAM,KAAK,6BAA6B,EACxC,EAAM,KAAK,GAAG,EACd,EAAM,KAAK,eAAe,EAC1B,EAAM,KAAK,iBAAiB,GAAO,gBAAgB,IAAI,EACvD,EAAM,KAAK,gBAAgB,GAAO,eAAe,IAAI,EACrD,EAAM,KAAK,uBAAuB,KAAK,MAAM,EAAkB,GAAG,EAClE,EAAM,KAAK,kBAAkB,GAAO,iBAAiB,IAAI,EACzD,EAAM,KAAK,GAAG,EACd,EAAM,KAAK,6BAA6B,EACxC,EAAM,KAAK,yBAAyB,EACpC,EAAa,SAAS,MAAM;AAC1B,KAAM,KAAK,GAAG,EAAE,SAAS,GAAG,EAAE,MAAM,IAAI,EAAE,cAAc,GAAG,QAAQ,EAAE,GAAG;IACxE,EACF,EAAM,KAAK,GAAG,EACd,EAAM,KAAK,uBAAuB,EAClC,EAAM,KAAK,uBAAuB,EAClC,EAAS,SAAS,MAAM;AACtB,KAAM,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,SAAS,GAAG,EAAE,UAAU,IAAI;IACtD,EACF,EAAM,KAAK,GAAG,EACd,EAAM,KAAK,gBAAgB,EAC3B,EAAM,KAAK,qBAAqB,EAChC,EAAY,SAAS,MAAM;AACzB,KAAM,KAAK,GAAG,IAAI,KAAK,EAAE,UAAU,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,EAAE,SAAS,IAAI;IAC7F;EAEF,IAAM,IAAO,IAAI,KAAK,CAAC,EAAM,KAAK,KAAK,CAAC,EAAE,EAAE,MAAM,2BAA2B,CAAC,EACxE,IAAM,IAAI,gBAAgB,EAAK,EAC/B,IAAI,SAAS,cAAc,IAAI;AAIrC,EAHA,EAAE,OAAO,GACT,EAAE,WAAW,+CAA8B,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG,CAAC,OACjF,EAAE,OAAO,EACT,IAAI,gBAAgB,EAAI;IAGpB,KAAa,MACb,IAAQ,IAAU,kBAAC,GAAD,EAAO,WAAU,oCAAqC,CAAA,GAE1E,kBAAC,QAAD;EAAM,WAAU;YACb,IAAQ;EACJ,CAAA;AA2BX,QAvBI,IAEA,kBAAC,GAAD;EAAY,OAAM;EAAoB,aAAY;YAChD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAS,WAAU,mDAAoD,CAAA,EACvE,kBAAC,KAAD;IAAG,WAAU;cAAsB;IAA6B,CAAA,CAC5D;;EACK,CAAA,GAIZ,IAaH,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,SAAS;IACnC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;OACT,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAAA,iBACzB;MACL;;YAnBV;GAsBE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAIhB,CAAA;GAGd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAO,EAAM,aAAa;MACjC,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MACzC,WAAW,EAAS,KAAK,MAAM,EAAE,MAAM;MACvC,kBAAiB;MACjB,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,EAAM,eAAe,GAAG,eAAe,QAAQ;MACvD,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MACzC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAO,EAAkB;MAChC,MAAM,kBAAC,GAAD,EAAY,WAAU,WAAY,CAAA;MACxC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,QAAQ,EAAM,iBAAiB,GAAG,eAAe,QAAQ;MACzD,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACE;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eAAvC,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA4C;OAAkB,CAAA,EAC5E,kBAAC,QAAD;OAAM,WAAU;iBAA0B;OAAoB,CAAA,CAC1D;SACL,EAAS,SAAS,IACjB,kBAAC,GAAD;MACE,QAAQ;MACR,QAAQ;MACR,iBAAiB,MAAM,EAAO,EAAE;MAChC,OAAM;MACN,CAAA,GAEF,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CAEI;QACZ,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eAAvC,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAiD;MAAsB,CAAA,EACpF,EAAiB,SAAS,IACzB,kBAAC,GAAD;MACE,UAAU;MACV,MAAM;MACN,aAAa,EAAW,eAAe,QAAQ;MAC/C,eAAc;MACd,iBAAiB,MAAM,EAAE,eAAe,QAAQ;MAChD,CAAA,GAEF,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CAEI;OACR;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAc,WAAU,+BAAgC,CAAA,EACxD,kBAAC,MAAD;OAAI,WAAU;iBAA0C;OAAyB,CAAA,CAC7E;SACL,EAAS,SAAS,IACjB,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAS,KAAK,GAAM,MACnB,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACG,EAAU,EAAM,EACjB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAyC,EAAK;SAAS,CAAA,EACpE,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAe,EAAK,SAAS;SAC5B,CAAA,CACA,EAAA,CAAA,CACF;WACN,kBAAC,KAAD;QAAG,WAAU;kBAAb,CACE,kBAAC,GAAD,EAAM,WAAU,iEAAkE,CAAA,GAChF,EAAK,UAAU,GAAG,QAAQ,EAAE,CAC5B;UACA;SAhBC,EAAK,GAgBN,CACN;MACE,CAAA,GAEN,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CAEF;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,+BAAgC,CAAA,EACrD,kBAAC,MAAD;OAAI,WAAU;iBAA0C;OAA+B,CAAA,CACnF;SACL,EAAa,SAAS,IACrB,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAa,KAAK,MACjB,kBAAC,OAAD,EAAA,UAAA;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAe,EAAS,SAAS;SAC7B,CAAA,EACP,kBAAC,QAAD;SAAM,WAAU;mBAAhB,EACI,EAAS,cAAc,GAAG,QAAQ,EAAE,EAAC,IAClC;WACH;;OACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,OAAD;SACE,WAAU;SACV,OAAO,EAAE,OAAO,GAAG,EAAS,WAAW,IAAI;SAC3C,CAAA;QACE,CAAA;OACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,QAAD;SAAM,WAAU;mBAAhB;WACI,EAAS,SAAS,GAAG,eAAe,QAAQ;UAAE;UAChD,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAS,UAAU,IAAI,SAAS;WAC5B,CAAA;UACF;;QACH,CAAA;OACF,EAAA,EAvBI,EAAS,SAuBb,CACN;MACE,CAAA,GAEN,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAEvC,CAAA,CAEF;OACF;;GAGL,EAAY,SAAS,KACpB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAU,WAAU,+BAAgC,CAAA,EACpD,kBAAC,MAAD;MAAI,WAAU;gBAA0C;MAAkB,CAAA,CACtE;QACN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,MAAD;SAAI,WAAU;mBAAuF;SAEhG,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBAAuF;SAEhG,CAAA;QACL,kBAAC,MAAD;SAAI,WAAU;mBAAwF;SAEjG,CAAA;QACF;UACC,CAAA,EACR,kBAAC,SAAD;OAAO,WAAU;iBACd,EAAY,KAAK,MAChB,kBAAC,MAAD;QAAmB,WAAU;kBAA7B;SACE,kBAAC,MAAD;UAAI,WAAU;oBACX,IAAI,KAAK,EAAM,UAAU,CAAC,mBAAmB,SAAS;WACrD,KAAK;WACL,OAAO;WACP,MAAM;WACP,CAAC;UACC,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACZ,kBAAC,QAAD;WACE,WAAW,oFAAoF,EAC7F,EAAM,OACP;qBAEA,EAAM,OAAO,QAAQ,MAAM,IAAI;WAC3B,CAAA;UACJ,CAAA;SACL,kBAAC,MAAD;UAAI,WAAU;oBACX,EAAO,EAAM,SAAS,EAAE;UACtB,CAAA;SACF;UApBI,EAAM,GAoBV,CACL;OACI,CAAA,CACF;;KACJ,CAAA,CACF;;GAIR,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAa,WAAU,qCAAsC,CAAA;OAC7D,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAAiB,CAAA;OAC9E,kBAAC,KAAD;QAAG,WAAU;mBACT,EAAM,eAAe,GAAG,eAAe,QAAQ;QAC/C,CAAA;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAmC;QAAmB,CAAA;OAC/D;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAW,WAAU,iDAAkD,CAAA;OACvE,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAAwB,CAAA;OACrF,kBAAC,KAAD;QAAG,WAAU;kBAA+C,EAAO,EAAkB;QAAK,CAAA;OAC1F,kBAAC,KAAD;QAAG,WAAU;kBAAmC;QAAqB,CAAA;OACjE;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAO,WAAU,0CAA2C,CAAA;OAC5D,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAAiB,CAAA;OAC9E,kBAAC,KAAD;QAAG,WAAU;mBACT,EAAM,mBAAmB,GAAG,eAAe,QAAQ;QACnD,CAAA;OACJ,kBAAC,KAAD;QAAG,WAAU;kBAAmC;QAAkC,CAAA;OAC9E;;KACF;;GACK;MApQX,kBAAC,GAAD;EAAY,OAAM;EAAoB,aAAY;YAChD,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA;EACS,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOrdersPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/AdminOrdersPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoDH,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"AdminOrdersPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/AdminOrdersPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoDH,wBAAgB,eAAe,gCAyjB9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOrdersPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminOrdersPage.tsx"],"sourcesContent":["/**\n * Admin Orders Page\n * @module kadaikodi/pages/admin\n *\n * Platform-wide order management wired to the live backend\n * (useOrders + useDashboardStats + useUpdateOrderStatus). Metrics, the order\n * table and the details modal all render real records only — no fabricated\n * customers, carts, workers or deltas.\n */\n\nimport { useState, useMemo } from 'react';\nimport {\n Search,\n ShoppingBag,\n IndianRupee,\n TrendingUp,\n Clock,\n Eye,\n CheckCircle2,\n XCircle,\n RefreshCw,\n AlertCircle,\n Loader2,\n X,\n MapPin,\n Store,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart } from '../../components/viz';\nimport type { Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useOrders, useDashboardStats, useUpdateOrderStatus } from '../../hooks/useKadaikodiApi';\nimport type { MockOrder, OrderStatus } from '../../types';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst dayKeyOf = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\n\nconst ORDER_STATUSES: OrderStatus[] = [\n 'pending',\n 'confirmed',\n 'preparing',\n 'ready',\n 'picked_up',\n 'out_for_delivery',\n 'delivered',\n 'cancelled',\n];\n\nexport function AdminOrdersPage() {\n const { data: orders = [], isLoading } = useOrders({});\n const { data: stats } = useDashboardStats();\n const updateStatus = useUpdateOrderStatus();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [selectedOrder, setSelectedOrder] = useState<MockOrder | null>(null);\n const [showOrderModal, setShowOrderModal] = useState(false);\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n const formatDate = (d: string) =>\n d\n ? new Date(d).toLocaleString('en-IN', {\n day: '2-digit',\n month: 'short',\n hour: '2-digit',\n minute: '2-digit',\n })\n : '—';\n\n // Derived platform metrics — real values only.\n const averageOrderValue = useMemo(() => {\n if (stats && stats.totalOrders > 0) return stats.totalRevenue / stats.totalOrders;\n if (orders.length === 0) return 0;\n return orders.reduce((sum, o) => sum + o.total, 0) / orders.length;\n }, [stats, orders]);\n\n // Real per-day trends (last 30 days) for KPI sparklines.\n const { ordersPerDay, gmvPerDay } = useMemo(() => {\n const today = dayKeyOf(new Date());\n const start = today - 29 * DAY_MS;\n const c = new Map<number, number>();\n const g = new Map<number, number>();\n for (let t = start; t <= today; t += DAY_MS) {\n c.set(t, 0);\n g.set(t, 0);\n }\n for (const o of orders) {\n const t = dayKeyOf(new Date(o.orderDate));\n if (c.has(t)) {\n c.set(t, (c.get(t) ?? 0) + 1);\n g.set(t, (g.get(t) ?? 0) + (o.total ?? 0));\n }\n }\n return { ordersPerDay: [...c.values()], gmvPerDay: [...g.values()] };\n }, [orders]);\n\n // Real order-status distribution for the donut.\n const statusSegments = useMemo<Segment[]>(() => {\n const m = new Map<string, number>();\n for (const o of orders) m.set(o.status, (m.get(o.status) ?? 0) + 1);\n return [...m.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([s, v]) => ({ label: s.replace(/_/g, ' ').replace(/\\b\\w/g, (x) => x.toUpperCase()), value: v }));\n }, [orders]);\n\n const getStatusColor = (status: string) => {\n const map: Record<string, string> = {\n delivered: 'bg-status-success-bg-subtle text-status-success-text',\n preparing: 'bg-status-warning-bg-subtle text-status-warning-text',\n ready: 'bg-status-warning-bg-subtle text-status-warning-text',\n picked_up: 'bg-[var(--color-accent-soft)] text-text-primary',\n out_for_delivery: 'bg-[var(--color-accent-soft)] text-text-primary',\n cancelled: 'bg-status-error-bg-subtle text-status-error-text',\n confirmed: 'bg-[var(--color-accent-soft)] text-text-primary',\n };\n return map[status] ?? 'bg-bg-sunken text-text-secondary';\n };\n\n const getPaymentIcon = (status: string) => {\n switch (status) {\n case 'completed':\n return <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />;\n case 'failed':\n return <XCircle className=\"h-4 w-4 text-status-error-text\" />;\n case 'refunded':\n return <RefreshCw className=\"h-4 w-4 text-text-primary\" />;\n default:\n return <AlertCircle className=\"h-4 w-4 text-status-warning-text\" />;\n }\n };\n\n const filteredOrders = useMemo(() => {\n return orders.filter((order) => {\n const matchesSearch =\n !searchQuery ||\n order.id.toLowerCase().includes(searchQuery.toLowerCase()) ||\n order.cartName.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesStatus = statusFilter === 'all' || order.status === statusFilter;\n return matchesSearch && matchesStatus;\n });\n }, [orders, searchQuery, statusFilter]);\n\n const handleUpdateStatus = (orderId: string, newStatus: OrderStatus) => {\n updateStatus.mutate(\n { id: orderId, status: newStatus.toUpperCase() },\n {\n onSuccess: () => {\n setSelectedOrder((prev) => (prev ? { ...prev, status: newStatus } : prev));\n },\n }\n );\n };\n\n const formatAddress = (order: MockOrder) => {\n const a = order.deliveryAddress;\n const parts = [a.street, a.area, a.city, a.pincode].filter(Boolean);\n return parts.length > 0 ? parts.join(', ') : '—';\n };\n\n if (isLoading) {\n return (\n <PageLayout title=\"Order Management\" description=\"Loading orders...\">\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mb-4\" />\n <p className=\"text-text-secondary\">Loading orders...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Order Management\"\n description={`${stats?.totalOrders ?? orders.length} total orders on the platform`}\n >\n <PagePurpose className=\"mb-6\">\n Every order placed across Kadaikodi in one queue. Use this page to keep operations running\n smoothly — search and filter orders, spot anything pending or stuck, open an order to see\n its full details, and step in when a customer or cart needs help. The metrics up top give\n you a live read on volume, revenue and what's awaiting action.\n </PagePurpose>\n\n {/* KPI row + real order-status donut */}\n {stats && (\n <div className=\"mb-6 grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"grid grid-cols-2 gap-4 lg:col-span-2\">\n <StatCard\n label=\"Total Orders\"\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n sparkline={ordersPerDay}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Total Revenue\"\n value={fmtCur(stats.totalRevenue)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvPerDay}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Avg Order Value\"\n value={fmtCur(averageOrderValue)}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Pending Orders\"\n value={(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </div>\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Order Status</h3>\n {statusSegments.length > 0 ? (\n <DonutChart\n segments={statusSegments}\n size={140}\n centerLabel={orders.length.toLocaleString('en-IN')}\n centerCaption=\"orders\"\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">Status mix will appear here.</p>\n )}\n </GlassCard>\n </div>\n )}\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 mb-6\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search by order ID or cart name...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <select\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface py-2 px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">All Status</option>\n {ORDER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n {/* Dual Layout */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Mobile Card View */}\n <div className=\"block md:hidden space-y-4 p-4\">\n {filteredOrders.map((order) => (\n <div\n key={order.id}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\"\n >\n <div className=\"flex justify-between items-start mb-3\">\n <div>\n <h3 className=\"font-medium text-text-primary text-lg\">{order.cartName}</h3>\n <p className=\"text-sm text-text-secondary\">{formatDate(order.orderDate)}</p>\n </div>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mb-3 text-sm\">\n <div>\n <p className=\"text-text-secondary font-medium\">Items</p>\n <p className=\"text-text-primary\">{order.items.length} item(s)</p>\n </div>\n <div>\n <p className=\"text-text-secondary font-medium\">Order ID</p>\n <p className=\"text-text-primary font-mono text-xs break-all\">{order.id}</p>\n </div>\n </div>\n <div className=\"flex justify-between items-center mb-3\">\n <div>\n <p className=\"text-text-secondary font-medium text-sm\">Amount</p>\n <p className=\"text-text-primary font-semibold text-lg\">{fmtCur(order.total)}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(order.paymentStatus)}\n <div className=\"text-right\">\n <p className=\"text-sm text-text-primary\">{order.paymentMethod}</p>\n <p className=\"text-xs text-text-secondary capitalize\">{order.paymentStatus}</p>\n </div>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setSelectedOrder(order);\n setShowOrderModal(true);\n }}\n className=\"w-full rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors flex items-center justify-center gap-2\"\n >\n <Eye className=\"h-4 w-4\" /> View Details\n </button>\n </div>\n ))}\n </div>\n\n {/* Desktop Table View */}\n <div className=\"hidden md:block overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Order ID\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Cart\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Items\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Amount\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Status\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Payment\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Time\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {filteredOrders.map((order) => (\n <tr key={order.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3 text-sm font-mono font-medium text-text-primary\">\n {order.id.slice(0, 12)}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"text-sm font-medium text-text-primary\">{order.cartName}</div>\n </td>\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {order.items.length} item(s)\n </td>\n <td className=\"px-4 py-3 text-sm font-semibold text-text-primary\">\n {fmtCur(order.total)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(order.paymentStatus)}\n <div>\n <div className=\"text-sm text-text-primary\">{order.paymentMethod}</div>\n <div className=\"text-xs text-text-secondary capitalize\">\n {order.paymentStatus}\n </div>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {formatDate(order.orderDate)}\n </td>\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => {\n setSelectedOrder(order);\n setShowOrderModal(true);\n }}\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-4 w-4\" />\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {filteredOrders.length === 0 && (\n <EmptyState\n illustration=\"empty-search\"\n title={orders.length === 0 ? 'No orders yet' : 'No orders found'}\n description={\n orders.length === 0\n ? 'Orders placed across the platform will appear here as they come in.'\n : 'No orders match your current search and filters. Try a different status or search term.'\n }\n action={\n orders.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n setSearchQuery('');\n setStatusFilter('all');\n }}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Clear filters\n </button>\n ) : undefined\n }\n />\n )}\n </div>\n\n {/* Order Details Modal */}\n {showOrderModal && selectedOrder && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-2xl rounded-lg border border-border-subtle bg-bg-surface shadow-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border-subtle\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Order Details</h2>\n <button\n type=\"button\"\n onClick={() => setShowOrderModal(false)}\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n <div className=\"p-4 space-y-6\">\n {/* Order Info */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Order Information</h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Order ID</p>\n <p className=\"font-medium text-text-primary font-mono text-sm break-all\">\n {selectedOrder.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(selectedOrder.status)\n )}\n >\n {selectedOrder.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Order Date</p>\n <p className=\"font-medium text-text-primary\">\n {formatDate(selectedOrder.orderDate)}\n </p>\n </div>\n {selectedOrder.estimatedDeliveryTime && (\n <div>\n <p className=\"text-sm text-text-secondary\">Estimated Delivery</p>\n <p className=\"font-medium text-text-primary\">\n {formatDate(selectedOrder.estimatedDeliveryTime)}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* Cart Details */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Store className=\"h-4 w-4\" /> Cart\n </h3>\n <p className=\"font-medium text-text-primary\">{selectedOrder.cartName}</p>\n </div>\n\n {/* Delivery Address */}\n {formatAddress(selectedOrder) !== '—' && (\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Delivery Address</h3>\n <p className=\"font-medium text-text-primary flex items-start gap-1\">\n <MapPin className=\"h-3 w-3 mt-1 shrink-0\" />\n {formatAddress(selectedOrder)}\n </p>\n </div>\n )}\n\n {/* Order Items */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Order Items</h3>\n <div className=\"rounded-lg bg-bg-sunken/50 p-4\">\n {selectedOrder.items.length > 0 ? (\n <ul className=\"space-y-2\">\n {selectedOrder.items.map((item) => (\n <li\n key={item.id}\n className=\"flex justify-between text-sm text-text-primary\"\n >\n <span>\n {item.name} × {item.quantity}\n </span>\n <span className=\"font-medium\">{fmtCur(item.price * item.quantity)}</span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">No line items recorded.</p>\n )}\n <div className=\"mt-4 pt-4 border-t border-border-subtle space-y-1 text-sm\">\n <div className=\"flex justify-between text-text-secondary\">\n <span>Subtotal</span>\n <span>{fmtCur(selectedOrder.subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Taxes</span>\n <span>{fmtCur(selectedOrder.taxes)}</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Delivery Fee</span>\n <span>{fmtCur(selectedOrder.deliveryFee)}</span>\n </div>\n {selectedOrder.discount ? (\n <div className=\"flex justify-between text-text-secondary\">\n <span>Discount</span>\n <span>-{fmtCur(selectedOrder.discount)}</span>\n </div>\n ) : null}\n <div className=\"flex justify-between pt-2 border-t border-border-subtle font-semibold text-text-primary text-base\">\n <span>Total</span>\n <span>{fmtCur(selectedOrder.total)}</span>\n </div>\n </div>\n </div>\n </div>\n\n {/* Payment Details */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Payment Details</h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Method</p>\n <p className=\"font-medium text-text-primary\">{selectedOrder.paymentMethod}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(selectedOrder.paymentStatus)}\n <span className=\"font-medium text-text-primary capitalize\">\n {selectedOrder.paymentStatus}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n {/* Customer Notes */}\n {selectedOrder.customerNotes && (\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Special Instructions</h3>\n <p className=\"text-sm rounded-lg bg-bg-sunken/50 p-3 text-text-primary\">\n {selectedOrder.customerNotes}\n </p>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row sm:justify-end gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={() => setShowOrderModal(false)}\n className=\"rounded-lg border border-border-subtle py-2 px-4 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Close\n </button>\n {selectedOrder.status !== 'delivered' && selectedOrder.status !== 'cancelled' && (\n <select\n disabled={updateStatus.isPending}\n className=\"rounded-lg border border-border-subtle bg-bg-surface py-2 px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n value={selectedOrder.status}\n onChange={(e) =>\n handleUpdateStatus(selectedOrder.id, e.target.value as OrderStatus)\n }\n >\n {ORDER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())}\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminOrdersPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminOrdersPage.tsx"],"sourcesContent":["/**\n * Admin Orders Page\n * @module kadaikodi/pages/admin\n *\n * Platform-wide order management wired to the live backend\n * (useOrders + useDashboardStats + useUpdateOrderStatus). Metrics, the order\n * table and the details modal all render real records only — no fabricated\n * customers, carts, workers or deltas.\n */\n\nimport { useState, useMemo } from 'react';\nimport {\n Search,\n ShoppingBag,\n IndianRupee,\n TrendingUp,\n Clock,\n Eye,\n CheckCircle2,\n XCircle,\n RefreshCw,\n AlertCircle,\n Loader2,\n X,\n MapPin,\n Store,\n} from 'lucide-react';\nimport { GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { StatCard, DonutChart } from '../../components/viz';\nimport type { Segment } from '../../components/viz';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useOrders, useDashboardStats, useUpdateOrderStatus } from '../../hooks/useKadaikodiApi';\nimport type { MockOrder, OrderStatus } from '../../types';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\nconst dayKeyOf = (d: Date) => {\n const x = new Date(d);\n x.setHours(0, 0, 0, 0);\n return x.getTime();\n};\n\nconst ORDER_STATUSES: OrderStatus[] = [\n 'pending',\n 'confirmed',\n 'preparing',\n 'ready',\n 'picked_up',\n 'out_for_delivery',\n 'delivered',\n 'cancelled',\n];\n\nexport function AdminOrdersPage() {\n const { data: orders = [], isLoading } = useOrders({});\n const { data: stats } = useDashboardStats();\n const updateStatus = useUpdateOrderStatus();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [selectedOrder, setSelectedOrder] = useState<MockOrder | null>(null);\n const [showOrderModal, setShowOrderModal] = useState(false);\n\n const fmtCur = (amount: number) => formatCurrency(amount, { minimumFractionDigits: 0 });\n\n const formatDate = (d: string) =>\n d\n ? new Date(d).toLocaleString('en-IN', {\n day: '2-digit',\n month: 'short',\n hour: '2-digit',\n minute: '2-digit',\n })\n : '—';\n\n // Derived platform metrics — real values only.\n const averageOrderValue = useMemo(() => {\n if (stats && stats.totalOrders > 0) return stats.totalRevenue / stats.totalOrders;\n if (orders.length === 0) return 0;\n return orders.reduce((sum, o) => sum + o.total, 0) / orders.length;\n }, [stats, orders]);\n\n // Real per-day trends (last 30 days) for KPI sparklines.\n const { ordersPerDay, gmvPerDay } = useMemo(() => {\n const today = dayKeyOf(new Date());\n const start = today - 29 * DAY_MS;\n const c = new Map<number, number>();\n const g = new Map<number, number>();\n for (let t = start; t <= today; t += DAY_MS) {\n c.set(t, 0);\n g.set(t, 0);\n }\n for (const o of orders) {\n const t = dayKeyOf(new Date(o.orderDate));\n if (c.has(t)) {\n c.set(t, (c.get(t) ?? 0) + 1);\n g.set(t, (g.get(t) ?? 0) + (o.total ?? 0));\n }\n }\n return { ordersPerDay: [...c.values()], gmvPerDay: [...g.values()] };\n }, [orders]);\n\n // Real order-status distribution for the donut.\n const statusSegments = useMemo<Segment[]>(() => {\n const m = new Map<string, number>();\n for (const o of orders) m.set(o.status, (m.get(o.status) ?? 0) + 1);\n return [...m.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([s, v]) => ({\n label: s.replace(/_/g, ' ').replace(/\\b\\w/g, (x) => x.toUpperCase()),\n value: v,\n }));\n }, [orders]);\n\n const getStatusColor = (status: string) => {\n const map: Record<string, string> = {\n delivered: 'bg-status-success-bg-subtle text-status-success-text',\n preparing: 'bg-status-warning-bg-subtle text-status-warning-text',\n ready: 'bg-status-warning-bg-subtle text-status-warning-text',\n picked_up: 'bg-[var(--color-accent-soft)] text-text-primary',\n out_for_delivery: 'bg-[var(--color-accent-soft)] text-text-primary',\n cancelled: 'bg-status-error-bg-subtle text-status-error-text',\n confirmed: 'bg-[var(--color-accent-soft)] text-text-primary',\n };\n return map[status] ?? 'bg-bg-sunken text-text-secondary';\n };\n\n const getPaymentIcon = (status: string) => {\n switch (status) {\n case 'completed':\n return <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />;\n case 'failed':\n return <XCircle className=\"h-4 w-4 text-status-error-text\" />;\n case 'refunded':\n return <RefreshCw className=\"h-4 w-4 text-text-primary\" />;\n default:\n return <AlertCircle className=\"h-4 w-4 text-status-warning-text\" />;\n }\n };\n\n const filteredOrders = useMemo(() => {\n return orders.filter((order) => {\n const matchesSearch =\n !searchQuery ||\n order.id.toLowerCase().includes(searchQuery.toLowerCase()) ||\n order.cartName.toLowerCase().includes(searchQuery.toLowerCase());\n const matchesStatus = statusFilter === 'all' || order.status === statusFilter;\n return matchesSearch && matchesStatus;\n });\n }, [orders, searchQuery, statusFilter]);\n\n const handleUpdateStatus = (orderId: string, newStatus: OrderStatus) => {\n updateStatus.mutate(\n { id: orderId, status: newStatus.toUpperCase() },\n {\n onSuccess: () => {\n setSelectedOrder((prev) => (prev ? { ...prev, status: newStatus } : prev));\n },\n }\n );\n };\n\n const formatAddress = (order: MockOrder) => {\n const a = order.deliveryAddress;\n const parts = [a.street, a.area, a.city, a.pincode].filter(Boolean);\n return parts.length > 0 ? parts.join(', ') : '—';\n };\n\n if (isLoading) {\n return (\n <PageLayout title=\"Order Management\" description=\"Loading orders...\">\n <div className=\"flex flex-col items-center justify-center py-20\">\n <Loader2 className=\"h-10 w-10 animate-spin text-text-secondary mb-4\" />\n <p className=\"text-text-secondary\">Loading orders...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Order Management\"\n description={`${stats?.totalOrders ?? orders.length} total orders on the platform`}\n >\n <PagePurpose className=\"mb-6\">\n Every order placed across Kadaikodi in one queue. Use this page to keep operations running\n smoothly — search and filter orders, spot anything pending or stuck, open an order to see\n its full details, and step in when a customer or cart needs help. The metrics up top give\n you a live read on volume, revenue and what's awaiting action.\n </PagePurpose>\n\n {/* KPI row + real order-status donut */}\n {stats && (\n <div className=\"mb-6 grid grid-cols-1 gap-4 lg:grid-cols-3\">\n <div className=\"grid grid-cols-2 gap-4 lg:col-span-2\">\n <StatCard\n label=\"Total Orders\"\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n icon={<ShoppingBag className=\"h-4 w-4\" />}\n sparkline={ordersPerDay}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Total Revenue\"\n value={fmtCur(stats.totalRevenue)}\n icon={<IndianRupee className=\"h-4 w-4\" />}\n sparkline={gmvPerDay}\n sparklineVariant=\"area\"\n />\n <StatCard\n label=\"Avg Order Value\"\n value={fmtCur(averageOrderValue)}\n icon={<TrendingUp className=\"h-4 w-4\" />}\n />\n <StatCard\n label=\"Pending Orders\"\n value={(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </div>\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <h3 className=\"mb-4 text-base font-semibold text-text-primary\">Order Status</h3>\n {statusSegments.length > 0 ? (\n <DonutChart\n segments={statusSegments}\n size={140}\n centerLabel={orders.length.toLocaleString('en-IN')}\n centerCaption=\"orders\"\n valueFormatter={(v) => v.toLocaleString('en-IN')}\n />\n ) : (\n <p className=\"text-sm text-text-secondary\">Status mix will appear here.</p>\n )}\n </GlassCard>\n </div>\n )}\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 mb-6\">\n <div className=\"flex flex-col gap-4\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search by order ID or cart name...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface py-2 pl-10 pr-4 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <select\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface py-2 px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">All Status</option>\n {ORDER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n {/* Dual Layout */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface overflow-hidden\">\n {/* Mobile Card View */}\n <div className=\"block md:hidden space-y-4 p-4\">\n {filteredOrders.map((order) => (\n <div\n key={order.id}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\"\n >\n <div className=\"flex justify-between items-start mb-3\">\n <div>\n <h3 className=\"font-medium text-text-primary text-lg\">{order.cartName}</h3>\n <p className=\"text-sm text-text-secondary\">{formatDate(order.orderDate)}</p>\n </div>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mb-3 text-sm\">\n <div>\n <p className=\"text-text-secondary font-medium\">Items</p>\n <p className=\"text-text-primary\">{order.items.length} item(s)</p>\n </div>\n <div>\n <p className=\"text-text-secondary font-medium\">Order ID</p>\n <p className=\"text-text-primary font-mono text-xs break-all\">{order.id}</p>\n </div>\n </div>\n <div className=\"flex justify-between items-center mb-3\">\n <div>\n <p className=\"text-text-secondary font-medium text-sm\">Amount</p>\n <p className=\"text-text-primary font-semibold text-lg\">{fmtCur(order.total)}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(order.paymentStatus)}\n <div className=\"text-right\">\n <p className=\"text-sm text-text-primary\">{order.paymentMethod}</p>\n <p className=\"text-xs text-text-secondary capitalize\">{order.paymentStatus}</p>\n </div>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => {\n setSelectedOrder(order);\n setShowOrderModal(true);\n }}\n className=\"w-full rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors flex items-center justify-center gap-2\"\n >\n <Eye className=\"h-4 w-4\" /> View Details\n </button>\n </div>\n ))}\n </div>\n\n {/* Desktop Table View */}\n <div className=\"hidden md:block overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle bg-bg-sunken/50\">\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Order ID\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Cart\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Items\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Amount\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Status\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Payment\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Time\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {filteredOrders.map((order) => (\n <tr key={order.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3 text-sm font-mono font-medium text-text-primary\">\n {order.id.slice(0, 12)}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"text-sm font-medium text-text-primary\">{order.cartName}</div>\n </td>\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {order.items.length} item(s)\n </td>\n <td className=\"px-4 py-3 text-sm font-semibold text-text-primary\">\n {fmtCur(order.total)}\n </td>\n <td className=\"px-4 py-3\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(order.paymentStatus)}\n <div>\n <div className=\"text-sm text-text-primary\">{order.paymentMethod}</div>\n <div className=\"text-xs text-text-secondary capitalize\">\n {order.paymentStatus}\n </div>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm text-text-secondary\">\n {formatDate(order.orderDate)}\n </td>\n <td className=\"px-4 py-3\">\n <button\n type=\"button\"\n onClick={() => {\n setSelectedOrder(order);\n setShowOrderModal(true);\n }}\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <Eye className=\"h-4 w-4\" />\n </button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {filteredOrders.length === 0 && (\n <EmptyState\n illustration=\"empty-search\"\n title={orders.length === 0 ? 'No orders yet' : 'No orders found'}\n description={\n orders.length === 0\n ? 'Orders placed across the platform will appear here as they come in.'\n : 'No orders match your current search and filters. Try a different status or search term.'\n }\n action={\n orders.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n setSearchQuery('');\n setStatusFilter('all');\n }}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Clear filters\n </button>\n ) : undefined\n }\n />\n )}\n </div>\n\n {/* Order Details Modal */}\n {showOrderModal && selectedOrder && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\">\n <div className=\"w-full max-w-2xl rounded-lg border border-border-subtle bg-bg-surface shadow-lg max-h-[90vh] overflow-y-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border-subtle\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Order Details</h2>\n <button\n type=\"button\"\n onClick={() => setShowOrderModal(false)}\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <X className=\"h-5 w-5\" />\n </button>\n </div>\n <div className=\"p-4 space-y-6\">\n {/* Order Info */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Order Information</h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Order ID</p>\n <p className=\"font-medium text-text-primary font-mono text-sm break-all\">\n {selectedOrder.id}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n getStatusColor(selectedOrder.status)\n )}\n >\n {selectedOrder.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Order Date</p>\n <p className=\"font-medium text-text-primary\">\n {formatDate(selectedOrder.orderDate)}\n </p>\n </div>\n {selectedOrder.estimatedDeliveryTime && (\n <div>\n <p className=\"text-sm text-text-secondary\">Estimated Delivery</p>\n <p className=\"font-medium text-text-primary\">\n {formatDate(selectedOrder.estimatedDeliveryTime)}\n </p>\n </div>\n )}\n </div>\n </div>\n\n {/* Cart Details */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3 flex items-center gap-2\">\n <Store className=\"h-4 w-4\" /> Cart\n </h3>\n <p className=\"font-medium text-text-primary\">{selectedOrder.cartName}</p>\n </div>\n\n {/* Delivery Address */}\n {formatAddress(selectedOrder) !== '—' && (\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Delivery Address</h3>\n <p className=\"font-medium text-text-primary flex items-start gap-1\">\n <MapPin className=\"h-3 w-3 mt-1 shrink-0\" />\n {formatAddress(selectedOrder)}\n </p>\n </div>\n )}\n\n {/* Order Items */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Order Items</h3>\n <div className=\"rounded-lg bg-bg-sunken/50 p-4\">\n {selectedOrder.items.length > 0 ? (\n <ul className=\"space-y-2\">\n {selectedOrder.items.map((item) => (\n <li\n key={item.id}\n className=\"flex justify-between text-sm text-text-primary\"\n >\n <span>\n {item.name} × {item.quantity}\n </span>\n <span className=\"font-medium\">{fmtCur(item.price * item.quantity)}</span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-sm text-text-secondary\">No line items recorded.</p>\n )}\n <div className=\"mt-4 pt-4 border-t border-border-subtle space-y-1 text-sm\">\n <div className=\"flex justify-between text-text-secondary\">\n <span>Subtotal</span>\n <span>{fmtCur(selectedOrder.subtotal)}</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Taxes</span>\n <span>{fmtCur(selectedOrder.taxes)}</span>\n </div>\n <div className=\"flex justify-between text-text-secondary\">\n <span>Delivery Fee</span>\n <span>{fmtCur(selectedOrder.deliveryFee)}</span>\n </div>\n {selectedOrder.discount ? (\n <div className=\"flex justify-between text-text-secondary\">\n <span>Discount</span>\n <span>-{fmtCur(selectedOrder.discount)}</span>\n </div>\n ) : null}\n <div className=\"flex justify-between pt-2 border-t border-border-subtle font-semibold text-text-primary text-base\">\n <span>Total</span>\n <span>{fmtCur(selectedOrder.total)}</span>\n </div>\n </div>\n </div>\n </div>\n\n {/* Payment Details */}\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Payment Details</h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div>\n <p className=\"text-sm text-text-secondary\">Method</p>\n <p className=\"font-medium text-text-primary\">{selectedOrder.paymentMethod}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">Status</p>\n <div className=\"flex items-center gap-2\">\n {getPaymentIcon(selectedOrder.paymentStatus)}\n <span className=\"font-medium text-text-primary capitalize\">\n {selectedOrder.paymentStatus}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n {/* Customer Notes */}\n {selectedOrder.customerNotes && (\n <div>\n <h3 className=\"font-semibold text-text-primary mb-3\">Special Instructions</h3>\n <p className=\"text-sm rounded-lg bg-bg-sunken/50 p-3 text-text-primary\">\n {selectedOrder.customerNotes}\n </p>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row sm:justify-end gap-3 pt-2\">\n <button\n type=\"button\"\n onClick={() => setShowOrderModal(false)}\n className=\"rounded-lg border border-border-subtle py-2 px-4 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Close\n </button>\n {selectedOrder.status !== 'delivered' && selectedOrder.status !== 'cancelled' && (\n <select\n disabled={updateStatus.isPending}\n className=\"rounded-lg border border-border-subtle bg-bg-surface py-2 px-3 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n value={selectedOrder.status}\n onChange={(e) =>\n handleUpdateStatus(selectedOrder.id, e.target.value as OrderStatus)\n }\n >\n {ORDER_STATUSES.map((s) => (\n <option key={s} value={s}>\n {s.replace(/_/g, ' ').replace(/\\b\\w/g, (c) => c.toUpperCase())}\n </option>\n ))}\n </select>\n )}\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartManagementPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/CartManagementPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyFH,wBAAgB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"CartManagementPage.d.ts","sourceRoot":"","sources":["../../../../src/kadaikodi/pages/admin/CartManagementPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyFH,wBAAgB,kBAAkB,gCA+oBjC"}
|