@burdenoff/microfe-kadaikodi 2026.704.2 → 2026.704.3
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/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/UserManagementPage.js +5 -5
- package/dist/kadaikodi/pages/admin/UserManagementPage.js.map +1 -1
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js +2 -2
- package/dist/kadaikodi/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/kadaikodi/pages/customer/CustomerDashboard.js +1 -1
- package/dist/kadaikodi/pages/customer/CustomerDashboard.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserManagementPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/UserManagementPage.tsx"],"sourcesContent":["/**\n * User Management Page\n * @module kadaikodi/pages/admin\n *\n * Full-featured user management with 8 stats cards, 5 filter controls,\n * dual layout (mobile cards + desktop table), user action modal\n * (edit form + delete confirmation), pagination, and loading state.\n */\n\nimport { useState, useMemo } from 'react';\nimport {\n Search,\n Users,\n UserCheck,\n UserX,\n Shield,\n Eye,\n Edit3,\n Trash2,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronLeft,\n ChevronRight,\n Smartphone,\n Mail,\n Phone,\n X,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useProviders } from '../../hooks/useKadaikodiApi';\nimport type { MockCart } from '../../types';\nimport { cn } from '../../utils/cn';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ninterface ExtendedUser {\n id: string;\n email: string;\n phone: string;\n firstName: string;\n lastName: string;\n role: 'customer' | 'cart_owner' | 'worker' | 'investor' | 'admin';\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n profilePicture: string;\n status: 'active' | 'inactive' | 'suspended' | 'pending';\n lastLoginTime?: string;\n totalOrders?: number;\n totalSpent?: number;\n totalEarnings?: number;\n kycStatus?: 'pending' | 'verified' | 'rejected';\n riskScore?: number;\n registrationDate: string;\n deviceInfo?: string;\n}\n\ninterface UserFilters {\n role: string;\n status: string;\n searchQuery: string;\n sortBy: string;\n registrationDate: string;\n}\n\ninterface UserStats {\n totalUsers: number;\n activeUsers: number;\n newUsers: number;\n verifiedUsers: number;\n customers: number;\n cartOwners: number;\n workers: number;\n investors: number;\n}\n\n/* ------------------------------------------------------------------ */\n/* Real data mapping — cart owners are real users (service providers). */\n/* No users endpoint exists, so we never fabricate accounts. */\n/* ------------------------------------------------------------------ */\n\nconst providerToUser = (p: MockCart): ExtendedUser => ({\n id: p.ownerId ?? p.id,\n email: '',\n phone: '',\n firstName: p.name,\n lastName: '',\n role: 'cart_owner',\n isEmailVerified: Boolean(p.isVerified),\n isPhoneVerified: false,\n profilePicture: '',\n status: p.isOpen ? 'active' : 'inactive',\n registrationDate: p.createdAt ?? new Date(0).toISOString(),\n});\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function UserManagementPage() {\n const { navigateTo } = useKadaikodi();\n\n const { data: providers = [], isLoading: loading } = useProviders();\n const [selectedUser, setSelectedUser] = useState<ExtendedUser | null>(null);\n const [showUserModal, setShowUserModal] = useState(false);\n const [actionType, setActionType] = useState<'edit' | 'delete'>('edit');\n const [editForm, setEditForm] = useState({\n firstName: '',\n lastName: '',\n email: '',\n phone: '',\n role: '',\n });\n const [currentPage, setCurrentPage] = useState(1);\n const itemsPerPage = 10;\n\n const [filters, setFilters] = useState<UserFilters>({\n role: '',\n status: '',\n searchQuery: '',\n sortBy: 'recent',\n registrationDate: '',\n });\n\n // Real user base: cart owners derived from the live service-provider list.\n const allUsers = useMemo<ExtendedUser[]>(() => providers.map(providerToUser), [providers]);\n\n const users = useMemo<ExtendedUser[]>(() => {\n let filtered = [...allUsers];\n\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n filtered = filtered.filter(\n (u) =>\n u.firstName.toLowerCase().includes(q) ||\n u.lastName.toLowerCase().includes(q) ||\n u.email.toLowerCase().includes(q) ||\n u.phone.includes(q)\n );\n }\n if (filters.role) filtered = filtered.filter((u) => u.role === filters.role);\n if (filters.status) filtered = filtered.filter((u) => u.status === filters.status);\n if (filters.registrationDate) {\n const days = parseInt(filters.registrationDate);\n const cutoff = new Date(Date.now() - days * 86400000);\n filtered = filtered.filter((u) => new Date(u.registrationDate) >= cutoff);\n }\n\n switch (filters.sortBy) {\n case 'name':\n filtered.sort((a, b) => a.firstName.localeCompare(b.firstName));\n break;\n case 'activity':\n filtered.sort((a, b) => {\n const aT = a.lastLoginTime ? new Date(a.lastLoginTime).getTime() : 0;\n const bT = b.lastLoginTime ? new Date(b.lastLoginTime).getTime() : 0;\n return bT - aT;\n });\n break;\n default:\n filtered.sort(\n (a, b) => new Date(b.registrationDate).getTime() - new Date(a.registrationDate).getTime()\n );\n }\n\n return filtered;\n }, [allUsers, filters]);\n\n const stats = useMemo<UserStats>(() => {\n const thirtyDaysAgo = Date.now() - 30 * 86400000;\n return {\n totalUsers: allUsers.length,\n activeUsers: allUsers.filter((u) => u.status === 'active').length,\n newUsers: allUsers.filter((u) => new Date(u.registrationDate).getTime() >= thirtyDaysAgo)\n .length,\n verifiedUsers: allUsers.filter((u) => u.isEmailVerified).length,\n customers: allUsers.filter((u) => u.role === 'customer').length,\n cartOwners: allUsers.filter((u) => u.role === 'cart_owner').length,\n workers: allUsers.filter((u) => u.role === 'worker').length,\n investors: allUsers.filter((u) => u.role === 'investor').length,\n };\n }, [allUsers]);\n\n const handleFilterChange = (key: keyof UserFilters, value: string) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n setCurrentPage(1);\n };\n\n const handleUserAction = (\n user: ExtendedUser,\n action: 'edit' | 'suspend' | 'activate' | 'delete' | 'verify'\n ) => {\n if (action === 'suspend' || action === 'activate' || action === 'verify') {\n const msg =\n action === 'suspend'\n ? `User ${user.firstName} ${user.lastName} suspended`\n : action === 'activate'\n ? `User ${user.firstName} ${user.lastName} activated`\n : `User ${user.firstName} ${user.lastName} verified`;\n alert(msg);\n return;\n }\n setSelectedUser(user);\n setActionType(action);\n setEditForm({\n firstName: user.firstName,\n lastName: user.lastName,\n email: user.email,\n phone: user.phone,\n role: user.role,\n });\n setShowUserModal(true);\n };\n\n const handleDeleteConfirm = () => {\n if (selectedUser) {\n alert(`User ${selectedUser.firstName} ${selectedUser.lastName} deleted`);\n setShowUserModal(false);\n }\n };\n\n const handleSaveEdit = () => {\n alert('User updated successfully');\n setShowUserModal(false);\n };\n\n const formatDate = (d: string) =>\n new Date(d).toLocaleDateString('en-IN', { year: 'numeric', month: 'short', day: 'numeric' });\n\n const formatCurrency = (amount: number) => `₹${amount.toLocaleString('en-IN')}`;\n\n const pagedUsers = useMemo(() => {\n const start = (currentPage - 1) * itemsPerPage;\n return users.slice(start, start + itemsPerPage);\n }, [users, currentPage]);\n\n const roleBadge = (role: string) => {\n const c: Record<string, string> = {\n customer: 'bg-[var(--color-accent-soft)] text-text-primary',\n cart_owner: 'bg-status-success-bg-subtle text-status-success-text',\n worker: 'bg-status-warning-bg-subtle text-status-warning-text',\n investor: 'bg-accent-purple-subtle text-text-primary',\n admin: 'bg-status-error-bg-subtle text-status-error-text',\n };\n const l: Record<string, string> = {\n customer: 'Customer',\n cart_owner: 'Cart Owner',\n worker: 'Worker',\n investor: 'Investor',\n admin: 'Admin',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n c[role]\n )}\n >\n {l[role] ?? role}\n </span>\n );\n };\n\n const statusBadge = (status: string) => {\n const c: Record<string, string> = {\n active: 'bg-status-success-bg-subtle text-status-success-text',\n inactive: 'bg-bg-sunken text-text-secondary',\n suspended: 'bg-status-error-bg-subtle text-status-error-text',\n pending: 'bg-status-warning-bg-subtle text-status-warning-text',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n c[status]\n )}\n >\n {status}\n </span>\n );\n };\n\n /* ---- Loading ---- */\n if (loading) {\n return (\n <PageLayout title=\"User Management\" description=\"Loading users...\">\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 users...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"User Management\"\n description={`${stats.totalUsers.toLocaleString('en-IN')} registered users on the platform`}\n >\n <PagePurpose className=\"mb-6\">\n The directory of everyone on Kadaikodi — customers, cart owners, workers and investors. Use\n this page to find an account, verify or activate users, edit roles, and step in when someone\n needs help or needs to be removed. Filter by role or status to manage one group at a time.\n </PagePurpose>\n\n {/* 8 Stats Cards — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">User Base Overview</h2>\n <div className=\"grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3\">\n {[\n { label: 'Total Users', value: stats.totalUsers.toLocaleString('en-IN'), cls: '' },\n {\n label: 'Active',\n value: stats.activeUsers.toLocaleString('en-IN'),\n cls: 'bg-status-success-bg-subtle border-status-success-text/30',\n },\n {\n label: 'New (30d)',\n value: stats.newUsers.toString(),\n cls: 'bg-[var(--color-accent-soft)] border-border-focus/30',\n },\n {\n label: 'Verified',\n value: stats.verifiedUsers.toLocaleString('en-IN'),\n cls: 'bg-accent-purple-subtle border-accent-purple/30',\n },\n { label: 'Customers', value: stats.customers.toLocaleString('en-IN'), cls: '' },\n { label: 'Cart Owners', value: stats.cartOwners.toString(), cls: '' },\n { label: 'Workers', value: stats.workers.toString(), cls: '' },\n { label: 'Investors', value: stats.investors.toString(), cls: '' },\n ].map((s) => (\n <div\n key={s.label}\n className={cn('rounded-lg border border-border-subtle bg-bg-surface p-3', s.cls)}\n >\n <p className=\"text-xs text-text-secondary\">{s.label}</p>\n <p className=\"text-lg font-bold text-text-primary\">{s.value}</p>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* 5 Filter Controls */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 mb-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-3\">\n <div className=\"relative\">\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 users...\"\n value={filters.searchQuery}\n onChange={(e) => handleFilterChange('searchQuery', 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={filters.role}\n onChange={(e) => handleFilterChange('role', 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 Roles</option>\n <option value=\"customer\">Customers</option>\n <option value=\"cart_owner\">Cart Owners</option>\n <option value=\"worker\">Workers</option>\n <option value=\"investor\">Investors</option>\n </select>\n <select\n value={filters.status}\n onChange={(e) => handleFilterChange('status', 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 Status</option>\n <option value=\"active\">Active</option>\n <option value=\"inactive\">Inactive</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n </select>\n <select\n value={filters.registrationDate}\n onChange={(e) => handleFilterChange('registrationDate', 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 Time</option>\n <option value=\"1\">Last 24 hours</option>\n <option value=\"7\">Last 7 days</option>\n <option value=\"30\">Last 30 days</option>\n <option value=\"90\">Last 90 days</option>\n </select>\n <select\n value={filters.sortBy}\n onChange={(e) => handleFilterChange('sortBy', 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=\"recent\">Most Recent</option>\n <option value=\"name\">Name A-Z</option>\n <option value=\"activity\">Last Activity</option>\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 {pagedUsers.map((user) => (\n <div\n key={user.id}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\"\n >\n <div className=\"flex items-center mb-3\">\n {user.profilePicture ? (\n <img\n src={user.profilePicture}\n alt={`${user.firstName} ${user.lastName}`}\n className=\"w-12 h-12 rounded-full object-cover mr-3\"\n />\n ) : (\n <div className=\"w-12 h-12 rounded-full bg-bg-sunken flex items-center justify-center mr-3\">\n <Users className=\"h-6 w-6 text-text-secondary\" />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-medium text-text-primary\">\n {user.firstName} {user.lastName}\n </h3>\n <p className=\"text-sm text-text-secondary truncate\">{user.email}</p>\n <p className=\"text-sm text-text-secondary\">{user.phone}</p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2 mb-3\">\n {roleBadge(user.role)}\n {statusBadge(user.status)}\n </div>\n <div className=\"grid grid-cols-2 gap-4 text-sm mb-3\">\n <div>\n <p className=\"text-text-secondary\">Registered</p>\n <p className=\"text-text-primary\">{formatDate(user.registrationDate)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last Login</p>\n <p className=\"text-text-primary\">\n {user.lastLoginTime ? formatDate(user.lastLoginTime) : 'Never'}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-3 mb-3\">\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.isEmailVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Email</span>\n </div>\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.isPhoneVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Phone</span>\n </div>\n {user.kycStatus && (\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.kycStatus === 'verified'\n ? 'bg-status-success-text'\n : user.kycStatus === 'pending'\n ? 'bg-status-warning-text'\n : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">KYC</span>\n </div>\n )}\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'edit')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Edit\n </button>\n {user.status === 'active' ? (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'suspend')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n Suspend\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'activate')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n Activate\n </button>\n )}\n {user.kycStatus === 'pending' && (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'verify')}\n className=\"rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Verify\n </button>\n )}\n </div>\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 User\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Role & Status\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Activity\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Performance\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Verification\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 {pagedUsers.map((user) => (\n <tr key={user.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n {user.profilePicture ? (\n <img\n src={user.profilePicture}\n alt={`${user.firstName}`}\n className=\"w-10 h-10 rounded-full object-cover\"\n />\n ) : (\n <div className=\"w-10 h-10 rounded-full bg-bg-sunken flex items-center justify-center\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n </div>\n )}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {user.firstName} {user.lastName}\n </p>\n <p className=\"text-xs text-text-secondary\">{user.email}</p>\n <p className=\"text-xs text-text-secondary\">{user.phone}</p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1\">\n {roleBadge(user.role)}\n <br />\n {statusBadge(user.status)}\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm\">\n <p className=\"text-text-primary\">\n Registered: {formatDate(user.registrationDate)}\n </p>\n {user.lastLoginTime && (\n <p className=\"text-text-secondary\">\n Last login: {formatDate(user.lastLoginTime)}\n </p>\n )}\n {user.deviceInfo && (\n <p className=\"text-text-secondary flex items-center gap-1\">\n <Smartphone className=\"h-3 w-3\" />\n {user.deviceInfo}\n </p>\n )}\n </td>\n <td className=\"px-4 py-3 text-sm\">\n {user.role === 'customer' && user.totalOrders != null && (\n <>\n <p className=\"text-text-primary\">{user.totalOrders} orders</p>\n <p className=\"text-text-secondary\">\n {formatCurrency(user.totalSpent ?? 0)} spent\n </p>\n </>\n )}\n {(user.role === 'cart_owner' || user.role === 'worker') &&\n user.totalEarnings != null && (\n <p className=\"text-text-primary\">\n {formatCurrency(user.totalEarnings)} earned\n </p>\n )}\n {user.role === 'investor' && user.riskScore != null && (\n <p className=\"text-text-primary\">Risk: {user.riskScore}/5</p>\n )}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.isEmailVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Email</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.isPhoneVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Phone</span>\n </div>\n {user.kycStatus && (\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.kycStatus === 'verified'\n ? 'bg-status-success-text'\n : user.kycStatus === 'pending'\n ? 'bg-status-warning-text'\n : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">KYC</span>\n </div>\n )}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'edit')}\n title=\"Edit\"\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <Edit3 className=\"h-4 w-4\" />\n </button>\n {user.status === 'active' ? (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'suspend')}\n title=\"Suspend\"\n className=\"rounded p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n <UserX className=\"h-4 w-4\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'activate')}\n title=\"Activate\"\n className=\"rounded p-1.5 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n <UserCheck className=\"h-4 w-4\" />\n </button>\n )}\n {user.kycStatus === 'pending' && (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'verify')}\n title=\"Verify\"\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-[var(--color-accent-soft)] transition-colors\"\n >\n <Shield className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {users.length === 0 && (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No users found\"\n description=\"No accounts match your current filters. Try clearing them or searching for a different name or email.\"\n action={\n <button\n type=\"button\"\n onClick={() =>\n setFilters({\n role: '',\n status: '',\n searchQuery: '',\n sortBy: 'recent',\n registrationDate: '',\n })\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 }\n />\n )}\n </div>\n\n {/* Pagination */}\n <div className=\"mt-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <p className=\"text-sm text-text-secondary text-center sm:text-left\">\n Showing{' '}\n <span className=\"font-medium\">\n {Math.min((currentPage - 1) * itemsPerPage + 1, users.length)}\n </span>{' '}\n to{' '}\n <span className=\"font-medium\">{Math.min(currentPage * itemsPerPage, users.length)}</span>{' '}\n of <span className=\"font-medium\">{users.length}</span> results\n </p>\n <div className=\"flex justify-center sm:justify-end gap-2\">\n <button\n type=\"button\"\n disabled={currentPage === 1}\n onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n disabled={currentPage * itemsPerPage >= users.length}\n onClick={() => setCurrentPage((p) => p + 1)}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n\n {/* User Action Modal */}\n {showUserModal && selectedUser && (\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-md 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 <h3 className=\"text-lg font-semibold text-text-primary\">\n {actionType === 'edit' ? 'Edit User' : 'Delete User'}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowUserModal(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\">\n {actionType === 'delete' ? (\n <div>\n <p className=\"text-text-secondary mb-4\">\n Are you sure you want to delete user "{selectedUser.firstName}{' '}\n {selectedUser.lastName}"? This action cannot be undone.\n </p>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowUserModal(false)}\n className=\"flex-1 rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDeleteConfirm}\n className=\"flex-1 rounded-lg bg-status-error-text hover:bg-status-error-text py-2 text-sm font-medium text-white transition-colors\"\n >\n Delete User\n </button>\n </div>\n </div>\n ) : (\n <div>\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n First Name\n </label>\n <input\n type=\"text\"\n value={editForm.firstName}\n onChange={(e) =>\n setEditForm((f) => ({ ...f, firstName: e.target.value }))\n }\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Last Name\n </label>\n <input\n type=\"text\"\n value={editForm.lastName}\n onChange={(e) => setEditForm((f) => ({ ...f, lastName: e.target.value }))}\n className=\"w-full 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 </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Email\n </label>\n <input\n type=\"email\"\n value={editForm.email}\n onChange={(e) => setEditForm((f) => ({ ...f, email: e.target.value }))}\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Phone\n </label>\n <input\n type=\"tel\"\n value={editForm.phone}\n onChange={(e) => setEditForm((f) => ({ ...f, phone: e.target.value }))}\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Role\n </label>\n <select\n value={editForm.role}\n onChange={(e) => setEditForm((f) => ({ ...f, role: e.target.value }))}\n className=\"w-full 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=\"customer\">Customer</option>\n <option value=\"cart_owner\">Cart Owner</option>\n <option value=\"worker\">Worker</option>\n <option value=\"investor\">Investor</option>\n </select>\n </div>\n </div>\n <div className=\"flex gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowUserModal(false)}\n className=\"flex-1 rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSaveEdit}\n className=\"flex-1 rounded-lg bg-action-primary-bg py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Save Changes\n </button>\n <button\n type=\"button\"\n onClick={() => setActionType('delete')}\n className=\"rounded-lg border border-border-subtle py-2 px-3 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"UserManagementPage.js","names":[],"sources":["../../../../src/kadaikodi/pages/admin/UserManagementPage.tsx"],"sourcesContent":["/**\n * User Management Page\n * @module kadaikodi/pages/admin\n *\n * Full-featured user management with 8 stats cards, 5 filter controls,\n * dual layout (mobile cards + desktop table), user action modal\n * (edit form + delete confirmation), pagination, and loading state.\n */\n\nimport { useState, useMemo } from 'react';\nimport {\n Search,\n Users,\n UserCheck,\n UserX,\n Shield,\n Eye,\n Edit3,\n Trash2,\n CheckCircle2,\n XCircle,\n Loader2,\n ChevronLeft,\n ChevronRight,\n Smartphone,\n Mail,\n Phone,\n X,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useProviders } from '../../hooks/useKadaikodiApi';\nimport type { MockCart } from '../../types';\nimport { cn } from '../../utils/cn';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\ninterface ExtendedUser {\n id: string;\n email: string;\n phone: string;\n firstName: string;\n lastName: string;\n role: 'customer' | 'cart_owner' | 'worker' | 'investor' | 'admin';\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n profilePicture: string;\n status: 'active' | 'inactive' | 'suspended' | 'pending';\n lastLoginTime?: string;\n totalOrders?: number;\n totalSpent?: number;\n totalEarnings?: number;\n kycStatus?: 'pending' | 'verified' | 'rejected';\n riskScore?: number;\n registrationDate: string;\n deviceInfo?: string;\n}\n\ninterface UserFilters {\n role: string;\n status: string;\n searchQuery: string;\n sortBy: string;\n registrationDate: string;\n}\n\ninterface UserStats {\n totalUsers: number;\n activeUsers: number;\n newUsers: number;\n verifiedUsers: number;\n customers: number;\n cartOwners: number;\n workers: number;\n investors: number;\n}\n\n/* ------------------------------------------------------------------ */\n/* Real data mapping — cart owners are real users (service providers). */\n/* No users endpoint exists, so we never fabricate accounts. */\n/* ------------------------------------------------------------------ */\n\nconst providerToUser = (p: MockCart): ExtendedUser => ({\n id: p.ownerId ?? p.id,\n email: '',\n phone: '',\n firstName: p.name,\n lastName: '',\n role: 'cart_owner',\n isEmailVerified: Boolean(p.isVerified),\n isPhoneVerified: false,\n profilePicture: '',\n status: p.isOpen ? 'active' : 'inactive',\n registrationDate: p.createdAt ?? new Date(0).toISOString(),\n});\n\n/* ------------------------------------------------------------------ */\n/* Component */\n/* ------------------------------------------------------------------ */\n\nexport function UserManagementPage() {\n const { navigateTo } = useKadaikodi();\n\n const { data: providers = [], isLoading: loading } = useProviders();\n const [selectedUser, setSelectedUser] = useState<ExtendedUser | null>(null);\n const [showUserModal, setShowUserModal] = useState(false);\n const [actionType, setActionType] = useState<'edit' | 'delete'>('edit');\n const [editForm, setEditForm] = useState({\n firstName: '',\n lastName: '',\n email: '',\n phone: '',\n role: '',\n });\n const [currentPage, setCurrentPage] = useState(1);\n const itemsPerPage = 10;\n\n const [filters, setFilters] = useState<UserFilters>({\n role: '',\n status: '',\n searchQuery: '',\n sortBy: 'recent',\n registrationDate: '',\n });\n\n // Real user base: cart owners derived from the live service-provider list.\n const allUsers = useMemo<ExtendedUser[]>(() => providers.map(providerToUser), [providers]);\n\n const users = useMemo<ExtendedUser[]>(() => {\n let filtered = [...allUsers];\n\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n filtered = filtered.filter(\n (u) =>\n u.firstName.toLowerCase().includes(q) ||\n u.lastName.toLowerCase().includes(q) ||\n u.email.toLowerCase().includes(q) ||\n u.phone.includes(q)\n );\n }\n if (filters.role) filtered = filtered.filter((u) => u.role === filters.role);\n if (filters.status) filtered = filtered.filter((u) => u.status === filters.status);\n if (filters.registrationDate) {\n const days = parseInt(filters.registrationDate);\n const cutoff = new Date(Date.now() - days * 86400000);\n filtered = filtered.filter((u) => new Date(u.registrationDate) >= cutoff);\n }\n\n switch (filters.sortBy) {\n case 'name':\n filtered.sort((a, b) => a.firstName.localeCompare(b.firstName));\n break;\n case 'activity':\n filtered.sort((a, b) => {\n const aT = a.lastLoginTime ? new Date(a.lastLoginTime).getTime() : 0;\n const bT = b.lastLoginTime ? new Date(b.lastLoginTime).getTime() : 0;\n return bT - aT;\n });\n break;\n default:\n filtered.sort(\n (a, b) => new Date(b.registrationDate).getTime() - new Date(a.registrationDate).getTime()\n );\n }\n\n return filtered;\n }, [allUsers, filters]);\n\n const stats = useMemo<UserStats>(() => {\n const thirtyDaysAgo = Date.now() - 30 * 86400000;\n return {\n totalUsers: allUsers.length,\n activeUsers: allUsers.filter((u) => u.status === 'active').length,\n newUsers: allUsers.filter((u) => new Date(u.registrationDate).getTime() >= thirtyDaysAgo)\n .length,\n verifiedUsers: allUsers.filter((u) => u.isEmailVerified).length,\n customers: allUsers.filter((u) => u.role === 'customer').length,\n cartOwners: allUsers.filter((u) => u.role === 'cart_owner').length,\n workers: allUsers.filter((u) => u.role === 'worker').length,\n investors: allUsers.filter((u) => u.role === 'investor').length,\n };\n }, [allUsers]);\n\n const handleFilterChange = (key: keyof UserFilters, value: string) => {\n setFilters((prev) => ({ ...prev, [key]: value }));\n setCurrentPage(1);\n };\n\n const handleUserAction = (\n user: ExtendedUser,\n action: 'edit' | 'suspend' | 'activate' | 'delete' | 'verify'\n ) => {\n if (action === 'suspend' || action === 'activate' || action === 'verify') {\n const msg =\n action === 'suspend'\n ? `User ${user.firstName} ${user.lastName} suspended`\n : action === 'activate'\n ? `User ${user.firstName} ${user.lastName} activated`\n : `User ${user.firstName} ${user.lastName} verified`;\n alert(msg);\n return;\n }\n setSelectedUser(user);\n setActionType(action);\n setEditForm({\n firstName: user.firstName,\n lastName: user.lastName,\n email: user.email,\n phone: user.phone,\n role: user.role,\n });\n setShowUserModal(true);\n };\n\n const handleDeleteConfirm = () => {\n if (selectedUser) {\n alert(`User ${selectedUser.firstName} ${selectedUser.lastName} deleted`);\n setShowUserModal(false);\n }\n };\n\n const handleSaveEdit = () => {\n alert('User updated successfully');\n setShowUserModal(false);\n };\n\n const formatDate = (d: string) =>\n new Date(d).toLocaleDateString('en-IN', { year: 'numeric', month: 'short', day: 'numeric' });\n\n const formatCurrency = (amount: number) => `₹${amount.toLocaleString('en-IN')}`;\n\n const pagedUsers = useMemo(() => {\n const start = (currentPage - 1) * itemsPerPage;\n return users.slice(start, start + itemsPerPage);\n }, [users, currentPage]);\n\n const roleBadge = (role: string) => {\n const c: Record<string, string> = {\n customer: 'bg-[var(--color-accent-soft)] text-text-primary',\n cart_owner: 'bg-status-success-bg-subtle text-status-success-text',\n worker: 'bg-status-warning-bg-subtle text-status-warning-text',\n investor: 'bg-accent-purple-subtle text-text-primary',\n admin: 'bg-status-error-bg-subtle text-status-error-text',\n };\n const l: Record<string, string> = {\n customer: 'Customer',\n cart_owner: 'Cart Owner',\n worker: 'Worker',\n investor: 'Investor',\n admin: 'Admin',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium',\n c[role]\n )}\n >\n {l[role] ?? role}\n </span>\n );\n };\n\n const statusBadge = (status: string) => {\n const c: Record<string, string> = {\n active: 'bg-status-success-bg-subtle text-status-success-text',\n inactive: 'bg-bg-sunken text-text-secondary',\n suspended: 'bg-status-error-bg-subtle text-status-error-text',\n pending: 'bg-status-warning-bg-subtle text-status-warning-text',\n };\n return (\n <span\n className={cn(\n 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium capitalize',\n c[status]\n )}\n >\n {status}\n </span>\n );\n };\n\n /* ---- Loading ---- */\n if (loading) {\n return (\n <PageLayout title=\"User Management\" description=\"Loading users...\">\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 users...</p>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title=\"User Management\"\n description={`${(stats.totalUsers ?? 0).toLocaleString('en-IN')} registered users on the platform`}\n >\n <PagePurpose className=\"mb-6\">\n The directory of everyone on Kadaikodi — customers, cart owners, workers and investors. Use\n this page to find an account, verify or activate users, edit roles, and step in when someone\n needs help or needs to be removed. Filter by role or status to manage one group at a time.\n </PagePurpose>\n\n {/* 8 Stats Cards — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-6\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">User Base Overview</h2>\n <div className=\"grid grid-cols-2 sm:grid-cols-4 lg:grid-cols-8 gap-3\">\n {[\n { label: 'Total Users', value: (stats.totalUsers ?? 0).toLocaleString('en-IN'), cls: '' },\n {\n label: 'Active',\n value: (stats.activeUsers ?? 0).toLocaleString('en-IN'),\n cls: 'bg-status-success-bg-subtle border-status-success-text/30',\n },\n {\n label: 'New (30d)',\n value: stats.newUsers.toString(),\n cls: 'bg-[var(--color-accent-soft)] border-border-focus/30',\n },\n {\n label: 'Verified',\n value: (stats.verifiedUsers ?? 0).toLocaleString('en-IN'),\n cls: 'bg-accent-purple-subtle border-accent-purple/30',\n },\n { label: 'Customers', value: (stats.customers ?? 0).toLocaleString('en-IN'), cls: '' },\n { label: 'Cart Owners', value: stats.cartOwners.toString(), cls: '' },\n { label: 'Workers', value: stats.workers.toString(), cls: '' },\n { label: 'Investors', value: stats.investors.toString(), cls: '' },\n ].map((s) => (\n <div\n key={s.label}\n className={cn('rounded-lg border border-border-subtle bg-bg-surface p-3', s.cls)}\n >\n <p className=\"text-xs text-text-secondary\">{s.label}</p>\n <p className=\"text-lg font-bold text-text-primary\">{s.value}</p>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* 5 Filter Controls */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 mb-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-3\">\n <div className=\"relative\">\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 users...\"\n value={filters.searchQuery}\n onChange={(e) => handleFilterChange('searchQuery', 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={filters.role}\n onChange={(e) => handleFilterChange('role', 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 Roles</option>\n <option value=\"customer\">Customers</option>\n <option value=\"cart_owner\">Cart Owners</option>\n <option value=\"worker\">Workers</option>\n <option value=\"investor\">Investors</option>\n </select>\n <select\n value={filters.status}\n onChange={(e) => handleFilterChange('status', 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 Status</option>\n <option value=\"active\">Active</option>\n <option value=\"inactive\">Inactive</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n </select>\n <select\n value={filters.registrationDate}\n onChange={(e) => handleFilterChange('registrationDate', 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 Time</option>\n <option value=\"1\">Last 24 hours</option>\n <option value=\"7\">Last 7 days</option>\n <option value=\"30\">Last 30 days</option>\n <option value=\"90\">Last 90 days</option>\n </select>\n <select\n value={filters.sortBy}\n onChange={(e) => handleFilterChange('sortBy', 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=\"recent\">Most Recent</option>\n <option value=\"name\">Name A-Z</option>\n <option value=\"activity\">Last Activity</option>\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 {pagedUsers.map((user) => (\n <div\n key={user.id}\n className=\"rounded-lg border border-border-subtle bg-bg-sunken/30 p-4\"\n >\n <div className=\"flex items-center mb-3\">\n {user.profilePicture ? (\n <img\n src={user.profilePicture}\n alt={`${user.firstName} ${user.lastName}`}\n className=\"w-12 h-12 rounded-full object-cover mr-3\"\n />\n ) : (\n <div className=\"w-12 h-12 rounded-full bg-bg-sunken flex items-center justify-center mr-3\">\n <Users className=\"h-6 w-6 text-text-secondary\" />\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-medium text-text-primary\">\n {user.firstName} {user.lastName}\n </h3>\n <p className=\"text-sm text-text-secondary truncate\">{user.email}</p>\n <p className=\"text-sm text-text-secondary\">{user.phone}</p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2 mb-3\">\n {roleBadge(user.role)}\n {statusBadge(user.status)}\n </div>\n <div className=\"grid grid-cols-2 gap-4 text-sm mb-3\">\n <div>\n <p className=\"text-text-secondary\">Registered</p>\n <p className=\"text-text-primary\">{formatDate(user.registrationDate)}</p>\n </div>\n <div>\n <p className=\"text-text-secondary\">Last Login</p>\n <p className=\"text-text-primary\">\n {user.lastLoginTime ? formatDate(user.lastLoginTime) : 'Never'}\n </p>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-3 mb-3\">\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.isEmailVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Email</span>\n </div>\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.isPhoneVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Phone</span>\n </div>\n {user.kycStatus && (\n <div className=\"flex items-center\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full mr-2',\n user.kycStatus === 'verified'\n ? 'bg-status-success-text'\n : user.kycStatus === 'pending'\n ? 'bg-status-warning-text'\n : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">KYC</span>\n </div>\n )}\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'edit')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Edit\n </button>\n {user.status === 'active' ? (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'suspend')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n Suspend\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'activate')}\n className=\"rounded-lg border border-border-subtle px-3 py-1.5 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n Activate\n </button>\n )}\n {user.kycStatus === 'pending' && (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'verify')}\n className=\"rounded-lg bg-action-primary-bg px-3 py-1.5 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Verify\n </button>\n )}\n </div>\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 User\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Role & Status\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Activity\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Performance\n </th>\n <th className=\"px-4 py-3 text-left text-xs font-medium text-text-secondary uppercase tracking-wider\">\n Verification\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 {pagedUsers.map((user) => (\n <tr key={user.id} className=\"hover:bg-accent/50 transition-colors\">\n <td className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n {user.profilePicture ? (\n <img\n src={user.profilePicture}\n alt={`${user.firstName}`}\n className=\"w-10 h-10 rounded-full object-cover\"\n />\n ) : (\n <div className=\"w-10 h-10 rounded-full bg-bg-sunken flex items-center justify-center\">\n <Users className=\"h-5 w-5 text-text-secondary\" />\n </div>\n )}\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {user.firstName} {user.lastName}\n </p>\n <p className=\"text-xs text-text-secondary\">{user.email}</p>\n <p className=\"text-xs text-text-secondary\">{user.phone}</p>\n </div>\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1\">\n {roleBadge(user.role)}\n <br />\n {statusBadge(user.status)}\n </div>\n </td>\n <td className=\"px-4 py-3 text-sm\">\n <p className=\"text-text-primary\">\n Registered: {formatDate(user.registrationDate)}\n </p>\n {user.lastLoginTime && (\n <p className=\"text-text-secondary\">\n Last login: {formatDate(user.lastLoginTime)}\n </p>\n )}\n {user.deviceInfo && (\n <p className=\"text-text-secondary flex items-center gap-1\">\n <Smartphone className=\"h-3 w-3\" />\n {user.deviceInfo}\n </p>\n )}\n </td>\n <td className=\"px-4 py-3 text-sm\">\n {user.role === 'customer' && user.totalOrders != null && (\n <>\n <p className=\"text-text-primary\">{user.totalOrders} orders</p>\n <p className=\"text-text-secondary\">\n {formatCurrency(user.totalSpent ?? 0)} spent\n </p>\n </>\n )}\n {(user.role === 'cart_owner' || user.role === 'worker') &&\n user.totalEarnings != null && (\n <p className=\"text-text-primary\">\n {formatCurrency(user.totalEarnings)} earned\n </p>\n )}\n {user.role === 'investor' && user.riskScore != null && (\n <p className=\"text-text-primary\">Risk: {user.riskScore}/5</p>\n )}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.isEmailVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Email</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.isPhoneVerified ? 'bg-status-success-text' : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">Phone</span>\n </div>\n {user.kycStatus && (\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\n 'w-2 h-2 rounded-full',\n user.kycStatus === 'verified'\n ? 'bg-status-success-text'\n : user.kycStatus === 'pending'\n ? 'bg-status-warning-text'\n : 'bg-status-error-text'\n )}\n />\n <span className=\"text-sm text-text-secondary\">KYC</span>\n </div>\n )}\n </div>\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex gap-1\">\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'edit')}\n title=\"Edit\"\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-accent transition-colors\"\n >\n <Edit3 className=\"h-4 w-4\" />\n </button>\n {user.status === 'active' ? (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'suspend')}\n title=\"Suspend\"\n className=\"rounded p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n <UserX className=\"h-4 w-4\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'activate')}\n title=\"Activate\"\n className=\"rounded p-1.5 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n <UserCheck className=\"h-4 w-4\" />\n </button>\n )}\n {user.kycStatus === 'pending' && (\n <button\n type=\"button\"\n onClick={() => handleUserAction(user, 'verify')}\n title=\"Verify\"\n className=\"rounded p-1.5 text-text-secondary hover:text-text-primary hover:bg-[var(--color-accent-soft)] transition-colors\"\n >\n <Shield className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {users.length === 0 && (\n <EmptyState\n illustration=\"empty-people\"\n title=\"No users found\"\n description=\"No accounts match your current filters. Try clearing them or searching for a different name or email.\"\n action={\n <button\n type=\"button\"\n onClick={() =>\n setFilters({\n role: '',\n status: '',\n searchQuery: '',\n sortBy: 'recent',\n registrationDate: '',\n })\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 }\n />\n )}\n </div>\n\n {/* Pagination */}\n <div className=\"mt-6 flex flex-col sm:flex-row sm:items-center justify-between gap-4\">\n <p className=\"text-sm text-text-secondary text-center sm:text-left\">\n Showing{' '}\n <span className=\"font-medium\">\n {Math.min((currentPage - 1) * itemsPerPage + 1, users.length)}\n </span>{' '}\n to{' '}\n <span className=\"font-medium\">{Math.min(currentPage * itemsPerPage, users.length)}</span>{' '}\n of <span className=\"font-medium\">{users.length}</span> results\n </p>\n <div className=\"flex justify-center sm:justify-end gap-2\">\n <button\n type=\"button\"\n disabled={currentPage === 1}\n onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n Previous\n </button>\n <button\n type=\"button\"\n disabled={currentPage * itemsPerPage >= users.length}\n onClick={() => setCurrentPage((p) => p + 1)}\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 disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n Next\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n\n {/* User Action Modal */}\n {showUserModal && selectedUser && (\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-md 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 <h3 className=\"text-lg font-semibold text-text-primary\">\n {actionType === 'edit' ? 'Edit User' : 'Delete User'}\n </h3>\n <button\n type=\"button\"\n onClick={() => setShowUserModal(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\">\n {actionType === 'delete' ? (\n <div>\n <p className=\"text-text-secondary mb-4\">\n Are you sure you want to delete user "{selectedUser.firstName}{' '}\n {selectedUser.lastName}"? This action cannot be undone.\n </p>\n <div className=\"flex gap-3\">\n <button\n type=\"button\"\n onClick={() => setShowUserModal(false)}\n className=\"flex-1 rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDeleteConfirm}\n className=\"flex-1 rounded-lg bg-status-error-text hover:bg-status-error-text py-2 text-sm font-medium text-white transition-colors\"\n >\n Delete User\n </button>\n </div>\n </div>\n ) : (\n <div>\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n First Name\n </label>\n <input\n type=\"text\"\n value={editForm.firstName}\n onChange={(e) =>\n setEditForm((f) => ({ ...f, firstName: e.target.value }))\n }\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Last Name\n </label>\n <input\n type=\"text\"\n value={editForm.lastName}\n onChange={(e) => setEditForm((f) => ({ ...f, lastName: e.target.value }))}\n className=\"w-full 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 </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Email\n </label>\n <input\n type=\"email\"\n value={editForm.email}\n onChange={(e) => setEditForm((f) => ({ ...f, email: e.target.value }))}\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Phone\n </label>\n <input\n type=\"tel\"\n value={editForm.phone}\n onChange={(e) => setEditForm((f) => ({ ...f, phone: e.target.value }))}\n className=\"w-full 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 </div>\n <div>\n <label className=\"block text-sm font-medium text-text-secondary mb-1\">\n Role\n </label>\n <select\n value={editForm.role}\n onChange={(e) => setEditForm((f) => ({ ...f, role: e.target.value }))}\n className=\"w-full 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=\"customer\">Customer</option>\n <option value=\"cart_owner\">Cart Owner</option>\n <option value=\"worker\">Worker</option>\n <option value=\"investor\">Investor</option>\n </select>\n </div>\n </div>\n <div className=\"flex gap-3 mt-6\">\n <button\n type=\"button\"\n onClick={() => setShowUserModal(false)}\n className=\"flex-1 rounded-lg border border-border-subtle py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSaveEdit}\n className=\"flex-1 rounded-lg bg-action-primary-bg py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Save Changes\n </button>\n <button\n type=\"button\"\n onClick={() => setActionType('delete')}\n className=\"rounded-lg border border-border-subtle py-2 px-3 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle transition-colors\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -375,7 +375,7 @@ var k = 864e5, A = {
|
|
|
375
375
|
},
|
|
376
376
|
{
|
|
377
377
|
label: "Average Rating",
|
|
378
|
-
value: R.rating.toFixed(1),
|
|
378
|
+
value: (R.rating ?? 0).toFixed(1),
|
|
379
379
|
icon: b,
|
|
380
380
|
color: "text-status-warning-text"
|
|
381
381
|
}
|
|
@@ -573,7 +573,7 @@ var k = 864e5, A = {
|
|
|
573
573
|
className: "flex items-center justify-between mb-1",
|
|
574
574
|
children: [/* @__PURE__ */ T("span", {
|
|
575
575
|
className: "flex items-center gap-0.5 text-status-warning-text text-xs font-medium",
|
|
576
|
-
children: [/* @__PURE__ */ w(b, { className: "h-3 w-3 fill-status-warning-text text-status-warning-text" }), e.rating.toFixed(1)]
|
|
576
|
+
children: [/* @__PURE__ */ w(b, { className: "h-3 w-3 fill-status-warning-text text-status-warning-text" }), (e.rating ?? 0).toFixed(1)]
|
|
577
577
|
}), /* @__PURE__ */ w("span", {
|
|
578
578
|
className: "text-xs text-text-secondary",
|
|
579
579
|
children: new Date(e.createdAt).toLocaleDateString("en-IN", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsDashboard.js","names":[],"sources":["../../../../src/kadaikodi/pages/cart-owner/AnalyticsDashboard.tsx"],"sourcesContent":["/**\n * Analytics Dashboard Page\n *\n * Performance insights for the signed-in owner's own service provider (cart):\n * revenue overview with real period-over-period growth, order statistics,\n * popular times, performance metrics, top/least selling offerings, category\n * performance, reviews & ratings, peak hours/days and daily order trends —\n * every number derived from live backend data (no fabrication).\n *\n * @module kadaikodi/pages/cart-owner\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n IndianRupee,\n ShoppingCart,\n TrendingUp,\n BarChart3,\n Clock,\n Star,\n ArrowUpRight,\n ArrowDownRight,\n Download,\n Target,\n Percent,\n CalendarDays,\n ThumbsUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport {\n useProviders,\n useOrders,\n useOfferings,\n useReviews,\n} from '../../hooks/useKadaikodiApi';\nimport { formatCurrency } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DAY_MS = 86_400_000;\n\nconst CATEGORY_LABELS: Record<string, string> = {\n FOOD_BEVERAGE: 'Food & Beverage',\n PERSONAL_CARE_BEAUTY: 'Personal Care & Beauty',\n CLOTHING_FASHION: 'Clothing & Fashion',\n REPAIR_MAINTENANCE: 'Repair & Maintenance',\n HOME_SERVICES: 'Home Services',\n HEALTH_WELLNESS: 'Health & Wellness',\n EDUCATION_SKILLS: 'Education & Skills',\n RETAIL_MERCHANDISE: 'Retail & Merchandise',\n GROCERY_ESSENTIALS: 'Grocery & Essentials',\n};\n\nconst startOfToday = (): Date => {\n const d = new Date();\n d.setHours(0, 0, 0, 0);\n return d;\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AnalyticsDashboard = () => {\n const { navigateTo, currentUser } = useKadaikodi();\n const [dateRange, setDateRange] = useState('week');\n\n // Resolve the signed-in owner's provider (\"my cart\").\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const myProvider = useMemo(\n () => providers.find((p) => p.ownerId === currentUser?.id) ?? providers[0],\n [providers, currentUser?.id]\n );\n const providerId = myProvider?.id;\n\n const { data: orders = [] } = useOrders({ providerId });\n const { data: offerings = [] } = useOfferings(providerId);\n const { data: reviews = [] } = useReviews(providerId ?? '');\n\n const offeringCategoryByName = useMemo(\n () => new Map(offerings.map((o) => [o.name, CATEGORY_LABELS[o.category] ?? o.category])),\n [offerings]\n );\n\n // ---- Revenue (real, from delivered orders) ----\n const revenue = useMemo(() => {\n const delivered = orders.filter((o) => o.status === 'delivered');\n const t0 = startOfToday().getTime();\n const inRange = (start: number, end: number) =>\n delivered\n .filter((o) => {\n const d = new Date(o.orderDate).getTime();\n return d >= start && d < end;\n })\n .reduce((s, o) => s + o.total, 0);\n\n const today = inRange(t0, t0 + DAY_MS);\n const yesterday = inRange(t0 - DAY_MS, t0);\n const week = inRange(t0 - 6 * DAY_MS, t0 + DAY_MS);\n const prevWeek = inRange(t0 - 13 * DAY_MS, t0 - 6 * DAY_MS);\n const month = inRange(t0 - 29 * DAY_MS, t0 + DAY_MS);\n const prevMonth = inRange(t0 - 59 * DAY_MS, t0 - 29 * DAY_MS);\n const year = inRange(t0 - 364 * DAY_MS, t0 + DAY_MS);\n\n const growth = (cur: number, prev: number): number | null =>\n prev > 0 ? Math.round(((cur - prev) / prev) * 1000) / 10 : null;\n\n return {\n today,\n week,\n month,\n year,\n dailyGrowth: growth(today, yesterday),\n weeklyGrowth: growth(week, prevWeek),\n monthlyGrowth: growth(month, prevMonth),\n };\n }, [orders]);\n\n // ---- Order & performance stats (real) ----\n const orderStats = useMemo(() => {\n const total = orders.length;\n const delivered = orders.filter((o) => o.status === 'delivered');\n const cancelled = orders.filter((o) => o.status === 'cancelled');\n const avgOrderValue =\n delivered.length > 0\n ? Math.round(delivered.reduce((s, o) => s + o.total, 0) / delivered.length)\n : 0;\n return {\n total,\n completed: delivered.length,\n cancelled: cancelled.length,\n avgOrderValue,\n acceptanceRate: total > 0 ? Math.round(((total - cancelled.length) / total) * 100) : 0,\n cancellationRate: total > 0 ? Math.round((cancelled.length / total) * 100) : 0,\n completionRate: total > 0 ? Math.round((delivered.length / total) * 100) : 0,\n };\n }, [orders]);\n\n // ---- Popular times, peak hours & days (real, from order timestamps) ----\n const { popularTimes, peakHours, peakDays } = useMemo(() => {\n const hourCounts = new Array<number>(24).fill(0);\n const dayCounts = new Map<string, number>();\n orders.forEach((o) => {\n const d = new Date(o.orderDate);\n hourCounts[d.getHours()] += 1;\n const day = d.toLocaleDateString('en-US', { weekday: 'long' });\n dayCounts.set(day, (dayCounts.get(day) ?? 0) + 1);\n });\n const times = hourCounts\n .map((count, hour) => ({ hour, orders: count }))\n .filter((h) => h.orders > 0);\n return {\n popularTimes: times,\n peakHours: [...times].sort((a, b) => b.orders - a.orders).slice(0, 4),\n peakDays: [...dayCounts.entries()]\n .map(([day, count]) => ({ day, orders: count }))\n .sort((a, b) => b.orders - a.orders)\n .slice(0, 3),\n };\n }, [orders]);\n\n // ---- Top / least selling & category performance (real, from order items) ----\n const { topSelling, leastSelling, categories } = useMemo(() => {\n const itemAgg = new Map<string, { quantity: number; revenue: number; category: string }>();\n const catAgg = new Map<string, { revenue: number; units: number }>();\n orders.forEach((o) =>\n o.items.forEach((it) => {\n const category = offeringCategoryByName.get(it.name) ?? 'Other';\n const item = itemAgg.get(it.name) ?? { quantity: 0, revenue: 0, category };\n item.quantity += it.quantity;\n item.revenue += it.price * it.quantity;\n itemAgg.set(it.name, item);\n const cat = catAgg.get(category) ?? { revenue: 0, units: 0 };\n cat.revenue += it.price * it.quantity;\n cat.units += it.quantity;\n catAgg.set(category, cat);\n })\n );\n const items = [...itemAgg.entries()].map(([name, v]) => ({ name, ...v }));\n return {\n topSelling: [...items].sort((a, b) => b.revenue - a.revenue).slice(0, 5),\n leastSelling: [...items].sort((a, b) => a.quantity - b.quantity).slice(0, 3),\n categories: [...catAgg.entries()]\n .map(([category, v]) => ({ category, ...v }))\n .sort((a, b) => b.revenue - a.revenue),\n };\n }, [orders, offeringCategoryByName]);\n\n // ---- Reviews & ratings (real) ----\n const reviewSummary = useMemo(() => {\n const count = reviews.length;\n const average = count > 0 ? reviews.reduce((s, r) => s + r.rating, 0) / count : 0;\n const positive = reviews.filter((r) => r.rating >= 4).length;\n const distribution = [5, 4, 3, 2, 1].map((star) => ({\n star,\n count: reviews.filter((r) => r.rating === star).length,\n }));\n const recent = [...reviews]\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())\n .slice(0, 4);\n return { count, average, positive, distribution, recent };\n }, [reviews]);\n\n // ---- Daily order trends (real, last 14 days) ----\n const orderTrends = useMemo(() => {\n const t0 = startOfToday().getTime();\n const days: { date: string; orders: number; revenue: number }[] = [];\n for (let i = 13; i >= 0; i--) {\n const start = t0 - i * DAY_MS;\n const end = start + DAY_MS;\n const dayOrders = orders.filter((o) => {\n const d = new Date(o.orderDate).getTime();\n return d >= start && d < end;\n });\n days.push({\n date: new Date(start).toISOString(),\n orders: dayOrders.length,\n revenue: dayOrders\n .filter((o) => o.status === 'delivered')\n .reduce((s, o) => s + o.total, 0),\n });\n }\n return days;\n }, [orders]);\n\n const maxPopularTime = Math.max(...popularTimes.map((t) => t.orders), 1);\n const maxCategoryRevenue = Math.max(...categories.map((c) => c.revenue), 1);\n const maxPeakHourOrders = Math.max(...peakHours.map((h) => h.orders), 1);\n const maxPeakDayOrders = Math.max(...peakDays.map((d) => d.orders), 1);\n const maxDistribution = Math.max(...reviewSummary.distribution.map((d) => d.count), 1);\n\n const formatPct = (v: number) => `${v > 0 ? '+' : ''}${v}%`;\n\n // ---- Loading / no-provider states ----\n if (providersLoading) {\n return (\n <PageLayout title=\"Analytics Dashboard\" description=\"Business performance insights and data\">\n <div className=\"flex items-center justify-center py-24\">\n <div className=\"h-10 w-10 animate-spin rounded-full border-b-2 border-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!myProvider) {\n return (\n <PageLayout title=\"Analytics Dashboard\" description=\"Business performance insights and data\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No analytics yet\"\n description=\"You don't have a service provider yet. Set up your cart and start taking orders to see your performance insights here.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart-owner/service-config')}\n className=\"inline-flex items-center gap-2 rounded-md bg-accent px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent/80\"\n >\n Set up your cart\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const revenueCards = [\n { label: \"Today's Revenue\", value: revenue.today, growth: revenue.dailyGrowth },\n { label: 'This Week', value: revenue.week, growth: revenue.weeklyGrowth },\n { label: 'This Month', value: revenue.month, growth: revenue.monthlyGrowth },\n { label: 'This Year', value: revenue.year, growth: null as number | null },\n ];\n\n return (\n <PageLayout\n title=\"Analytics Dashboard\"\n description={`Performance insights for ${myProvider.name}`}\n actions={\n <div className=\"flex items-center gap-2\">\n <select\n value={dateRange}\n onChange={(e) => setDateRange(e.target.value)}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"today\">Today</option>\n <option value=\"week\">This Week</option>\n <option value=\"month\">This Month</option>\n <option value=\"year\">This Year</option>\n </select>\n <button\n type=\"button\"\n onClick={() => alert('Export feature coming soon!')}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n The performance story of your cart. Use this page to understand how your business is really\n doing — track revenue over time, see how orders break down, learn which hours are busiest so\n you can staff and stock accordingly, and spot your best-selling items. All figures are drawn\n from your live orders and reviews.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-owner-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: 'inventory',\n label: 'Restock your best-sellers',\n description: 'Make sure popular items never run out.',\n onClick: () => navigateTo('/cart-owner/inventory'),\n },\n {\n id: 'orders',\n label: 'Work through live orders',\n description: 'Keep prep and delivery times sharp.',\n onClick: () => navigateTo('/cart-owner/orders'),\n },\n {\n id: 'staff',\n label: 'Plan staff for peak hours',\n description: 'Cover the busiest times you see here.',\n onClick: () => navigateTo('/cart-owner/staff'),\n },\n ]}\n />\n\n <div className=\"space-y-6\">\n {/* Revenue Overview — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"text-base font-semibold text-text-primary mb-3\">Revenue Overview</h2>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {revenueCards.map((card) => (\n <div\n key={card.label}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\"\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"rounded-md bg-accent p-2\">\n <IndianRupee className=\"h-5 w-5 text-text-secondary\" />\n </div>\n {card.growth !== null && (\n <div\n className={cn(\n 'flex items-center gap-1 text-xs font-medium',\n card.growth >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {card.growth >= 0 ? (\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n ) : (\n <ArrowDownRight className=\"h-3.5 w-3.5\" />\n )}\n {formatPct(card.growth)}\n </div>\n )}\n </div>\n <p className=\"mt-3 text-2xl font-bold text-text-primary\">\n {formatCurrency(card.value, { minimumFractionDigits: 0 })}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">{card.label}</p>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Order Statistics + Popular Times */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Order Statistics */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <ShoppingCart className=\"h-4 w-4 text-text-secondary\" />\n Order Statistics\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-text-secondary\">Total Orders</p>\n <p className=\"text-2xl font-bold text-text-primary\">{orderStats.total}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Avg. Order Value</p>\n <p className=\"text-2xl font-bold text-text-primary\">\n {formatCurrency(orderStats.avgOrderValue, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Completed</p>\n <p className=\"text-xl font-bold text-status-success-text\">\n {orderStats.completed}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Cancelled</p>\n <p className=\"text-xl font-bold text-status-error-text\">{orderStats.cancelled}</p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Popular Times */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n Popular Times\n </h2>\n </div>\n <div className=\"p-5 space-y-2\">\n {popularTimes.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No order activity yet. Busy hours will appear here once orders come in.\n </p>\n ) : (\n popularTimes.map((t) => {\n const pct = (t.orders / maxPopularTime) * 100;\n return (\n <div key={t.hour}>\n <div className=\"flex items-center justify-between text-xs mb-0.5\">\n <span className=\"text-text-primary w-14\">{`${t.hour}:00`}</span>\n <span className=\"text-text-secondary\">{t.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n {/* Performance Metrics */}\n <section>\n <h2 className=\"text-base font-semibold text-text-primary mb-3\">Performance Metrics</h2>\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n label: 'Acceptance Rate',\n value: `${orderStats.acceptanceRate}%`,\n icon: Target,\n color: 'text-status-success-text',\n },\n {\n label: 'Cancellation Rate',\n value: `${orderStats.cancellationRate}%`,\n icon: Percent,\n color: 'text-status-error-text',\n },\n {\n label: 'Completion Rate',\n value: `${orderStats.completionRate}%`,\n icon: TrendingUp,\n color: 'text-text-primary',\n },\n {\n label: 'Average Rating',\n value: myProvider.rating.toFixed(1),\n icon: Star,\n color: 'text-status-warning-text',\n },\n ].map((metric) => {\n const Icon = metric.icon;\n return (\n <div\n key={metric.label}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\"\n >\n <div className=\"flex items-center gap-2 mb-2\">\n <Icon className={cn('h-5 w-5', metric.color)} />\n <span className=\"text-xs text-text-secondary\">{metric.label}</span>\n </div>\n <p className={cn('text-2xl font-bold', metric.color)}>{metric.value}</p>\n </div>\n );\n })}\n </div>\n </section>\n\n {/* Top Selling + Category Performance */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Top Selling Offerings */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Top Selling Offerings\n </h2>\n </div>\n {topSelling.length === 0 ? (\n <p className=\"text-sm text-text-secondary p-5\">\n No sales yet. Your best-selling items will appear here.\n </p>\n ) : (\n <>\n <div className=\"divide-y divide-border\">\n {topSelling.map((item, i) => (\n <div key={item.name} className=\"flex items-center justify-between px-5 py-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-6 w-6 items-center justify-center rounded-full bg-bg-sunken text-xs font-medium text-text-secondary\">\n {i + 1}\n </span>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{item.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {item.category} | {item.quantity} sold\n </p>\n </div>\n </div>\n <span className=\"text-sm font-medium text-text-primary\">\n {formatCurrency(item.revenue, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n {leastSelling.length > 0 && (\n <div className=\"border-t border-border-subtle px-5 py-3\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Least Selling</p>\n {leastSelling.map((item) => (\n <div key={item.name} className=\"flex items-center justify-between py-1.5\">\n <span className=\"text-xs text-text-secondary\">{item.name}</span>\n <span className=\"text-xs text-text-secondary\">\n {item.quantity} sold |{' '}\n {formatCurrency(item.revenue, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n )}\n </>\n )}\n </div>\n\n {/* Category Performance */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n Category Performance\n </h2>\n </div>\n <div className=\"p-5 space-y-4\">\n {categories.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No category data yet.\n </p>\n ) : (\n categories.map((cat) => {\n const pct = (cat.revenue / maxCategoryRevenue) * 100;\n return (\n <div key={cat.category}>\n <div className=\"flex items-center justify-between text-sm mb-1\">\n <span className=\"text-text-primary\">{cat.category}</span>\n <span className=\"text-text-secondary\">\n {formatCurrency(cat.revenue, { minimumFractionDigits: 0 })} ({cat.units}{' '}\n sold)\n </span>\n </div>\n <div className=\"h-2.5 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n {/* Reviews & Ratings + Peak Hours/Days */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Reviews & Ratings */}\n <div className=\"lg:col-span-2 rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Star className=\"h-4 w-4 text-text-secondary\" />\n Reviews & Ratings\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-4 mb-5\">\n <div>\n <p className=\"text-xs text-text-secondary\">Total Reviews</p>\n <p className=\"text-xl font-bold text-text-primary\">{reviewSummary.count}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Average Rating</p>\n <p className=\"text-xl font-bold text-status-warning-text flex items-center gap-1\">\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n {reviewSummary.count > 0 ? reviewSummary.average.toFixed(1) : '—'}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Positive (4★+)</p>\n <p className=\"text-xl font-bold text-status-success-text\">\n {reviewSummary.positive}\n </p>\n </div>\n </div>\n\n {reviewSummary.count === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No reviews yet. Customer reviews will appear here once you start fulfilling orders.\n </p>\n ) : (\n <>\n {/* Rating distribution */}\n <div className=\"space-y-1.5 mb-5\">\n {reviewSummary.distribution.map((d) => (\n <div key={d.star} className=\"flex items-center gap-2 text-xs\">\n <span className=\"w-8 text-text-secondary flex items-center gap-0.5\">\n {d.star}\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n </span>\n <div className=\"flex-1 h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${(d.count / maxDistribution) * 100}%` }}\n />\n </div>\n <span className=\"w-6 text-right text-text-secondary\">{d.count}</span>\n </div>\n ))}\n </div>\n\n {/* Recent reviews */}\n <p className=\"text-sm font-medium text-text-primary mb-2\">Recent Reviews</p>\n <div className=\"space-y-3\">\n {reviewSummary.recent.map((r) => (\n <div\n key={r.id}\n className=\"rounded-md border border-border-subtle p-3 text-sm\"\n >\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"flex items-center gap-0.5 text-status-warning-text text-xs font-medium\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n {r.rating.toFixed(1)}\n </span>\n <span className=\"text-xs text-text-secondary\">\n {new Date(r.createdAt).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })}\n </span>\n </div>\n {r.review && <p className=\"text-xs text-text-secondary\">{r.review}</p>}\n </div>\n ))}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Peak Hours & Days */}\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n Peak Hours\n </h2>\n </div>\n <div className=\"p-5 space-y-3\">\n {peakHours.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-2 text-center\">No data yet.</p>\n ) : (\n peakHours.map((h) => {\n const pct = (h.orders / maxPeakHourOrders) * 100;\n return (\n <div key={h.hour}>\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span className=\"text-text-primary\">{`${h.hour}:00`}</span>\n <span className=\"text-text-secondary\">{h.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <CalendarDays className=\"h-4 w-4 text-text-secondary\" />\n Peak Days\n </h2>\n </div>\n <div className=\"p-5 space-y-3\">\n {peakDays.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-2 text-center\">No data yet.</p>\n ) : (\n peakDays.map((d) => {\n const pct = (d.orders / maxPeakDayOrders) * 100;\n return (\n <div key={d.day}>\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span className=\"text-text-primary\">{d.day}</span>\n <span className=\"text-text-secondary\">{d.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n\n {/* Reviews satisfaction footnote */}\n {reviewSummary.count > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\">\n <div className=\"flex items-center gap-1.5 text-sm\">\n <ThumbsUp className=\"h-4 w-4 text-status-success-text\" />\n <span className=\"text-text-secondary\">\n {reviewSummary.positive}/{reviewSummary.count} positive reviews\n </span>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Daily Order Trends (last 14 days, real) */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Daily Order Trends\n </h2>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-5 py-2.5 text-left font-medium text-text-secondary\">Date</th>\n <th className=\"px-5 py-2.5 text-right font-medium text-text-secondary\">Orders</th>\n <th className=\"px-5 py-2.5 text-right font-medium text-text-secondary\">Revenue</th>\n </tr>\n </thead>\n <tbody>\n {orderTrends.map((day) => (\n <tr key={day.date} className=\"border-b border-border-subtle last:border-0\">\n <td className=\"px-5 py-2.5 text-text-primary\">\n {new Date(day.date).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n })}\n </td>\n <td className=\"px-5 py-2.5 text-right text-text-primary\">{day.orders}</td>\n <td className=\"px-5 py-2.5 text-right font-medium text-text-primary\">\n {formatCurrency(day.revenue, { minimumFractionDigits: 0 })}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsDashboard.js","names":[],"sources":["../../../../src/kadaikodi/pages/cart-owner/AnalyticsDashboard.tsx"],"sourcesContent":["/**\n * Analytics Dashboard Page\n *\n * Performance insights for the signed-in owner's own service provider (cart):\n * revenue overview with real period-over-period growth, order statistics,\n * popular times, performance metrics, top/least selling offerings, category\n * performance, reviews & ratings, peak hours/days and daily order trends —\n * every number derived from live backend data (no fabrication).\n *\n * @module kadaikodi/pages/cart-owner\n */\n\nimport { useMemo, useState } from 'react';\nimport {\n IndianRupee,\n ShoppingCart,\n TrendingUp,\n BarChart3,\n Clock,\n Star,\n ArrowUpRight,\n ArrowDownRight,\n Download,\n Target,\n Percent,\n CalendarDays,\n ThumbsUp,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useKadaikodi } from '../../context/KadaikodiContext';\nimport { useProviders, useOrders, useOfferings, useReviews } from '../../hooks/useKadaikodiApi';\nimport { formatCurrency } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DAY_MS = 86_400_000;\n\nconst CATEGORY_LABELS: Record<string, string> = {\n FOOD_BEVERAGE: 'Food & Beverage',\n PERSONAL_CARE_BEAUTY: 'Personal Care & Beauty',\n CLOTHING_FASHION: 'Clothing & Fashion',\n REPAIR_MAINTENANCE: 'Repair & Maintenance',\n HOME_SERVICES: 'Home Services',\n HEALTH_WELLNESS: 'Health & Wellness',\n EDUCATION_SKILLS: 'Education & Skills',\n RETAIL_MERCHANDISE: 'Retail & Merchandise',\n GROCERY_ESSENTIALS: 'Grocery & Essentials',\n};\n\nconst startOfToday = (): Date => {\n const d = new Date();\n d.setHours(0, 0, 0, 0);\n return d;\n};\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const AnalyticsDashboard = () => {\n const { navigateTo, currentUser } = useKadaikodi();\n const [dateRange, setDateRange] = useState('week');\n\n // Resolve the signed-in owner's provider (\"my cart\").\n const { data: providers = [], isLoading: providersLoading } = useProviders();\n const myProvider = useMemo(\n () => providers.find((p) => p.ownerId === currentUser?.id) ?? providers[0],\n [providers, currentUser?.id]\n );\n const providerId = myProvider?.id;\n\n const { data: orders = [] } = useOrders({ providerId });\n const { data: offerings = [] } = useOfferings(providerId);\n const { data: reviews = [] } = useReviews(providerId ?? '');\n\n const offeringCategoryByName = useMemo(\n () => new Map(offerings.map((o) => [o.name, CATEGORY_LABELS[o.category] ?? o.category])),\n [offerings]\n );\n\n // ---- Revenue (real, from delivered orders) ----\n const revenue = useMemo(() => {\n const delivered = orders.filter((o) => o.status === 'delivered');\n const t0 = startOfToday().getTime();\n const inRange = (start: number, end: number) =>\n delivered\n .filter((o) => {\n const d = new Date(o.orderDate).getTime();\n return d >= start && d < end;\n })\n .reduce((s, o) => s + o.total, 0);\n\n const today = inRange(t0, t0 + DAY_MS);\n const yesterday = inRange(t0 - DAY_MS, t0);\n const week = inRange(t0 - 6 * DAY_MS, t0 + DAY_MS);\n const prevWeek = inRange(t0 - 13 * DAY_MS, t0 - 6 * DAY_MS);\n const month = inRange(t0 - 29 * DAY_MS, t0 + DAY_MS);\n const prevMonth = inRange(t0 - 59 * DAY_MS, t0 - 29 * DAY_MS);\n const year = inRange(t0 - 364 * DAY_MS, t0 + DAY_MS);\n\n const growth = (cur: number, prev: number): number | null =>\n prev > 0 ? Math.round(((cur - prev) / prev) * 1000) / 10 : null;\n\n return {\n today,\n week,\n month,\n year,\n dailyGrowth: growth(today, yesterday),\n weeklyGrowth: growth(week, prevWeek),\n monthlyGrowth: growth(month, prevMonth),\n };\n }, [orders]);\n\n // ---- Order & performance stats (real) ----\n const orderStats = useMemo(() => {\n const total = orders.length;\n const delivered = orders.filter((o) => o.status === 'delivered');\n const cancelled = orders.filter((o) => o.status === 'cancelled');\n const avgOrderValue =\n delivered.length > 0\n ? Math.round(delivered.reduce((s, o) => s + o.total, 0) / delivered.length)\n : 0;\n return {\n total,\n completed: delivered.length,\n cancelled: cancelled.length,\n avgOrderValue,\n acceptanceRate: total > 0 ? Math.round(((total - cancelled.length) / total) * 100) : 0,\n cancellationRate: total > 0 ? Math.round((cancelled.length / total) * 100) : 0,\n completionRate: total > 0 ? Math.round((delivered.length / total) * 100) : 0,\n };\n }, [orders]);\n\n // ---- Popular times, peak hours & days (real, from order timestamps) ----\n const { popularTimes, peakHours, peakDays } = useMemo(() => {\n const hourCounts = new Array<number>(24).fill(0);\n const dayCounts = new Map<string, number>();\n orders.forEach((o) => {\n const d = new Date(o.orderDate);\n hourCounts[d.getHours()] += 1;\n const day = d.toLocaleDateString('en-US', { weekday: 'long' });\n dayCounts.set(day, (dayCounts.get(day) ?? 0) + 1);\n });\n const times = hourCounts\n .map((count, hour) => ({ hour, orders: count }))\n .filter((h) => h.orders > 0);\n return {\n popularTimes: times,\n peakHours: [...times].sort((a, b) => b.orders - a.orders).slice(0, 4),\n peakDays: [...dayCounts.entries()]\n .map(([day, count]) => ({ day, orders: count }))\n .sort((a, b) => b.orders - a.orders)\n .slice(0, 3),\n };\n }, [orders]);\n\n // ---- Top / least selling & category performance (real, from order items) ----\n const { topSelling, leastSelling, categories } = useMemo(() => {\n const itemAgg = new Map<string, { quantity: number; revenue: number; category: string }>();\n const catAgg = new Map<string, { revenue: number; units: number }>();\n orders.forEach((o) =>\n o.items.forEach((it) => {\n const category = offeringCategoryByName.get(it.name) ?? 'Other';\n const item = itemAgg.get(it.name) ?? { quantity: 0, revenue: 0, category };\n item.quantity += it.quantity;\n item.revenue += it.price * it.quantity;\n itemAgg.set(it.name, item);\n const cat = catAgg.get(category) ?? { revenue: 0, units: 0 };\n cat.revenue += it.price * it.quantity;\n cat.units += it.quantity;\n catAgg.set(category, cat);\n })\n );\n const items = [...itemAgg.entries()].map(([name, v]) => ({ name, ...v }));\n return {\n topSelling: [...items].sort((a, b) => b.revenue - a.revenue).slice(0, 5),\n leastSelling: [...items].sort((a, b) => a.quantity - b.quantity).slice(0, 3),\n categories: [...catAgg.entries()]\n .map(([category, v]) => ({ category, ...v }))\n .sort((a, b) => b.revenue - a.revenue),\n };\n }, [orders, offeringCategoryByName]);\n\n // ---- Reviews & ratings (real) ----\n const reviewSummary = useMemo(() => {\n const count = reviews.length;\n const average = count > 0 ? reviews.reduce((s, r) => s + r.rating, 0) / count : 0;\n const positive = reviews.filter((r) => r.rating >= 4).length;\n const distribution = [5, 4, 3, 2, 1].map((star) => ({\n star,\n count: reviews.filter((r) => r.rating === star).length,\n }));\n const recent = [...reviews]\n .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())\n .slice(0, 4);\n return { count, average, positive, distribution, recent };\n }, [reviews]);\n\n // ---- Daily order trends (real, last 14 days) ----\n const orderTrends = useMemo(() => {\n const t0 = startOfToday().getTime();\n const days: { date: string; orders: number; revenue: number }[] = [];\n for (let i = 13; i >= 0; i--) {\n const start = t0 - i * DAY_MS;\n const end = start + DAY_MS;\n const dayOrders = orders.filter((o) => {\n const d = new Date(o.orderDate).getTime();\n return d >= start && d < end;\n });\n days.push({\n date: new Date(start).toISOString(),\n orders: dayOrders.length,\n revenue: dayOrders.filter((o) => o.status === 'delivered').reduce((s, o) => s + o.total, 0),\n });\n }\n return days;\n }, [orders]);\n\n const maxPopularTime = Math.max(...popularTimes.map((t) => t.orders), 1);\n const maxCategoryRevenue = Math.max(...categories.map((c) => c.revenue), 1);\n const maxPeakHourOrders = Math.max(...peakHours.map((h) => h.orders), 1);\n const maxPeakDayOrders = Math.max(...peakDays.map((d) => d.orders), 1);\n const maxDistribution = Math.max(...reviewSummary.distribution.map((d) => d.count), 1);\n\n const formatPct = (v: number) => `${v > 0 ? '+' : ''}${v}%`;\n\n // ---- Loading / no-provider states ----\n if (providersLoading) {\n return (\n <PageLayout title=\"Analytics Dashboard\" description=\"Business performance insights and data\">\n <div className=\"flex items-center justify-center py-24\">\n <div className=\"h-10 w-10 animate-spin rounded-full border-b-2 border-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n if (!myProvider) {\n return (\n <PageLayout title=\"Analytics Dashboard\" description=\"Business performance insights and data\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"No analytics yet\"\n description=\"You don't have a service provider yet. Set up your cart and start taking orders to see your performance insights here.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/cart-owner/service-config')}\n className=\"inline-flex items-center gap-2 rounded-md bg-accent px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent/80\"\n >\n Set up your cart\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const revenueCards = [\n { label: \"Today's Revenue\", value: revenue.today, growth: revenue.dailyGrowth },\n { label: 'This Week', value: revenue.week, growth: revenue.weeklyGrowth },\n { label: 'This Month', value: revenue.month, growth: revenue.monthlyGrowth },\n { label: 'This Year', value: revenue.year, growth: null as number | null },\n ];\n\n return (\n <PageLayout\n title=\"Analytics Dashboard\"\n description={`Performance insights for ${myProvider.name}`}\n actions={\n <div className=\"flex items-center gap-2\">\n <select\n value={dateRange}\n onChange={(e) => setDateRange(e.target.value)}\n className=\"rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary\"\n >\n <option value=\"today\">Today</option>\n <option value=\"week\">This Week</option>\n <option value=\"month\">This Month</option>\n <option value=\"year\">This Year</option>\n </select>\n <button\n type=\"button\"\n onClick={() => alert('Export feature coming soon!')}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-2 text-sm text-text-primary hover:bg-bg-sunken transition-colors\"\n >\n <Download className=\"h-4 w-4\" />\n Export\n </button>\n </div>\n }\n >\n <PagePurpose className=\"mb-6\">\n The performance story of your cart. Use this page to understand how your business is really\n doing — track revenue over time, see how orders break down, learn which hours are busiest so\n you can staff and stock accordingly, and spot your best-selling items. All figures are drawn\n from your live orders and reviews.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"kadaikodi-owner-analytics\"\n className=\"mb-6\"\n steps={[\n {\n id: 'inventory',\n label: 'Restock your best-sellers',\n description: 'Make sure popular items never run out.',\n onClick: () => navigateTo('/cart-owner/inventory'),\n },\n {\n id: 'orders',\n label: 'Work through live orders',\n description: 'Keep prep and delivery times sharp.',\n onClick: () => navigateTo('/cart-owner/orders'),\n },\n {\n id: 'staff',\n label: 'Plan staff for peak hours',\n description: 'Cover the busiest times you see here.',\n onClick: () => navigateTo('/cart-owner/staff'),\n },\n ]}\n />\n\n <div className=\"space-y-6\">\n {/* Revenue Overview — the one emphasis zone for this page */}\n <EmphasisPanel>\n <h2 className=\"text-base font-semibold text-text-primary mb-3\">Revenue Overview</h2>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {revenueCards.map((card) => (\n <div\n key={card.label}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\"\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"rounded-md bg-accent p-2\">\n <IndianRupee className=\"h-5 w-5 text-text-secondary\" />\n </div>\n {card.growth !== null && (\n <div\n className={cn(\n 'flex items-center gap-1 text-xs font-medium',\n card.growth >= 0 ? 'text-status-success-text' : 'text-status-error-text'\n )}\n >\n {card.growth >= 0 ? (\n <ArrowUpRight className=\"h-3.5 w-3.5\" />\n ) : (\n <ArrowDownRight className=\"h-3.5 w-3.5\" />\n )}\n {formatPct(card.growth)}\n </div>\n )}\n </div>\n <p className=\"mt-3 text-2xl font-bold text-text-primary\">\n {formatCurrency(card.value, { minimumFractionDigits: 0 })}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">{card.label}</p>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Order Statistics + Popular Times */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Order Statistics */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <ShoppingCart className=\"h-4 w-4 text-text-secondary\" />\n Order Statistics\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-text-secondary\">Total Orders</p>\n <p className=\"text-2xl font-bold text-text-primary\">{orderStats.total}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Avg. Order Value</p>\n <p className=\"text-2xl font-bold text-text-primary\">\n {formatCurrency(orderStats.avgOrderValue, { minimumFractionDigits: 0 })}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Completed</p>\n <p className=\"text-xl font-bold text-status-success-text\">\n {orderStats.completed}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Cancelled</p>\n <p className=\"text-xl font-bold text-status-error-text\">{orderStats.cancelled}</p>\n </div>\n </div>\n </div>\n </div>\n\n {/* Popular Times */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n Popular Times\n </h2>\n </div>\n <div className=\"p-5 space-y-2\">\n {popularTimes.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No order activity yet. Busy hours will appear here once orders come in.\n </p>\n ) : (\n popularTimes.map((t) => {\n const pct = (t.orders / maxPopularTime) * 100;\n return (\n <div key={t.hour}>\n <div className=\"flex items-center justify-between text-xs mb-0.5\">\n <span className=\"text-text-primary w-14\">{`${t.hour}:00`}</span>\n <span className=\"text-text-secondary\">{t.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n {/* Performance Metrics */}\n <section>\n <h2 className=\"text-base font-semibold text-text-primary mb-3\">Performance Metrics</h2>\n <div className=\"grid grid-cols-2 lg:grid-cols-4 gap-4\">\n {[\n {\n label: 'Acceptance Rate',\n value: `${orderStats.acceptanceRate}%`,\n icon: Target,\n color: 'text-status-success-text',\n },\n {\n label: 'Cancellation Rate',\n value: `${orderStats.cancellationRate}%`,\n icon: Percent,\n color: 'text-status-error-text',\n },\n {\n label: 'Completion Rate',\n value: `${orderStats.completionRate}%`,\n icon: TrendingUp,\n color: 'text-text-primary',\n },\n {\n label: 'Average Rating',\n value: (myProvider.rating ?? 0).toFixed(1),\n icon: Star,\n color: 'text-status-warning-text',\n },\n ].map((metric) => {\n const Icon = metric.icon;\n return (\n <div\n key={metric.label}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\"\n >\n <div className=\"flex items-center gap-2 mb-2\">\n <Icon className={cn('h-5 w-5', metric.color)} />\n <span className=\"text-xs text-text-secondary\">{metric.label}</span>\n </div>\n <p className={cn('text-2xl font-bold', metric.color)}>{metric.value}</p>\n </div>\n );\n })}\n </div>\n </section>\n\n {/* Top Selling + Category Performance */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Top Selling Offerings */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Top Selling Offerings\n </h2>\n </div>\n {topSelling.length === 0 ? (\n <p className=\"text-sm text-text-secondary p-5\">\n No sales yet. Your best-selling items will appear here.\n </p>\n ) : (\n <>\n <div className=\"divide-y divide-border\">\n {topSelling.map((item, i) => (\n <div key={item.name} className=\"flex items-center justify-between px-5 py-3\">\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-6 w-6 items-center justify-center rounded-full bg-bg-sunken text-xs font-medium text-text-secondary\">\n {i + 1}\n </span>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">{item.name}</p>\n <p className=\"text-xs text-text-secondary\">\n {item.category} | {item.quantity} sold\n </p>\n </div>\n </div>\n <span className=\"text-sm font-medium text-text-primary\">\n {formatCurrency(item.revenue, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n {leastSelling.length > 0 && (\n <div className=\"border-t border-border-subtle px-5 py-3\">\n <p className=\"text-xs font-medium text-text-secondary mb-2\">Least Selling</p>\n {leastSelling.map((item) => (\n <div key={item.name} className=\"flex items-center justify-between py-1.5\">\n <span className=\"text-xs text-text-secondary\">{item.name}</span>\n <span className=\"text-xs text-text-secondary\">\n {item.quantity} sold |{' '}\n {formatCurrency(item.revenue, { minimumFractionDigits: 0 })}\n </span>\n </div>\n ))}\n </div>\n )}\n </>\n )}\n </div>\n\n {/* Category Performance */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-text-secondary\" />\n Category Performance\n </h2>\n </div>\n <div className=\"p-5 space-y-4\">\n {categories.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No category data yet.\n </p>\n ) : (\n categories.map((cat) => {\n const pct = (cat.revenue / maxCategoryRevenue) * 100;\n return (\n <div key={cat.category}>\n <div className=\"flex items-center justify-between text-sm mb-1\">\n <span className=\"text-text-primary\">{cat.category}</span>\n <span className=\"text-text-secondary\">\n {formatCurrency(cat.revenue, { minimumFractionDigits: 0 })} ({cat.units}{' '}\n sold)\n </span>\n </div>\n <div className=\"h-2.5 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n {/* Reviews & Ratings + Peak Hours/Days */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Reviews & Ratings */}\n <div className=\"lg:col-span-2 rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Star className=\"h-4 w-4 text-text-secondary\" />\n Reviews & Ratings\n </h2>\n </div>\n <div className=\"p-5\">\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-4 mb-5\">\n <div>\n <p className=\"text-xs text-text-secondary\">Total Reviews</p>\n <p className=\"text-xl font-bold text-text-primary\">{reviewSummary.count}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Average Rating</p>\n <p className=\"text-xl font-bold text-status-warning-text flex items-center gap-1\">\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n {reviewSummary.count > 0 ? reviewSummary.average.toFixed(1) : '—'}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Positive (4★+)</p>\n <p className=\"text-xl font-bold text-status-success-text\">\n {reviewSummary.positive}\n </p>\n </div>\n </div>\n\n {reviewSummary.count === 0 ? (\n <p className=\"text-sm text-text-secondary py-4 text-center\">\n No reviews yet. Customer reviews will appear here once you start fulfilling\n orders.\n </p>\n ) : (\n <>\n {/* Rating distribution */}\n <div className=\"space-y-1.5 mb-5\">\n {reviewSummary.distribution.map((d) => (\n <div key={d.star} className=\"flex items-center gap-2 text-xs\">\n <span className=\"w-8 text-text-secondary flex items-center gap-0.5\">\n {d.star}\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n </span>\n <div className=\"flex-1 h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${(d.count / maxDistribution) * 100}%` }}\n />\n </div>\n <span className=\"w-6 text-right text-text-secondary\">{d.count}</span>\n </div>\n ))}\n </div>\n\n {/* Recent reviews */}\n <p className=\"text-sm font-medium text-text-primary mb-2\">Recent Reviews</p>\n <div className=\"space-y-3\">\n {reviewSummary.recent.map((r) => (\n <div\n key={r.id}\n className=\"rounded-md border border-border-subtle p-3 text-sm\"\n >\n <div className=\"flex items-center justify-between mb-1\">\n <span className=\"flex items-center gap-0.5 text-status-warning-text text-xs font-medium\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n {(r.rating ?? 0).toFixed(1)}\n </span>\n <span className=\"text-xs text-text-secondary\">\n {new Date(r.createdAt).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })}\n </span>\n </div>\n {r.review && <p className=\"text-xs text-text-secondary\">{r.review}</p>}\n </div>\n ))}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Peak Hours & Days */}\n <div className=\"space-y-6\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n Peak Hours\n </h2>\n </div>\n <div className=\"p-5 space-y-3\">\n {peakHours.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-2 text-center\">No data yet.</p>\n ) : (\n peakHours.map((h) => {\n const pct = (h.orders / maxPeakHourOrders) * 100;\n return (\n <div key={h.hour}>\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span className=\"text-text-primary\">{`${h.hour}:00`}</span>\n <span className=\"text-text-secondary\">{h.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <CalendarDays className=\"h-4 w-4 text-text-secondary\" />\n Peak Days\n </h2>\n </div>\n <div className=\"p-5 space-y-3\">\n {peakDays.length === 0 ? (\n <p className=\"text-sm text-text-secondary py-2 text-center\">No data yet.</p>\n ) : (\n peakDays.map((d) => {\n const pct = (d.orders / maxPeakDayOrders) * 100;\n return (\n <div key={d.day}>\n <div className=\"flex items-center justify-between text-xs mb-1\">\n <span className=\"text-text-primary\">{d.day}</span>\n <span className=\"text-text-secondary\">{d.orders} orders</span>\n </div>\n <div className=\"h-2 rounded-full bg-bg-sunken overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-[var(--color-accent-soft)]\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </div>\n </div>\n\n {/* Reviews satisfaction footnote */}\n {reviewSummary.count > 0 && (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-5 shadow-sm\">\n <div className=\"flex items-center gap-1.5 text-sm\">\n <ThumbsUp className=\"h-4 w-4 text-status-success-text\" />\n <span className=\"text-text-secondary\">\n {reviewSummary.positive}/{reviewSummary.count} positive reviews\n </span>\n </div>\n </div>\n )}\n </div>\n </div>\n\n {/* Daily Order Trends (last 14 days, real) */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface shadow-sm\">\n <div className=\"border-b border-border-subtle px-5 py-4\">\n <h2 className=\"text-base font-semibold text-text-primary flex items-center gap-2\">\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n Daily Order Trends\n </h2>\n </div>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle\">\n <th className=\"px-5 py-2.5 text-left font-medium text-text-secondary\">Date</th>\n <th className=\"px-5 py-2.5 text-right font-medium text-text-secondary\">Orders</th>\n <th className=\"px-5 py-2.5 text-right font-medium text-text-secondary\">\n Revenue\n </th>\n </tr>\n </thead>\n <tbody>\n {orderTrends.map((day) => (\n <tr key={day.date} className=\"border-b border-border-subtle last:border-0\">\n <td className=\"px-5 py-2.5 text-text-primary\">\n {new Date(day.date).toLocaleDateString('en-IN', {\n day: '2-digit',\n month: 'short',\n })}\n </td>\n <td className=\"px-5 py-2.5 text-right text-text-primary\">{day.orders}</td>\n <td className=\"px-5 py-2.5 text-right font-medium text-text-primary\">\n {formatCurrency(day.revenue, { minimumFractionDigits: 0 })}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -287,7 +287,7 @@ function w() {
|
|
|
287
287
|
/* @__PURE__ */ v(g, { className: "h-3 w-3 fill-status-warning-text text-status-warning-text" }),
|
|
288
288
|
/* @__PURE__ */ v("span", {
|
|
289
289
|
className: "font-medium text-text-primary",
|
|
290
|
-
children: e.rating.toFixed(1)
|
|
290
|
+
children: (e.rating ?? 0).toFixed(1)
|
|
291
291
|
}),
|
|
292
292
|
/* @__PURE__ */ y("span", { children: [
|
|
293
293
|
"(",
|