@burdenoff/microfe-kadaikodi 2026.704.2 → 2026.704.4
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/KadaikodiRoutes.js +30 -26
- package/dist/kadaikodi/KadaikodiRoutes.js.map +1 -1
- package/dist/kadaikodi/hooks/useKadaikodiApi.js +76 -6
- package/dist/kadaikodi/hooks/useKadaikodiApi.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js +45 -107
- package/dist/kadaikodi/pages/admin/AdminAnalyticsPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminDashboard.js +30 -32
- package/dist/kadaikodi/pages/admin/AdminDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.js +137 -120
- package/dist/kadaikodi/pages/admin/AdminFinancialPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.js +2 -2
- package/dist/kadaikodi/pages/admin/AdminOrdersPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminProfilePage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/AdminSettingsPage.js +332 -276
- package/dist/kadaikodi/pages/admin/AdminSettingsPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/CartManagementPage.js +278 -283
- package/dist/kadaikodi/pages/admin/CartManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/admin/UserManagementPage.js +310 -366
- package/dist/kadaikodi/pages/admin/UserManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js +105 -111
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/BusinessSettingsPage.js +290 -257
- package/dist/kadaikodi/pages/cart-owner/BusinessSettingsPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/CartOwnerDashboard.js +84 -70
- package/dist/kadaikodi/pages/cart-owner/CartOwnerDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/CartOwnerProfilePage.js +270 -228
- package/dist/kadaikodi/pages/cart-owner/CartOwnerProfilePage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/CartServiceConfigPage.js +194 -173
- package/dist/kadaikodi/pages/cart-owner/CartServiceConfigPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js +202 -139
- package/dist/kadaikodi/pages/cart-owner/InventoryManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js +232 -0
- package/dist/kadaikodi/pages/cart-owner/InvestmentListingPage.js.map +1 -0
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js +84 -79
- package/dist/kadaikodi/pages/cart-owner/OrderManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/StaffManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/common/HelpSupportPage.js +310 -317
- package/dist/kadaikodi/pages/common/HelpSupportPage.js.map +1 -1
- package/dist/kadaikodi/pages/common/NotificationCenterPage.js.map +1 -1
- package/dist/kadaikodi/pages/common/SettingsPage.js +502 -586
- package/dist/kadaikodi/pages/common/SettingsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.js +243 -143
- package/dist/kadaikodi/pages/customer/BookingDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDetailPage.js +4 -4
- package/dist/kadaikodi/pages/customer/CartDetailPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CartDiscoveryPage.js +2 -2
- package/dist/kadaikodi/pages/customer/CartDiscoveryPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CheckoutPage.js +4 -4
- package/dist/kadaikodi/pages/customer/CheckoutPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CustomerDashboard.js +6 -6
- package/dist/kadaikodi/pages/customer/CustomerDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CustomerProfilePage.js +189 -199
- package/dist/kadaikodi/pages/customer/CustomerProfilePage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/DirectServiceBookingPage.js +6 -4
- package/dist/kadaikodi/pages/customer/DirectServiceBookingPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/FavoritesPage.js +6 -6
- package/dist/kadaikodi/pages/customer/FavoritesPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderHistoryPage.js +4 -4
- package/dist/kadaikodi/pages/customer/OrderHistoryPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.js +409 -304
- package/dist/kadaikodi/pages/customer/OrderTrackingPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/ReviewsPage.js +209 -162
- package/dist/kadaikodi/pages/customer/ReviewsPage.js.map +1 -1
- package/dist/kadaikodi/pages/customer/SavedAddressesPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorAnalyticsPage.js.map +1 -1
- package/dist/kadaikodi/pages/investor/InvestorProfilePage.js +441 -514
- package/dist/kadaikodi/pages/investor/InvestorProfilePage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.js +207 -186
- package/dist/kadaikodi/pages/worker/TaskDetailsPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerOrdersPage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerProfilePage.js +340 -272
- package/dist/kadaikodi/pages/worker/WorkerProfilePage.js.map +1 -1
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js +122 -75
- package/dist/kadaikodi/pages/worker/WorkerSchedulePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminAnalyticsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminAnalyticsPage.tsx"],"sourcesContent":["/**\n * Admin Analytics Page\n * @module kadaikodi/pages/admin\n *\n * Platform analytics wired to the live backend (useDashboardStats +\n * useAdminAnalytics + useProviders + useOrders). Every figure is real or\n * derived from real records — order-status distribution is counted from\n * loaded orders, category/growth breakdowns come from the analytics hook.\n * Metrics with no honest source (operating costs, delivery time, etc.) are\n * omitted rather than fabricated.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Users,\n ShoppingBag,\n IndianRupee,\n BarChart3,\n Zap,\n Loader2,\n Star,\n Award,\n Store,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useDashboardStats,\n useAdminAnalytics,\n useProviders,\n useOrders,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ntype TabKey = 'overview' | 'revenue' | 'users' | 'performance';\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminAnalyticsPage() {\n const { navigateTo } = useKadaikodi();\n\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: analytics, isLoading: analyticsLoading } = useAdminAnalytics();\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const { data: orders = [], isLoading: ordersLoading } = useOrders({});\n\n const [activeTab, setActiveTab] = useState<TabKey>('overview');\n\n const loading = statsLoading || analyticsLoading || providersLoading || ordersLoading;\n\n const fmtCur = (amount: number) => {\n if (amount >= 10000000) return `₹${(amount / 10000000).toFixed(1)}Cr`;\n if (amount >= 100000) return `₹${(amount / 100000).toFixed(1)}L`;\n return formatCurrency(amount, { minimumFractionDigits: 0 });\n };\n\n // Order status distribution — counted from real loaded orders.\n const orderStats = useMemo(() => {\n const total = orders.length;\n const completed = orders.filter((o) => o.status === 'delivered').length;\n const cancelled = orders.filter((o) => o.status === 'cancelled').length;\n const pending = orders.filter(\n (o) => o.status === 'pending' || o.status === 'confirmed'\n ).length;\n const startOfToday = new Date();\n startOfToday.setHours(0, 0, 0, 0);\n const ordersToday = orders.filter(\n (o) => new Date(o.orderDate).getTime() >= startOfToday.getTime()\n ).length;\n const avgOrderValue = stats && stats.totalOrders > 0 ? stats.totalRevenue / stats.totalOrders : 0;\n return { total, completed, cancelled, pending, ordersToday, avgOrderValue };\n }, [orders, stats]);\n\n const pendingCarts = useMemo(() => providers.filter((p) => !p.isOpen).length, [providers]);\n\n const categoryRows = useMemo(() => {\n const rows = analytics?.revenueByCategory ?? [];\n const total = rows.reduce((sum, r) => sum + r.revenue, 0);\n return rows.map((r) => ({ ...r, percentage: total > 0 ? (r.revenue / total) * 100 : 0 }));\n }, [analytics]);\n\n const topCarts = analytics?.topProviders ?? [];\n const customerGrowth = analytics?.customerGrowth ?? [];\n const categoryDistribution = analytics?.categoryDistribution ?? [];\n\n const pct = (part: number, whole: number) => (whole > 0 ? (part / whole) * 100 : 0);\n\n const medalIcon = (index: number) => {\n if (index < 3) return <Award className=\"h-5 w-5 text-status-warning-text\" />;\n return <BarChart3 className=\"h-5 w-5 text-text-secondary\" />;\n };\n\n if (loading || !stats) {\n return (\n <PageLayout title=\"Platform Analytics\" description=\"Loading analytics...\">\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 analytics...</p>\n </div>\n </PageLayout>\n );\n }\n\n const tabs: Array<{ key: TabKey; label: string; icon: typeof BarChart3 }> = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'revenue', label: 'Revenue', icon: IndianRupee },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'performance', label: 'Performance', icon: Zap },\n ];\n\n return (\n <PageLayout title=\"Platform Analytics\" description=\"Comprehensive platform metrics and trends\">\n <PagePurpose className=\"mb-6\">\n Your bird's-eye view of how Kadaikodi is performing. Spot revenue and order trends, see\n which carts and categories drive the most business, and understand where customers are\n growing — so you can decide where to invest and which carts to promote. Switch tabs to drill\n into revenue, users, and operational performance.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-admin-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: 'review-carts',\n label: 'Review top-performing carts',\n description: 'See which carts drive the most revenue and orders.',\n onClick: () => setActiveTab('overview'),\n },\n {\n id: 'check-revenue',\n label: 'Break down revenue by category',\n description: 'Find your strongest categories in the Revenue tab.',\n onClick: () => setActiveTab('revenue'),\n },\n {\n id: 'manage-carts',\n label: 'Approve pending carts',\n description: 'Onboard the carts waiting for review.',\n onClick: () => navigateTo('/admin/carts'),\n done: pendingCarts === 0,\n },\n ]}\n />\n\n <div className=\"flex justify-end mb-6\">\n <span className=\"text-sm text-text-secondary\">\n Last updated: {new Date().toLocaleString('en-IN')}\n </span>\n </div>\n\n {/* Key Metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Key Metrics</h2>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <MetricsCard\n featured\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={fmtCur(stats.totalRevenue)}\n label=\"Total Revenue\"\n />\n <MetricsCard\n icon={<ShoppingBag className=\"h-5 w-5\" />}\n value={stats.totalOrders.toLocaleString('en-IN')}\n label=\"Total Orders\"\n />\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={stats.activeCustomers.toLocaleString('en-IN')}\n label=\"Active Customers\"\n />\n <MetricsCard\n icon={<Store className=\"h-5 w-5\" />}\n value={stats.activeProviders.toLocaleString('en-IN')}\n label=\"Active Carts\"\n trendValue={`${pendingCarts} pending`}\n />\n </div>\n </EmphasisPanel>\n\n {/* Tabs */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface mb-8\">\n <div className=\"border-b border-border-subtle\">\n <nav className=\"flex -mb-px overflow-x-auto\">\n {tabs.map((tab) => {\n const TabIcon = tab.icon;\n return (\n <button\n key={tab.key}\n type=\"button\"\n onClick={() => setActiveTab(tab.key)}\n className={cn(\n 'flex items-center gap-2 py-3 px-4 sm:px-6 text-sm font-medium whitespace-nowrap border-b-2 transition-colors',\n activeTab === tab.key\n ? 'border-primary text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n )}\n >\n <TabIcon className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"space-y-8\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Order Status Distribution\n </h3>\n {orderStats.total > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-4\">\n <p className=\"text-sm text-status-success-text\">Completed Orders</p>\n <p className=\"text-2xl font-bold text-status-success-text\">\n {orderStats.completed.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-success-text/80\">\n {pct(orderStats.completed, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n <div className=\"rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle p-4\">\n <p className=\"text-sm text-status-warning-text\">Pending Orders</p>\n <p className=\"text-2xl font-bold text-status-warning-text\">\n {orderStats.pending.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-warning-text/80\">\n {pct(orderStats.pending, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">Cancelled Orders</p>\n <p className=\"text-2xl font-bold text-status-error-text\">\n {orderStats.cancelled.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-error-text/80\">\n {pct(orderStats.cancelled, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Order breakdowns will appear here once orders are placed.\n </p>\n )}\n </div>\n\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">Top Performing Carts</h3>\n {topCarts.length > 0 ? (\n <div className=\"rounded-lg border border-border-subtle overflow-hidden\">\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\">\n Cart Name\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase\">\n Revenue\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase\">\n Orders\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase\">\n Rating\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {topCarts.map((cart, i) => (\n <tr key={cart.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {medalIcon(i)}\n <span className=\"text-sm font-medium text-text-primary\">\n {cart.name}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm text-right font-medium text-text-primary\">\n {fmtCur(cart.revenue)}\n </td>\n <td className=\"px-4 py-3 text-sm text-right text-text-primary\">\n {cart.orders.toLocaleString('en-IN')}\n </td>\n <td className=\"px-4 py-3 text-sm text-right text-text-primary\">\n <span className=\"flex items-center justify-end gap-1\">\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n {cart.rating.toFixed(1)}\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\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 </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <div className=\"rounded-lg border border-border-focus/30 bg-[var(--color-accent-soft)] p-6\">\n <h4 className=\"text-sm font-medium text-text-primary mb-2\">Total Revenue</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {fmtCur(stats.totalRevenue)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <h4 className=\"text-sm font-medium text-text-secondary mb-2\">Total Orders</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {stats.totalOrders.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-6\">\n <h4 className=\"text-sm font-medium text-status-success-text mb-2\">\n Average Order Value\n </h4>\n <p className=\"text-2xl font-bold text-status-success-text\">\n {fmtCur(orderStats.avgOrderValue)}\n </p>\n </div>\n </div>\n\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">Revenue by Category</h3>\n {categoryRows.length > 0 ? (\n <div className=\"space-y-3\">\n {categoryRows.map((row, i) => (\n <div\n key={row.category}\n className=\"flex items-center justify-between p-4 rounded-lg border border-border-subtle bg-bg-sunken/30\"\n >\n <div className=\"flex items-center gap-3\">\n {medalIcon(i)}\n <div>\n <p className=\"font-medium text-text-primary capitalize\">\n {row.category}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {row.orders.toLocaleString('en-IN')} orders\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"font-bold text-text-primary\">{fmtCur(row.revenue)}</p>\n <p className=\"text-sm text-text-secondary\">\n {row.percentage.toFixed(1)}%\n </p>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Category revenue will appear here once orders are recorded.\n </p>\n )}\n </div>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Active Customers</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {stats.activeCustomers.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Store className=\"h-5 w-5 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Active Cart Owners</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {stats.activeProviders.toLocaleString('en-IN')}\n </p>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/20 p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">Customer Growth</h3>\n {customerGrowth.length > 0 ? (\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-4 py-2 text-left text-xs font-medium text-text-secondary uppercase\">\n Month\n </th>\n <th className=\"px-4 py-2 text-right text-xs font-medium text-text-secondary uppercase\">\n New Customers\n </th>\n <th className=\"px-4 py-2 text-right text-xs font-medium text-text-secondary uppercase\">\n Total Customers\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {customerGrowth.map((row) => (\n <tr key={row.month}>\n <td className=\"px-4 py-2 text-sm font-medium text-text-primary\">\n {row.month}\n </td>\n <td className=\"px-4 py-2 text-sm text-right text-text-primary\">\n +{row.newCustomers.toLocaleString('en-IN')}\n </td>\n <td className=\"px-4 py-2 text-sm text-right text-text-primary\">\n {row.totalCustomers.toLocaleString('en-IN')}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Customer growth trends will appear here as your customer base builds up.\n </p>\n )}\n </div>\n\n {categoryDistribution.length > 0 && (\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Cart Category Distribution\n </h3>\n <div className=\"space-y-3\">\n {categoryDistribution.map((row) => (\n <div key={row.category}>\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-sm font-medium text-text-primary capitalize\">\n {row.category}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {row.count.toLocaleString('en-IN')} ({row.percentage.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: `${row.percentage}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Performance Tab */}\n {activeTab === 'performance' && (\n <div className=\"space-y-8\">\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\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Star className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">\n Customer Satisfaction\n </h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary flex items-center gap-1\">\n <Star className=\"h-6 w-6 fill-status-warning-text text-status-warning-text\" />\n {stats.avgRating > 0 ? stats.avgRating.toFixed(1) : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Average rating across carts</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Completion Rate</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.total > 0\n ? `${pct(orderStats.completed, orderStats.total).toFixed(1)}%`\n : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Orders delivered</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Cancellation Rate</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.total > 0\n ? `${pct(orderStats.cancelled, orderStats.total).toFixed(1)}%`\n : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Orders cancelled</p>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">Today's Activity</h3>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div>\n <p className=\"text-sm text-text-secondary mb-2\">Orders Today</p>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.ordersToday.toLocaleString('en-IN')}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary mb-2\">Pending Orders</p>\n <p className=\"text-3xl font-bold text-status-warning-text\">\n {stats.pendingOrders.toLocaleString('en-IN')}\n </p>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminAnalyticsPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/AdminAnalyticsPage.tsx"],"sourcesContent":["/**\n * Admin Analytics Page\n * @module kadaikodi/pages/admin\n *\n * Platform analytics wired to the live backend (useDashboardStats +\n * useAdminAnalytics + useProviders + useOrders). Every figure is real or\n * derived from real records — order-status distribution is counted from\n * loaded orders, category/growth breakdowns come from the analytics hook.\n * Metrics with no honest source (operating costs, delivery time, etc.) are\n * omitted rather than fabricated.\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n Users,\n ShoppingBag,\n IndianRupee,\n BarChart3,\n Zap,\n Loader2,\n Star,\n Award,\n Store,\n TrendingUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useDashboardStats,\n useAdminAnalytics,\n useProviders,\n useOrders,\n} from '../../hooks/useKadaikodiApi';\nimport { cn } from '../../utils/cn';\nimport { formatCurrency } from '../../utils/formatters';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ntype TabKey = 'overview' | 'revenue' | 'users' | 'performance';\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function AdminAnalyticsPage() {\n const { navigateTo } = useKadaikodi();\n\n const { data: stats, isLoading: statsLoading } = useDashboardStats();\n const { data: analytics, isLoading: analyticsLoading } = useAdminAnalytics();\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const { data: orders = [], isLoading: ordersLoading } = useOrders({});\n\n const [activeTab, setActiveTab] = useState<TabKey>('overview');\n\n const loading = statsLoading || analyticsLoading || providersLoading || ordersLoading;\n\n const fmtCur = (amount: number) => {\n if (amount >= 10000000) return `₹${(amount / 10000000).toFixed(1)}Cr`;\n if (amount >= 100000) return `₹${(amount / 100000).toFixed(1)}L`;\n return formatCurrency(amount, { minimumFractionDigits: 0 });\n };\n\n // Order status distribution — counted from real loaded orders.\n const orderStats = useMemo(() => {\n const total = orders.length;\n const completed = orders.filter((o) => o.status === 'delivered').length;\n const cancelled = orders.filter((o) => o.status === 'cancelled').length;\n const pending = orders.filter((o) => o.status === 'pending' || o.status === 'confirmed').length;\n const startOfToday = new Date();\n startOfToday.setHours(0, 0, 0, 0);\n const ordersToday = orders.filter(\n (o) => new Date(o.orderDate).getTime() >= startOfToday.getTime()\n ).length;\n const avgOrderValue =\n stats && stats.totalOrders > 0 ? stats.totalRevenue / stats.totalOrders : 0;\n return { total, completed, cancelled, pending, ordersToday, avgOrderValue };\n }, [orders, stats]);\n\n const pendingCarts = useMemo(() => providers.filter((p) => !p.isOpen).length, [providers]);\n\n const topCarts = analytics?.topProviders ?? [];\n const customerTotal = analytics?.customerGrowth?.customers ?? 0;\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 categoryDistRows = 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 prettyCategory = (c: string) => c.replace(/_/g, ' ').toLowerCase();\n\n const pct = (part: number, whole: number) => (whole > 0 ? (part / whole) * 100 : 0);\n\n const medalIcon = (index: number) => {\n if (index < 3) return <Award className=\"h-5 w-5 text-status-warning-text\" />;\n return <BarChart3 className=\"h-5 w-5 text-text-secondary\" />;\n };\n\n if (loading || !stats) {\n return (\n <PageLayout title=\"Platform Analytics\" description=\"Loading analytics...\">\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 analytics...</p>\n </div>\n </PageLayout>\n );\n }\n\n const tabs: Array<{ key: TabKey; label: string; icon: typeof BarChart3 }> = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'revenue', label: 'Revenue', icon: IndianRupee },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'performance', label: 'Performance', icon: Zap },\n ];\n\n return (\n <PageLayout title=\"Platform Analytics\" description=\"Comprehensive platform metrics and trends\">\n <PagePurpose className=\"mb-6\">\n Your bird's-eye view of how Kadaikodi is performing. Spot revenue and order trends, see\n which carts and categories drive the most business, and understand where customers are\n growing — so you can decide where to invest and which carts to promote. Switch tabs to drill\n into revenue, users, and operational performance.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-admin-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: 'review-carts',\n label: 'Review top-performing carts',\n description: 'See which carts drive the most revenue and orders.',\n onClick: () => setActiveTab('overview'),\n },\n {\n id: 'check-revenue',\n label: 'Break down revenue by category',\n description: 'Find your strongest categories in the Revenue tab.',\n onClick: () => setActiveTab('revenue'),\n },\n {\n id: 'manage-carts',\n label: 'Approve pending carts',\n description: 'Onboard the carts waiting for review.',\n onClick: () => navigateTo('/admin/carts'),\n done: pendingCarts === 0,\n },\n ]}\n />\n\n <div className=\"flex justify-end mb-6\">\n <span className=\"text-sm text-text-secondary\">\n Last updated: {new Date().toLocaleString('en-IN')}\n </span>\n </div>\n\n {/* Key Metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Key Metrics</h2>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <MetricsCard\n featured\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={fmtCur(stats.totalRevenue)}\n label=\"Total Revenue\"\n />\n <MetricsCard\n icon={<ShoppingBag className=\"h-5 w-5\" />}\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n label=\"Total Orders\"\n />\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={(stats.activeCustomers ?? 0).toLocaleString('en-IN')}\n label=\"Active Customers\"\n />\n <MetricsCard\n icon={<Store className=\"h-5 w-5\" />}\n value={(stats.activeProviders ?? 0).toLocaleString('en-IN')}\n label=\"Active Carts\"\n trendValue={`${pendingCarts} pending`}\n />\n </div>\n </EmphasisPanel>\n\n {/* Tabs */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface mb-8\">\n <div className=\"border-b border-border-subtle\">\n <nav className=\"flex -mb-px overflow-x-auto\">\n {tabs.map((tab) => {\n const TabIcon = tab.icon;\n return (\n <button\n key={tab.key}\n type=\"button\"\n onClick={() => setActiveTab(tab.key)}\n className={cn(\n 'flex items-center gap-2 py-3 px-4 sm:px-6 text-sm font-medium whitespace-nowrap border-b-2 transition-colors',\n activeTab === tab.key\n ? 'border-primary text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n )}\n >\n <TabIcon className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">{tab.label}</span>\n </button>\n );\n })}\n </nav>\n </div>\n\n <div className=\"p-4 sm:p-6\">\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"space-y-8\">\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Order Status Distribution\n </h3>\n {orderStats.total > 0 ? (\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-4\">\n <p className=\"text-sm text-status-success-text\">Completed Orders</p>\n <p className=\"text-2xl font-bold text-status-success-text\">\n {orderStats.completed.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-success-text/80\">\n {pct(orderStats.completed, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n <div className=\"rounded-lg border border-status-warning-text/30 bg-status-warning-bg-subtle p-4\">\n <p className=\"text-sm text-status-warning-text\">Pending Orders</p>\n <p className=\"text-2xl font-bold text-status-warning-text\">\n {orderStats.pending.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-warning-text/80\">\n {pct(orderStats.pending, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle p-4\">\n <p className=\"text-sm text-status-error-text\">Cancelled Orders</p>\n <p className=\"text-2xl font-bold text-status-error-text\">\n {orderStats.cancelled.toLocaleString('en-IN')}\n </p>\n <p className=\"text-sm text-status-error-text/80\">\n {pct(orderStats.cancelled, orderStats.total).toFixed(1)}% of total\n </p>\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Order breakdowns will appear here once orders are placed.\n </p>\n )}\n </div>\n\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Top Performing Carts\n </h3>\n {topCarts.length > 0 ? (\n <div className=\"rounded-lg border border-border-subtle overflow-hidden\">\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\">\n Cart Name\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase\">\n Category\n </th>\n <th className=\"px-4 py-3 text-right text-xs font-medium text-text-secondary uppercase\">\n Rating\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {topCarts.map((cart, i) => (\n <tr key={cart.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n {medalIcon(i)}\n <span className=\"text-sm font-medium text-text-primary\">\n {cart.name}\n </span>\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm text-left text-text-secondary capitalize\">\n {prettyCategory(cart.category)}\n </td>\n <td className=\"px-4 py-3 text-sm text-right text-text-primary\">\n <span className=\"flex items-center justify-end 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 </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\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 </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <div className=\"rounded-lg border border-border-focus/30 bg-[var(--color-accent-soft)] p-6\">\n <h4 className=\"text-sm font-medium text-text-primary mb-2\">Total Revenue</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {fmtCur(stats.totalRevenue)}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <h4 className=\"text-sm font-medium text-text-secondary mb-2\">Total Orders</h4>\n <p className=\"text-2xl font-bold text-text-primary\">\n {(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-status-success-text/30 bg-status-success-bg-subtle p-6\">\n <h4 className=\"text-sm font-medium text-status-success-text mb-2\">\n Average Order Value\n </h4>\n <p className=\"text-2xl font-bold text-status-success-text\">\n {fmtCur(orderStats.avgOrderValue)}\n </p>\n </div>\n </div>\n\n <p className=\"text-sm text-text-secondary\">\n Revenue is not broken down by category by the platform backend. See the Users tab\n for how carts are distributed across categories.\n </p>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Active Customers</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {(stats.activeCustomers ?? 0).toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Store className=\"h-5 w-5 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Active Cart Owners</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {(stats.activeProviders ?? 0).toLocaleString('en-IN')}\n </p>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-sunken/20 p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-sm font-medium text-text-secondary\">Total Customers</h3>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {customerTotal.toLocaleString('en-IN')}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n Customers on the platform to date.\n </p>\n </div>\n\n {categoryDistRows.length > 0 && (\n <div>\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Cart Category Distribution\n </h3>\n <div className=\"space-y-3\">\n {categoryDistRows.map((row) => (\n <div key={row.category}>\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"text-sm font-medium text-text-primary capitalize\">\n {prettyCategory(row.category)}\n </span>\n <span className=\"text-sm text-text-secondary\">\n {(row.count ?? 0).toLocaleString('en-IN')} (\n {(row.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: `${row.percentage}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n\n {/* Performance Tab */}\n {activeTab === 'performance' && (\n <div className=\"space-y-8\">\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\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Star className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">\n Customer Satisfaction\n </h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary flex items-center gap-1\">\n <Star className=\"h-6 w-6 fill-status-warning-text text-status-warning-text\" />\n {stats.avgRating > 0 ? stats.avgRating.toFixed(1) : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Average rating across carts</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Completion Rate</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.total > 0\n ? `${pct(orderStats.completed, orderStats.total).toFixed(1)}%`\n : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Orders delivered</p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <div className=\"flex items-center gap-2 mb-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-secondary\">Cancellation Rate</h4>\n </div>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.total > 0\n ? `${pct(orderStats.cancelled, orderStats.total).toFixed(1)}%`\n : '—'}\n </p>\n <p className=\"text-sm text-text-secondary mt-1\">Orders cancelled</p>\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary mb-4\">\n Today's Activity\n </h3>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-6\">\n <div>\n <p className=\"text-sm text-text-secondary mb-2\">Orders Today</p>\n <p className=\"text-3xl font-bold text-text-primary\">\n {orderStats.ordersToday.toLocaleString('en-IN')}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary mb-2\">Pending Orders</p>\n <p className=\"text-3xl font-bold text-status-warning-text\">\n {(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n </p>\n </div>\n </div>\n </div>\n </div>\n )}\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAgDA,SAAgB,IAAqB;CACnC,IAAM,EAAE,kBAAe,GAAc,EAE/B,EAAE,MAAM,GAAO,WAAW,MAAiB,GAAmB,EAC9D,EAAE,MAAM,GAAW,WAAW,MAAqB,GAAmB,EACtE,EAAE,MAAM,IAAY,EAAE,EAAE,WAAW,MAAqB,GAAc,EACtE,EAAE,MAAM,IAAS,EAAE,EAAE,WAAW,MAAkB,EAAU,EAAE,CAAC,EAE/D,CAAC,GAAW,KAAgB,EAAiB,WAAW,EAExD,IAAU,KAAgB,KAAoB,KAAoB,GAElE,KAAU,MACV,KAAU,MAAiB,KAAK,IAAS,KAAU,QAAQ,EAAE,CAAC,MAC9D,KAAU,MAAe,KAAK,IAAS,KAAQ,QAAQ,EAAE,CAAC,KACvD,EAAe,GAAQ,EAAE,uBAAuB,GAAG,CAAC,EAIvD,IAAa,QAAc;EAC/B,IAAM,IAAQ,EAAO,QACf,IAAY,EAAO,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC,QAC3D,IAAY,EAAO,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC,QAC3D,IAAU,EAAO,QAAQ,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,YAAY,CAAC,QACnF,oBAAe,IAAI,MAAM;AAO/B,SANA,EAAa,SAAS,GAAG,GAAG,GAAG,EAAE,EAM1B;GAAE;GAAO;GAAW;GAAW;GAAS,aAL3B,EAAO,QACxB,MAAM,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS,IAAI,EAAa,SAAS,CACjE,CAAC;GAG0D,eAD1D,KAAS,EAAM,cAAc,IAAI,EAAM,eAAe,EAAM,cAAc;GACD;IAC1E,CAAC,GAAQ,EAAM,CAAC,EAEb,IAAe,QAAc,EAAU,QAAQ,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,EAAU,CAAC,EAEpF,IAAW,GAAW,gBAAgB,EAAE,EACxC,IAAgB,GAAW,gBAAgB,aAAa,GAIxD,IAAmB,QAAc;EACrC,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,KAAkB,MAAc,EAAE,QAAQ,MAAM,IAAI,CAAC,aAAa,EAElE,KAAO,GAAc,MAAmB,IAAQ,IAAK,IAAO,IAAS,MAAM,GAE3E,KAAa,MACb,IAAQ,IAAU,kBAAC,GAAD,EAAO,WAAU,oCAAqC,CAAA,GACrE,kBAAC,GAAD,EAAW,WAAU,+BAAgC,CAAA;AAG9D,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,GAAD;EAAY,OAAM;EAAqB,aAAY;YACjD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAS,WAAU,mDAAoD,CAAA,EACvE,kBAAC,KAAD;IAAG,WAAU;cAAsB;IAAwB,CAAA,CACvD;;EACK,CAAA;CAIjB,IAAM,IAAsE;EAC1E;GAAE,KAAK;GAAY,OAAO;GAAY,MAAM;GAAW;EACvD;GAAE,KAAK;GAAW,OAAO;GAAW,MAAM;GAAa;EACvD;GAAE,KAAK;GAAS,OAAO;GAAS,MAAM;GAAO;EAC7C;GAAE,KAAK;GAAe,OAAO;GAAe,MAAM;GAAK;EACxD;AAED,QACE,kBAAC,GAAD;EAAY,OAAM;EAAqB,aAAY;YAAnD;GACE,kBAAC,GAAD;IAAa,WAAU;cAAO;IAKhB,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,WAAU;IACV,OAAO;KACL;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAa,WAAW;MACxC;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAa,UAAU;MACvC;KACD;MACE,IAAI;MACJ,OAAO;MACP,aAAa;MACb,eAAe,EAAW,eAAe;MACzC,MAAM,MAAiB;MACxB;KACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,QAAD;KAAM,WAAU;eAAhB,CAA8C,mCAC7B,IAAI,MAAM,EAAC,eAAe,QAAQ,CAC5C;;IACH,CAAA;GAGN,kBAAC,GAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,MAAD;KAAI,WAAU;eAA+C;KAAgB,CAAA,EAC7E,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,UAAA;OACA,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OACzC,OAAO,EAAO,EAAM,aAAa;OACjC,OAAM;OACN,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;OACzC,QAAQ,EAAM,eAAe,GAAG,eAAe,QAAQ;OACvD,OAAM;OACN,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,QAAQ,EAAM,mBAAmB,GAAG,eAAe,QAAQ;OAC3D,OAAM;OACN,CAAA;MACF,kBAAC,GAAD;OACE,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;OACnC,QAAQ,EAAM,mBAAmB,GAAG,eAAe,QAAQ;OAC3D,OAAM;OACN,YAAY,GAAG,EAAa;OAC5B,CAAA;MACE;OACQ;;GAGhB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAK,KAAK,MAAQ;OACjB,IAAM,IAAU,EAAI;AACpB,cACE,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAa,EAAI,IAAI;QACpC,WAAW,EACT,gHACA,MAAc,EAAI,MACd,gCACA,iEACL;kBATH,CAWE,kBAAC,GAAD,EAAS,WAAU,WAAY,CAAA,EAC/B,kBAAC,QAAD;SAAM,WAAU;mBAAoB,EAAI;SAAa,CAAA,CAC9C;UAZF,EAAI,IAYF;QAEX;MACE,CAAA;KACF,CAAA,EAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MAEG,MAAc,cACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAExD,CAAA,EACJ,EAAW,QAAQ,IAClB,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,KAAD;YAAG,WAAU;sBAAmC;YAAoB,CAAA;WACpE,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAW,UAAU,eAAe,QAAQ;YAC3C,CAAA;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACG,EAAI,EAAW,WAAW,EAAW,MAAM,CAAC,QAAQ,EAAE,EAAC,aACtD;;WACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,KAAD;YAAG,WAAU;sBAAmC;YAAkB,CAAA;WAClE,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAW,QAAQ,eAAe,QAAQ;YACzC,CAAA;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACG,EAAI,EAAW,SAAS,EAAW,MAAM,CAAC,QAAQ,EAAE,EAAC,aACpD;;WACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,KAAD;YAAG,WAAU;sBAAiC;YAAoB,CAAA;WAClE,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAW,UAAU,eAAe,QAAQ;YAC3C,CAAA;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACG,EAAI,EAAW,WAAW,EAAW,MAAM,CAAC,QAAQ,EAAE,EAAC,aACtD;;WACA;;SACF;YAEN,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAEvC,CAAA,CAEF,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;QAAI,WAAU;kBAA+C;QAExD,CAAA,EACJ,EAAS,SAAS,IACjB,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,SAAD;SAAO,WAAU;mBAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;UAAI,WAAU;oBAAd;WACE,kBAAC,MAAD;YAAI,WAAU;sBAAwE;YAEjF,CAAA;WACL,kBAAC,MAAD;YAAI,WAAU;sBAAwE;YAEjF,CAAA;WACL,kBAAC,MAAD;YAAI,WAAU;sBAAyE;YAElF,CAAA;WACF;aACC,CAAA,EACR,kBAAC,SAAD;UAAO,WAAU;oBACd,EAAS,KAAK,GAAM,MACnB,kBAAC,MAAD;WAAkB,WAAU;qBAA5B;YACE,kBAAC,MAAD;aAAI,WAAU;uBACZ,kBAAC,OAAD;cAAK,WAAU;wBAAf,CACG,EAAU,EAAE,EACb,kBAAC,QAAD;eAAM,WAAU;yBACb,EAAK;eACD,CAAA,CACH;;aACH,CAAA;YACL,kBAAC,MAAD;aAAI,WAAU;uBACX,EAAe,EAAK,SAAS;aAC3B,CAAA;YACL,kBAAC,MAAD;aAAI,WAAU;uBACZ,kBAAC,QAAD;cAAM,WAAU;wBAAhB,CACE,kBAAC,GAAD,EAAM,WAAU,iEAAkE,CAAA,GAChF,EAAK,UAAU,GAAG,QAAQ,EAAE,CACzB;;aACJ,CAAA;YACF;aAlBI,EAAK,GAkBT,CACL;UACI,CAAA,CACF;;QACJ,CAAA,GAEN,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAEvC,CAAA,CAEF,EAAA,CAAA,CACF;;MAIP,MAAc,aACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA6C;WAAkB,CAAA,EAC7E,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAO,EAAM,aAAa;WACzB,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAA+C;WAAiB,CAAA,EAC9E,kBAAC,KAAD;WAAG,WAAU;sBACT,EAAM,eAAe,GAAG,eAAe,QAAQ;WAC/C,CAAA,CACA;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAoD;WAE7D,CAAA,EACL,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAO,EAAW,cAAc;WAC/B,CAAA,CACA;;SACF;WAEN,kBAAC,KAAD;QAAG,WAAU;kBAA8B;QAGvC,CAAA,CACA;;MAIP,MAAc,WACb,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,+BAAgC,CAAA,EACjD,kBAAC,MAAD;YAAI,WAAU;sBAA0C;YAAqB,CAAA,CACzE;cACN,kBAAC,KAAD;WAAG,WAAU;sBACT,EAAM,mBAAmB,GAAG,eAAe,QAAQ;WACnD,CAAA,CACA;aACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,+BAAgC,CAAA,EACjD,kBAAC,MAAD;YAAI,WAAU;sBAA0C;YAAuB,CAAA,CAC3E;cACN,kBAAC,KAAD;WAAG,WAAU;sBACT,EAAM,mBAAmB,GAAG,eAAe,QAAQ;WACnD,CAAA,CACA;YACF;;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,+BAAgC,CAAA,EACjD,kBAAC,MAAD;YAAI,WAAU;sBAA0C;YAAoB,CAAA,CACxE;;UACN,kBAAC,KAAD;WAAG,WAAU;qBACV,EAAc,eAAe,QAAQ;WACpC,CAAA;UACJ,kBAAC,KAAD;WAAG,WAAU;qBAAmC;WAE5C,CAAA;UACA;;QAEL,EAAiB,SAAS,KACzB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA+C;SAExD,CAAA,EACL,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAiB,KAAK,MACrB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBACb,EAAe,EAAI,SAAS;WACxB,CAAA,EACP,kBAAC,QAAD;WAAM,WAAU;qBAAhB;aACI,EAAI,SAAS,GAAG,eAAe,QAAQ;YAAC;aACxC,EAAI,cAAc,GAAG,QAAQ,EAAE;YAAC;YAC7B;aACH;aACN,kBAAC,OAAD;UAAK,WAAU;oBACb,kBAAC,OAAD;WACE,WAAU;WACV,OAAO,EAAE,OAAO,GAAG,EAAI,WAAW,IAAI;WACtC,CAAA;UACE,CAAA,CACF,EAAA,EAhBI,EAAI,SAgBR,CACN;SACE,CAAA,CACF,EAAA,CAAA;QAEJ;;MAIP,MAAc,iBACb,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,+BAAgC,CAAA,EAChD,kBAAC,MAAD;aAAI,WAAU;uBAA0C;aAEnD,CAAA,CACD;;WACN,kBAAC,KAAD;YAAG,WAAU;sBAAb,CACE,kBAAC,GAAD,EAAM,WAAU,6DAA8D,CAAA,EAC7E,EAAM,YAAY,IAAI,EAAM,UAAU,QAAQ,EAAE,GAAG,IAClD;;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAmC;YAA+B,CAAA;WAC3E;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAY,WAAU,+BAAgC,CAAA,EACtD,kBAAC,MAAD;aAAI,WAAU;uBAA0C;aAAoB,CAAA,CACxE;;WACN,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAW,QAAQ,IAChB,GAAG,EAAI,EAAW,WAAW,EAAW,MAAM,CAAC,QAAQ,EAAE,CAAC,KAC1D;YACF,CAAA;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAmC;YAAoB,CAAA;WAChE;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,OAAD;YAAK,WAAU;sBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,+BAAgC,CAAA,EACrD,kBAAC,MAAD;aAAI,WAAU;uBAA0C;aAAsB,CAAA,CAC1E;;WACN,kBAAC,KAAD;YAAG,WAAU;sBACV,EAAW,QAAQ,IAChB,GAAG,EAAI,EAAW,WAAW,EAAW,MAAM,CAAC,QAAQ,EAAE,CAAC,KAC1D;YACF,CAAA;WACJ,kBAAC,KAAD;YAAG,WAAU;sBAAmC;YAAoB,CAAA;WAChE;;SACF;WAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA+C;SAExD,CAAA,EACL,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAmC;UAAgB,CAAA,EAChE,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAW,YAAY,eAAe,QAAQ;UAC7C,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAmC;UAAkB,CAAA,EAClE,kBAAC,KAAD;UAAG,WAAU;qBACT,EAAM,iBAAiB,GAAG,eAAe,QAAQ;UACjD,CAAA,CACA,EAAA,CAAA,CACF;WACF;UACF;;MAEJ;OACF;;GACK"}
|
|
@@ -11,7 +11,7 @@ import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
|
11
11
|
import { CTAOverflowMenu as D, EmphasisPanel as O, NextSteps as k, PagePurpose as A } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/kadaikodi/pages/admin/AdminDashboard.tsx
|
|
13
13
|
function j() {
|
|
14
|
-
let { navigateTo: j, currentUser: M } = e(), N = M?.firstName ?? M?.name ?? "Admin", { data: P, isLoading: F } = o(), { data: I = [], isLoading: L } = c(), { data: R = [], isLoading: z } = s({}), { data: B } = a(), V = F || L || z, H = (e) => l(e, { minimumFractionDigits: 0 }), U = d(() => I.filter((e) => !e.isOpen).length, [I]), W = d(() => [...R].sort((e, t) => new Date(t.orderDate).getTime() - new Date(e.orderDate).getTime()).slice(0, 6), [R]), G = (e) => e === "delivered" ? "bg-status-success-bg-subtle text-status-success-text" : e === "cancelled" ? "bg-status-error-bg-subtle text-status-error-text" : "bg-status-warning-bg-subtle text-status-warning-text";
|
|
14
|
+
let { navigateTo: j, currentUser: M } = e(), N = M?.firstName ?? M?.name ?? "Admin", { data: P, isLoading: F } = o(), { data: I = [], isLoading: L } = c(), { data: R = [], isLoading: z } = s({}), { data: B } = a(), V = F || L || z, H = (e) => l(e, { minimumFractionDigits: 0 }), U = d(() => I.filter((e) => !e.isOpen).length, [I]), W = d(() => [...R].sort((e, t) => new Date(t.orderDate).getTime() - new Date(e.orderDate).getTime()).slice(0, 6), [R]), G = (e) => e.replace(/_/g, " ").toLowerCase(), K = B?.topProviders ?? [], q = Object.entries(B?.categoryDistribution ?? {}).sort((e, t) => t[1] - e[1]), J = (e) => e === "delivered" ? "bg-status-success-bg-subtle text-status-success-text" : e === "cancelled" ? "bg-status-error-bg-subtle text-status-error-text" : "bg-status-warning-bg-subtle text-status-warning-text";
|
|
15
15
|
return V ? /* @__PURE__ */ T(n, {
|
|
16
16
|
title: "Admin Dashboard",
|
|
17
17
|
description: "Loading...",
|
|
@@ -130,17 +130,17 @@ function j() {
|
|
|
130
130
|
children: [
|
|
131
131
|
/* @__PURE__ */ T(r, {
|
|
132
132
|
icon: /* @__PURE__ */ T(w, { className: "h-5 w-5" }),
|
|
133
|
-
value: P.activeCustomers.toLocaleString("en-IN"),
|
|
133
|
+
value: (P.activeCustomers ?? 0).toLocaleString("en-IN"),
|
|
134
134
|
label: "Active Customers"
|
|
135
135
|
}),
|
|
136
136
|
/* @__PURE__ */ T(r, {
|
|
137
137
|
icon: /* @__PURE__ */ T(C, { className: "h-5 w-5" }),
|
|
138
|
-
value: P.activeProviders.toLocaleString("en-IN"),
|
|
138
|
+
value: (P.activeProviders ?? 0).toLocaleString("en-IN"),
|
|
139
139
|
label: "Active Carts"
|
|
140
140
|
}),
|
|
141
141
|
/* @__PURE__ */ T(r, {
|
|
142
142
|
icon: /* @__PURE__ */ T(x, { className: "h-5 w-5" }),
|
|
143
|
-
value: P.totalOrders.toLocaleString("en-IN"),
|
|
143
|
+
value: (P.totalOrders ?? 0).toLocaleString("en-IN"),
|
|
144
144
|
label: "Total Orders"
|
|
145
145
|
}),
|
|
146
146
|
/* @__PURE__ */ T(r, {
|
|
@@ -181,7 +181,7 @@ function j() {
|
|
|
181
181
|
children: "Pending Orders"
|
|
182
182
|
}), /* @__PURE__ */ T("p", {
|
|
183
183
|
className: "text-xl font-bold text-status-warning-text",
|
|
184
|
-
children: P.pendingOrders.toLocaleString("en-IN")
|
|
184
|
+
children: (P.pendingOrders ?? 0).toLocaleString("en-IN")
|
|
185
185
|
})]
|
|
186
186
|
}),
|
|
187
187
|
/* @__PURE__ */ E("div", {
|
|
@@ -226,14 +226,14 @@ function j() {
|
|
|
226
226
|
className: "text-sm font-medium text-text-primary",
|
|
227
227
|
children: ["Order from ", e.cartName]
|
|
228
228
|
}), /* @__PURE__ */ T("span", {
|
|
229
|
-
className: t("inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize w-fit",
|
|
229
|
+
className: t("inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize w-fit", J(e.status)),
|
|
230
230
|
children: e.status.replace(/_/g, " ")
|
|
231
231
|
})]
|
|
232
232
|
}), /* @__PURE__ */ E("div", {
|
|
233
233
|
className: "flex flex-wrap items-center mt-1 text-xs text-text-secondary gap-3",
|
|
234
234
|
children: [/* @__PURE__ */ T("span", { children: u(e.orderDate) }), /* @__PURE__ */ E("span", {
|
|
235
235
|
className: "flex items-center gap-1",
|
|
236
|
-
children: [/* @__PURE__ */ T(v, { className: "h-3 w-3" }), e.total.toLocaleString("en-IN")]
|
|
236
|
+
children: [/* @__PURE__ */ T(v, { className: "h-3 w-3" }), (e.total ?? 0).toLocaleString("en-IN")]
|
|
237
237
|
})]
|
|
238
238
|
})]
|
|
239
239
|
})]
|
|
@@ -252,26 +252,30 @@ function j() {
|
|
|
252
252
|
className: "flex items-center gap-2 mb-4",
|
|
253
253
|
children: [/* @__PURE__ */ T(g, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ T("h3", {
|
|
254
254
|
className: "text-lg font-semibold text-text-primary",
|
|
255
|
-
children: "
|
|
255
|
+
children: "Carts by Category"
|
|
256
256
|
})]
|
|
257
|
-
}),
|
|
257
|
+
}), q.length > 0 ? /* @__PURE__ */ T("div", {
|
|
258
258
|
className: "space-y-3",
|
|
259
|
-
children:
|
|
259
|
+
children: q.map(([e, t]) => /* @__PURE__ */ E("div", {
|
|
260
260
|
className: "flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3",
|
|
261
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ T("p", {
|
|
262
262
|
className: "text-sm font-medium text-text-primary capitalize",
|
|
263
|
-
children: e
|
|
263
|
+
children: G(e)
|
|
264
264
|
}), /* @__PURE__ */ E("p", {
|
|
265
|
-
className: "text-xs text-text-secondary",
|
|
266
|
-
children: [e.orders.toLocaleString("en-IN"), " orders"]
|
|
267
|
-
})] }), /* @__PURE__ */ T("p", {
|
|
268
265
|
className: "text-sm font-semibold text-text-primary",
|
|
269
|
-
children:
|
|
266
|
+
children: [
|
|
267
|
+
(t ?? 0).toLocaleString("en-IN"),
|
|
268
|
+
" ",
|
|
269
|
+
/* @__PURE__ */ T("span", {
|
|
270
|
+
className: "text-xs font-normal text-text-secondary",
|
|
271
|
+
children: t === 1 ? "cart" : "carts"
|
|
272
|
+
})
|
|
273
|
+
]
|
|
270
274
|
})]
|
|
271
|
-
}, e
|
|
275
|
+
}, e))
|
|
272
276
|
}) : /* @__PURE__ */ T("p", {
|
|
273
277
|
className: "text-sm text-text-secondary",
|
|
274
|
-
children: "
|
|
278
|
+
children: "Cart categories will appear here as carts are onboarded."
|
|
275
279
|
})]
|
|
276
280
|
}), /* @__PURE__ */ E("div", {
|
|
277
281
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-6",
|
|
@@ -281,25 +285,19 @@ function j() {
|
|
|
281
285
|
className: "text-lg font-semibold text-text-primary",
|
|
282
286
|
children: "Top Carts"
|
|
283
287
|
})]
|
|
284
|
-
}),
|
|
288
|
+
}), K.length > 0 ? /* @__PURE__ */ T("div", {
|
|
285
289
|
className: "space-y-3",
|
|
286
|
-
children:
|
|
290
|
+
children: K.map((e) => /* @__PURE__ */ E("div", {
|
|
287
291
|
className: "flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3",
|
|
288
292
|
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("p", {
|
|
289
293
|
className: "text-sm font-medium text-text-primary",
|
|
290
294
|
children: e.name
|
|
291
|
-
}), /* @__PURE__ */
|
|
292
|
-
className: "text-xs text-text-secondary
|
|
293
|
-
children:
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
e.orders.toLocaleString("en-IN"),
|
|
298
|
-
" orders"
|
|
299
|
-
]
|
|
300
|
-
})] }), /* @__PURE__ */ T("p", {
|
|
301
|
-
className: "text-sm font-semibold text-text-primary",
|
|
302
|
-
children: H(e.revenue)
|
|
295
|
+
}), /* @__PURE__ */ T("p", {
|
|
296
|
+
className: "text-xs text-text-secondary capitalize",
|
|
297
|
+
children: G(e.category)
|
|
298
|
+
})] }), /* @__PURE__ */ E("p", {
|
|
299
|
+
className: "text-sm font-semibold text-text-primary flex items-center gap-1",
|
|
300
|
+
children: [/* @__PURE__ */ T(S, { className: "h-3.5 w-3.5 fill-status-warning-text text-status-warning-text" }), (e.rating ?? 0).toFixed(1)]
|
|
303
301
|
})]
|
|
304
302
|
}, e.id))
|
|
305
303
|
}) : /* @__PURE__ */ T("p", {
|
|
@@ -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 * activity row and breakdown is derived from real hook data\n * (useDashboardStats + useProviders + useOrders + useAdminAnalytics).\n * Where a metric has no honest backend source it is omitted rather than faked.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n Store,\n ShoppingBag,\n IndianRupee,\n Activity,\n ArrowRight,\n Star,\n Settings,\n BarChart3,\n Award,\n FileText,\n AlertTriangle,\n Loader2,\n} from 'lucide-react';\nimport { CTAOverflowMenu, EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { EmptyState } from '../../components/EmptyState';\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/* 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 // Carts awaiting activation/verification — real derivation from provider status.\n const pendingCarts = useMemo(() => providers.filter((p) => !p.isOpen).length, [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 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=\"Admin Dashboard\" description=\"Loading...\">\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 admin dashboard...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Admin Dashboard\"\n description={`Welcome, ${firstName}! Monitor and manage the platform.`}\n actions={\n pendingCarts > 0 ? (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-3 py-2\">\n <AlertTriangle className=\"h-4 w-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n {pendingCarts} carts awaiting review\n </span>\n </div>\n ) : undefined\n }\n >\n <PagePurpose className=\"mb-6\">\n The control room for the whole Kadaikodi marketplace — approve new carts, keep an eye on\n users, orders and platform revenue, and act on anything that needs attention. Clear pending\n approvals first, then review platform health below.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-admin-dashboard\"\n className=\"mb-6\"\n steps={[\n {\n id: 'approvals',\n label: 'Review pending cart approvals',\n description: 'Vet and approve new carts waiting to go live.',\n onClick: () => navigateTo('/admin/carts'),\n done: pendingCarts === 0,\n },\n {\n id: 'users',\n label: 'Manage users',\n description: 'Review accounts, roles and access across the platform.',\n onClick: () => navigateTo('/admin/users'),\n },\n {\n id: 'financial',\n label: 'Check financial health',\n description: 'Track revenue, commission and payouts.',\n onClick: () => navigateTo('/admin/financial'),\n },\n ]}\n />\n\n {/* Hero — the one emphasis zone: pending approvals + primary action + overflow */}\n <EmphasisPanel className=\"mb-8\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Quick Actions</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {pendingCarts > 0\n ? `${pendingCarts} carts awaiting your review.`\n : 'All caught up — nothing pending review.'}\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: `Review Carts${pendingCarts > 0 ? ` (${pendingCarts})` : ''}`,\n icon: <Store className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/carts'),\n }}\n actions={[\n {\n label: 'Manage Carts',\n icon: <Store className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/carts'),\n },\n {\n label: 'User Management',\n icon: <Users className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/users'),\n },\n {\n label: 'Order Management',\n icon: <ShoppingBag className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/orders'),\n },\n {\n label: 'Financial Analytics',\n icon: <IndianRupee className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/financial'),\n },\n {\n label: 'Analytics',\n icon: <BarChart3 className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/analytics'),\n },\n {\n label: 'Settings',\n icon: <Settings className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/settings'),\n },\n {\n label: 'Reports',\n icon: <FileText className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/analytics'),\n },\n ]}\n />\n </div>\n </EmphasisPanel>\n\n {/* Platform Overview — real dashboard stats */}\n {stats && (\n <div className=\"mb-8\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">Platform Overview</h2>\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={stats.activeCustomers.toLocaleString('en-IN')}\n label=\"Active Customers\"\n />\n <MetricsCard\n icon={<Store className=\"h-5 w-5\" />}\n value={stats.activeProviders.toLocaleString('en-IN')}\n label=\"Active Carts\"\n />\n <MetricsCard\n icon={<ShoppingBag className=\"h-5 w-5\" />}\n value={stats.totalOrders.toLocaleString('en-IN')}\n label=\"Total Orders\"\n />\n <MetricsCard\n featured\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={fmtCur(stats.totalRevenue)}\n label=\"Total Revenue\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4 mt-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Total Carts</p>\n <p className=\"text-xl font-bold text-text-primary\">\n {providers.length.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Pending Cart Approvals</p>\n <p className=\"text-xl font-bold text-status-error-text\">\n {pendingCarts.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Pending Orders</p>\n <p className=\"text-xl font-bold text-status-warning-text\">\n {stats.pendingOrders.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Average Rating</p>\n <p className=\"text-xl font-bold text-text-primary flex items-center gap-1\">\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n {stats.avgRating > 0 ? stats.avgRating.toFixed(1) : '—'}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Recent Activity — real recent orders */}\n <div className=\"mb-8\">\n <div className=\"flex items-center justify-between mb-4\">\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 hover:text-text-primary transition-colors\"\n >\n View all orders\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentOrders.length > 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface divide-y divide-border\">\n {recentOrders.map((order) => (\n <div key={order.id} className=\"flex items-start gap-3 px-4 py-3\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken mt-0.5 text-text-secondary\">\n <ShoppingBag className=\"h-4 w-4\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2\">\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 items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize w-fit',\n orderStatusBadge(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center mt-1 text-xs text-text-secondary gap-3\">\n <span>{formatRelativeTime(order.orderDate)}</span>\n <span className=\"flex items-center gap-1\">\n <IndianRupee className=\"h-3 w-3\" />\n {order.total.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 {/* Revenue by Category & Top Carts — real admin analytics */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8\">\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\">Revenue by Category</h3>\n </div>\n {analytics && analytics.revenueByCategory.length > 0 ? (\n <div className=\"space-y-3\">\n {analytics.revenueByCategory.map((row) => (\n <div\n key={row.category}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\n <div>\n <p className=\"text-sm font-medium text-text-primary capitalize\">\n {row.category}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {row.orders.toLocaleString('en-IN')} orders\n </p>\n </div>\n <p className=\"text-sm font-semibold text-text-primary\">{fmtCur(row.revenue)}</p>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n Category revenue will appear here once orders are recorded.\n </p>\n )}\n </div>\n\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 <Award className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Top Carts</h3>\n </div>\n {analytics && analytics.topProviders.length > 0 ? (\n <div className=\"space-y-3\">\n {analytics.topProviders.map((cart) => (\n <div\n key={cart.id}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{cart.name}</p>\n <p className=\"text-xs text-text-secondary flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n {cart.rating.toFixed(1)} · {cart.orders.toLocaleString('en-IN')} orders\n </p>\n </div>\n <p className=\"text-sm font-semibold text-text-primary\">{fmtCur(cart.revenue)}</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 </div>\n\n {/* Management Links */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {[\n {\n icon: Store,\n label: 'Cart Management',\n desc: 'Approve, manage, 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 Analytics',\n desc: 'View revenue, investments, and financial metrics',\n path: '/admin/financial',\n },\n {\n icon: Activity,\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 sm:p-6 text-left shadow-sm transition-all',\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=\"font-semibold text-text-primary text-sm sm:text-base\">\n {link.label}\n </h3>\n <p className=\"text-xs sm:text-sm text-text-secondary\">{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 * activity row and breakdown is derived from real hook data\n * (useDashboardStats + useProviders + useOrders + useAdminAnalytics).\n * Where a metric has no honest backend source it is omitted rather than faked.\n */\n\nimport { useMemo } from 'react';\nimport {\n Users,\n Store,\n ShoppingBag,\n IndianRupee,\n Activity,\n ArrowRight,\n Star,\n Settings,\n BarChart3,\n Award,\n FileText,\n AlertTriangle,\n Loader2,\n} from 'lucide-react';\nimport { CTAOverflowMenu, EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { MetricsCard } from '../../components/MetricsCard';\nimport { EmptyState } from '../../components/EmptyState';\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/* 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 // Carts awaiting activation/verification — real derivation from provider status.\n const pendingCarts = useMemo(() => providers.filter((p) => !p.isOpen).length, [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 // Admin analytics — only fields the backend actually returns.\n const prettyCategory = (c: string) => c.replace(/_/g, ' ').toLowerCase();\n const topProviders = analytics?.topProviders ?? [];\n const categoryRows = Object.entries(analytics?.categoryDistribution ?? {}).sort(\n (a, b) => b[1] - a[1]\n );\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=\"Admin Dashboard\" description=\"Loading...\">\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 admin dashboard...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"Admin Dashboard\"\n description={`Welcome, ${firstName}! Monitor and manage the platform.`}\n actions={\n pendingCarts > 0 ? (\n <div className=\"flex items-center gap-2 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-3 py-2\">\n <AlertTriangle className=\"h-4 w-4 text-status-error-text\" />\n <span className=\"text-sm font-medium text-status-error-text\">\n {pendingCarts} carts awaiting review\n </span>\n </div>\n ) : undefined\n }\n >\n <PagePurpose className=\"mb-6\">\n The control room for the whole Kadaikodi marketplace — approve new carts, keep an eye on\n users, orders and platform revenue, and act on anything that needs attention. Clear pending\n approvals first, then review platform health below.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-admin-dashboard\"\n className=\"mb-6\"\n steps={[\n {\n id: 'approvals',\n label: 'Review pending cart approvals',\n description: 'Vet and approve new carts waiting to go live.',\n onClick: () => navigateTo('/admin/carts'),\n done: pendingCarts === 0,\n },\n {\n id: 'users',\n label: 'Manage users',\n description: 'Review accounts, roles and access across the platform.',\n onClick: () => navigateTo('/admin/users'),\n },\n {\n id: 'financial',\n label: 'Check financial health',\n description: 'Track revenue, commission and payouts.',\n onClick: () => navigateTo('/admin/financial'),\n },\n ]}\n />\n\n {/* Hero — the one emphasis zone: pending approvals + primary action + overflow */}\n <EmphasisPanel className=\"mb-8\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Quick Actions</h2>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {pendingCarts > 0\n ? `${pendingCarts} carts awaiting your review.`\n : 'All caught up — nothing pending review.'}\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: `Review Carts${pendingCarts > 0 ? ` (${pendingCarts})` : ''}`,\n icon: <Store className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/carts'),\n }}\n actions={[\n {\n label: 'Manage Carts',\n icon: <Store className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/carts'),\n },\n {\n label: 'User Management',\n icon: <Users className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/users'),\n },\n {\n label: 'Order Management',\n icon: <ShoppingBag className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/orders'),\n },\n {\n label: 'Financial Analytics',\n icon: <IndianRupee className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/financial'),\n },\n {\n label: 'Analytics',\n icon: <BarChart3 className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/analytics'),\n },\n {\n label: 'Settings',\n icon: <Settings className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/settings'),\n },\n {\n label: 'Reports',\n icon: <FileText className=\"h-4 w-4\" />,\n onSelect: () => navigateTo('/admin/analytics'),\n },\n ]}\n />\n </div>\n </EmphasisPanel>\n\n {/* Platform Overview — real dashboard stats */}\n {stats && (\n <div className=\"mb-8\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">Platform Overview</h2>\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={(stats.activeCustomers ?? 0).toLocaleString('en-IN')}\n label=\"Active Customers\"\n />\n <MetricsCard\n icon={<Store className=\"h-5 w-5\" />}\n value={(stats.activeProviders ?? 0).toLocaleString('en-IN')}\n label=\"Active Carts\"\n />\n <MetricsCard\n icon={<ShoppingBag className=\"h-5 w-5\" />}\n value={(stats.totalOrders ?? 0).toLocaleString('en-IN')}\n label=\"Total Orders\"\n />\n <MetricsCard\n featured\n icon={<IndianRupee className=\"h-5 w-5\" />}\n value={fmtCur(stats.totalRevenue)}\n label=\"Total Revenue\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4 mt-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Total Carts</p>\n <p className=\"text-xl font-bold text-text-primary\">\n {providers.length.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Pending Cart Approvals</p>\n <p className=\"text-xl font-bold text-status-error-text\">\n {pendingCarts.toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Pending Orders</p>\n <p className=\"text-xl font-bold text-status-warning-text\">\n {(stats.pendingOrders ?? 0).toLocaleString('en-IN')}\n </p>\n </div>\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <p className=\"text-xs text-text-secondary\">Average Rating</p>\n <p className=\"text-xl font-bold text-text-primary flex items-center gap-1\">\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n {stats.avgRating > 0 ? stats.avgRating.toFixed(1) : '—'}\n </p>\n </div>\n </div>\n </div>\n )}\n\n {/* Recent Activity — real recent orders */}\n <div className=\"mb-8\">\n <div className=\"flex items-center justify-between mb-4\">\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 hover:text-text-primary transition-colors\"\n >\n View all orders\n <ArrowRight className=\"h-4 w-4\" />\n </button>\n </div>\n {recentOrders.length > 0 ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface divide-y divide-border\">\n {recentOrders.map((order) => (\n <div key={order.id} className=\"flex items-start gap-3 px-4 py-3\">\n <div className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-bg-sunken mt-0.5 text-text-secondary\">\n <ShoppingBag className=\"h-4 w-4\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex flex-col sm:flex-row sm:items-center justify-between gap-2\">\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 items-center rounded-full px-2 py-0.5 text-xs font-medium capitalize w-fit',\n orderStatusBadge(order.status)\n )}\n >\n {order.status.replace(/_/g, ' ')}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center mt-1 text-xs text-text-secondary gap-3\">\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 {/* Carts by Category & Top Carts — real admin analytics */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8\">\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\">Carts by Category</h3>\n </div>\n {categoryRows.length > 0 ? (\n <div className=\"space-y-3\">\n {categoryRows.map(([category, count]) => (\n <div\n key={category}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\n <p className=\"text-sm font-medium text-text-primary capitalize\">\n {prettyCategory(category)}\n </p>\n <p className=\"text-sm font-semibold text-text-primary\">\n {(count ?? 0).toLocaleString('en-IN')}{' '}\n <span className=\"text-xs font-normal text-text-secondary\">\n {count === 1 ? 'cart' : 'carts'}\n </span>\n </p>\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\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 <Award className=\"h-5 w-5 text-text-secondary\" />\n <h3 className=\"text-lg font-semibold text-text-primary\">Top Carts</h3>\n </div>\n {topProviders.length > 0 ? (\n <div className=\"space-y-3\">\n {topProviders.map((cart) => (\n <div\n key={cart.id}\n className=\"flex items-center justify-between rounded-lg bg-bg-sunken/50 p-3\"\n >\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 <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 </div>\n\n {/* Management Links */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {[\n {\n icon: Store,\n label: 'Cart Management',\n desc: 'Approve, manage, 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 Analytics',\n desc: 'View revenue, investments, and financial metrics',\n path: '/admin/financial',\n },\n {\n icon: Activity,\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 sm:p-6 text-left shadow-sm transition-all',\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=\"font-semibold text-text-primary text-sm sm:text-base\">\n {link.label}\n </h3>\n <p className=\"text-xs sm:text-sm text-text-secondary\">{link.desc}</p>\n </div>\n </button>\n );\n })}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|