@burdenoff/microfe-proserviceworld 2026.512.1 → 2026.624.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proserviceworld/pages/AdminReportsPage.js +28 -28
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +19 -19
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +27 -27
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/BookingsPage.js +24 -24
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js +33 -33
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +29 -29
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MessagesPage.js +15 -15
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +19 -19
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +36 -36
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +36 -36
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +19 -19
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +22 -22
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +35 -35
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServicesPage.js +14 -14
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/VerificationPage.js +23 -23
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
- package/package.json +1 -1
- package/dist/dev/main.d.ts +0 -2
- package/dist/dev/main.d.ts.map +0 -1
- package/dist/generated/global-operations.d.ts +0 -2
- package/dist/generated/global-operations.d.ts.map +0 -1
- package/dist/generated/global-types.d.ts +0 -18199
- package/dist/generated/global-types.d.ts.map +0 -1
- package/dist/generated/wspace-operations.d.ts +0 -2
- package/dist/generated/wspace-operations.d.ts.map +0 -1
- package/dist/generated/wspace-types.d.ts +0 -43046
- package/dist/generated/wspace-types.d.ts.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts +0 -5
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +0 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts +0 -2
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +0 -1
- package/dist/proserviceworld/index.d.ts +0 -5
- package/dist/proserviceworld/index.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/BookingsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/BookingsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/DashboardPage.d.ts +0 -2
- package/dist/proserviceworld/pages/DashboardPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/DocumentsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/MessagesPage.d.ts +0 -2
- package/dist/proserviceworld/pages/MessagesPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/PaymentsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/PaymentsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProjectsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProjectsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProvidersPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProvidersPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ReviewsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ReviewsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ServicesPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ServicesPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/VerificationPage.d.ts +0 -2
- package/dist/proserviceworld/pages/VerificationPage.d.ts.map +0 -1
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts +0 -6
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts.map +0 -1
- package/dist/proserviceworld/store/proserviceworldStore.d.ts +0 -32
- package/dist/proserviceworld/store/proserviceworldStore.d.ts.map +0 -1
- package/dist/proserviceworld/types/index.d.ts +0 -338
- package/dist/proserviceworld/types/index.d.ts.map +0 -1
- package/dist/proserviceworld/utils/cn.d.ts +0 -4
- package/dist/proserviceworld/utils/cn.d.ts.map +0 -1
- package/dist/proserviceworld/utils/i18n.d.ts +0 -6
- package/dist/proserviceworld/utils/i18n.d.ts.map +0 -1
- package/dist/proserviceworld/utils/navigation.d.ts +0 -2
- package/dist/proserviceworld/utils/navigation.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUsersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminUsersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Filter,\n Users,\n UserCheck,\n UserX,\n Shield,\n Star,\n CheckCircle2,\n XCircle,\n Clock,\n Ban,\n MoreHorizontal,\n Download,\n Plus,\n Mail,\n Calendar,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { AdminUserRole, AdminUserStatus } from '../types';\n\nconst roleConfig: Record<\n AdminUserRole,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n client: { label: 'Client', variant: 'secondary' },\n provider: { label: 'Provider', variant: 'default' },\n admin: { label: 'Admin', variant: 'destructive' },\n team_member: { label: 'Team', variant: 'outline' },\n};\n\nconst statusConfig: Record<\n AdminUserStatus,\n { label: string; icon: typeof CheckCircle2; color: string }\n> = {\n active: { label: 'Active', icon: CheckCircle2, color: 'text-green-600' },\n suspended: { label: 'Suspended', icon: Ban, color: 'text-orange-600' },\n pending: { label: 'Pending', icon: Clock, color: 'text-yellow-600' },\n banned: { label: 'Banned', icon: XCircle, color: 'text-red-600' },\n};\n\nexport function AdminUsersPage() {\n const { adminUsers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [roleFilter, setRoleFilter] = useState<AdminUserRole | 'all'>('all');\n const [statusFilter, setStatusFilter] = useState<AdminUserStatus | 'all'>('all');\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const filtered = useMemo(() => {\n let result = [...adminUsers];\n if (roleFilter !== 'all') result = result.filter((u) => u.role === roleFilter);\n if (statusFilter !== 'all') result = result.filter((u) => u.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (u) => u.name.toLowerCase().includes(q) || u.email.toLowerCase().includes(q)\n );\n }\n return result;\n }, [adminUsers, roleFilter, statusFilter, searchQuery]);\n\n // Stats\n const totalUsers = adminUsers.length;\n const activeUsers = adminUsers.filter((u) => u.status === 'active').length;\n const providers = adminUsers.filter((u) => u.role === 'provider').length;\n const verifiedUsers = adminUsers.filter((u) => u.verified).length;\n\n const toggleSelect = (id: string) => {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setSelectedIds(next);\n };\n\n const toggleSelectAll = () => {\n if (selectedIds.size === filtered.length) setSelectedIds(new Set());\n else setSelectedIds(new Set(filtered.map((u) => u.id)));\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">User Management</h1>\n <p className=\"text-sm text-muted-foreground\">\n Admin panel to manage platform users, roles, and access.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export\n </Button>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" /> Invite User\n </Button>\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-blue-100 p-2 dark:bg-blue-950\">\n <Users className=\"h-5 w-5 text-blue-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{totalUsers}</p>\n <p className=\"text-xs text-muted-foreground\">Total Users</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-green-100 p-2 dark:bg-green-950\">\n <UserCheck className=\"h-5 w-5 text-green-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{activeUsers}</p>\n <p className=\"text-xs text-muted-foreground\">Active Users</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-purple-100 p-2 dark:bg-purple-950\">\n <Shield className=\"h-5 w-5 text-purple-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{providers}</p>\n <p className=\"text-xs text-muted-foreground\">Providers</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-yellow-100 p-2 dark:bg-yellow-950\">\n <CheckCircle2 className=\"h-5 w-5 text-yellow-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{verifiedUsers}</p>\n <p className=\"text-xs text-muted-foreground\">Verified</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search by name or email...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <select\n className=\"rounded border bg-background px-2 py-1 text-sm\"\n value={roleFilter}\n onChange={(e) => setRoleFilter(e.target.value as AdminUserRole | 'all')}\n >\n <option value=\"all\">All Roles</option>\n <option value=\"client\">Client</option>\n <option value=\"provider\">Provider</option>\n <option value=\"admin\">Admin</option>\n <option value=\"team_member\">Team Member</option>\n </select>\n <select\n className=\"rounded border bg-background px-2 py-1 text-sm\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as AdminUserStatus | 'all')}\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n <option value=\"banned\">Banned</option>\n </select>\n </div>\n </div>\n\n {/* Bulk actions */}\n {selectedIds.size > 0 && (\n <div className=\"flex items-center gap-3 rounded-lg bg-primary/5 border border-primary/20 p-3\">\n <span className=\"text-sm font-medium\">{selectedIds.size} user(s) selected</span>\n <Button variant=\"outline\" size=\"sm\">\n Activate\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Suspend\n </Button>\n <Button variant=\"destructive\" size=\"sm\">\n Ban\n </Button>\n </div>\n )}\n\n {/* User table */}\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-muted-foreground\">\n <th className=\"p-3\">\n <input\n type=\"checkbox\"\n checked={selectedIds.size === filtered.length && filtered.length > 0}\n onChange={toggleSelectAll}\n className=\"rounded\"\n />\n </th>\n <th className=\"p-3\">User</th>\n <th className=\"p-3\">Role</th>\n <th className=\"p-3\">Status</th>\n <th className=\"p-3\">Verified</th>\n <th className=\"p-3\">Joined</th>\n <th className=\"p-3\">Last Active</th>\n <th className=\"p-3 text-right\">Bookings</th>\n <th className=\"p-3 text-right\">Spent</th>\n <th className=\"p-3\">Rating</th>\n <th className=\"p-3 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filtered.length === 0 ? (\n <tr>\n <td colSpan={11} className=\"p-8 text-center text-muted-foreground\">\n No users match your filters.\n </td>\n </tr>\n ) : (\n filtered.map((user) => {\n const sCfg = statusConfig[user.status];\n const StatusIcon = sCfg.icon;\n return (\n <tr key={user.id} className=\"border-b hover:bg-muted/50\">\n <td className=\"p-3\">\n <input\n type=\"checkbox\"\n checked={selectedIds.has(user.id)}\n onChange={() => toggleSelect(user.id)}\n className=\"rounded\"\n />\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary\">\n {user.name.charAt(0)}\n </div>\n <div>\n <p className=\"font-medium\">{user.name}</p>\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n </div>\n </div>\n </td>\n <td className=\"p-3\">\n <Badge variant={roleConfig[user.role].variant}>\n {roleConfig[user.role].label}\n </Badge>\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-1\">\n <StatusIcon className={`h-4 w-4 ${sCfg.color}`} />\n <span className={`text-xs font-medium ${sCfg.color}`}>\n {sCfg.label}\n </span>\n </div>\n </td>\n <td className=\"p-3\">\n {user.verified ? (\n <CheckCircle2 className=\"h-4 w-4 text-green-500\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-gray-300\" />\n )}\n </td>\n <td className=\"p-3 text-muted-foreground\">{user.joinedDate}</td>\n <td className=\"p-3 text-muted-foreground\">{user.lastActive}</td>\n <td className=\"p-3 text-right\">{user.bookingsCount}</td>\n <td className=\"p-3 text-right\">\n {user.totalSpent > 0 ? `$${user.totalSpent.toLocaleString()}` : '-'}\n </td>\n <td className=\"p-3\">\n {user.rating ? (\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-yellow-400 text-yellow-400\" />\n <span className=\"text-xs font-medium\">{user.rating}</span>\n </div>\n ) : (\n <span className=\"text-xs text-muted-foreground\">-</span>\n )}\n </td>\n <td className=\"p-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button variant=\"outline\" size=\"sm\">\n <Mail className=\"h-3 w-3\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <MoreHorizontal className=\"h-3 w-3\" />\n </Button>\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination hint */}\n <div className=\"mt-4 flex items-center justify-between border-t pt-4 text-sm text-muted-foreground\">\n <span>\n Showing {filtered.length} of {adminUsers.length} users\n </span>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\" disabled>\n Previous\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled>\n Next\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminUsersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminUsersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Input,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Filter,\n Users,\n UserCheck,\n UserX,\n Shield,\n Star,\n CheckCircle2,\n XCircle,\n Clock,\n Ban,\n MoreHorizontal,\n Download,\n Plus,\n Mail,\n Calendar,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { AdminUserRole, AdminUserStatus } from '../types';\n\nconst roleConfig: Record<\n AdminUserRole,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n client: { label: 'Client', variant: 'secondary' },\n provider: { label: 'Provider', variant: 'default' },\n admin: { label: 'Admin', variant: 'destructive' },\n team_member: { label: 'Team', variant: 'outline' },\n};\n\nconst statusConfig: Record<\n AdminUserStatus,\n { label: string; icon: typeof CheckCircle2; color: string }\n> = {\n active: { label: 'Active', icon: CheckCircle2, color: 'text-green-600' },\n suspended: { label: 'Suspended', icon: Ban, color: 'text-orange-600' },\n pending: { label: 'Pending', icon: Clock, color: 'text-yellow-600' },\n banned: { label: 'Banned', icon: XCircle, color: 'text-red-600' },\n};\n\nexport function AdminUsersPage() {\n const { adminUsers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [roleFilter, setRoleFilter] = useState<AdminUserRole | 'all'>('all');\n const [statusFilter, setStatusFilter] = useState<AdminUserStatus | 'all'>('all');\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const filtered = useMemo(() => {\n let result = [...adminUsers];\n if (roleFilter !== 'all') result = result.filter((u) => u.role === roleFilter);\n if (statusFilter !== 'all') result = result.filter((u) => u.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (u) => u.name.toLowerCase().includes(q) || u.email.toLowerCase().includes(q)\n );\n }\n return result;\n }, [adminUsers, roleFilter, statusFilter, searchQuery]);\n\n // Stats\n const totalUsers = adminUsers.length;\n const activeUsers = adminUsers.filter((u) => u.status === 'active').length;\n const providers = adminUsers.filter((u) => u.role === 'provider').length;\n const verifiedUsers = adminUsers.filter((u) => u.verified).length;\n\n const toggleSelect = (id: string) => {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setSelectedIds(next);\n };\n\n const toggleSelectAll = () => {\n if (selectedIds.size === filtered.length) setSelectedIds(new Set());\n else setSelectedIds(new Set(filtered.map((u) => u.id)));\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">User Management</h1>\n <p className=\"text-sm text-text-secondary\">\n Admin panel to manage platform users, roles, and access.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export\n </Button>\n <Button size=\"sm\">\n <Plus className=\"mr-2 h-4 w-4\" /> Invite User\n </Button>\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-blue-100 p-2 dark:bg-blue-950\">\n <Users className=\"h-5 w-5 text-blue-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{totalUsers}</p>\n <p className=\"text-xs text-text-secondary\">Total Users</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-green-100 p-2 dark:bg-green-950\">\n <UserCheck className=\"h-5 w-5 text-green-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{activeUsers}</p>\n <p className=\"text-xs text-text-secondary\">Active Users</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-purple-100 p-2 dark:bg-purple-950\">\n <Shield className=\"h-5 w-5 text-purple-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{providers}</p>\n <p className=\"text-xs text-text-secondary\">Providers</p>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-yellow-100 p-2 dark:bg-yellow-950\">\n <CheckCircle2 className=\"h-5 w-5 text-yellow-600\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold\">{verifiedUsers}</p>\n <p className=\"text-xs text-text-secondary\">Verified</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search by name or email...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-text-secondary\" />\n <select\n className=\"rounded border bg-bg-surface px-2 py-1 text-sm\"\n value={roleFilter}\n onChange={(e) => setRoleFilter(e.target.value as AdminUserRole | 'all')}\n >\n <option value=\"all\">All Roles</option>\n <option value=\"client\">Client</option>\n <option value=\"provider\">Provider</option>\n <option value=\"admin\">Admin</option>\n <option value=\"team_member\">Team Member</option>\n </select>\n <select\n className=\"rounded border bg-bg-surface px-2 py-1 text-sm\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as AdminUserStatus | 'all')}\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n <option value=\"banned\">Banned</option>\n </select>\n </div>\n </div>\n\n {/* Bulk actions */}\n {selectedIds.size > 0 && (\n <div className=\"flex items-center gap-3 rounded-lg bg-action-primary-bg/5 border border-primary/20 p-3\">\n <span className=\"text-sm font-medium\">{selectedIds.size} user(s) selected</span>\n <Button variant=\"outline\" size=\"sm\">\n Activate\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Suspend\n </Button>\n <Button variant=\"destructive\" size=\"sm\">\n Ban\n </Button>\n </div>\n )}\n\n {/* User table */}\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-text-secondary\">\n <th className=\"p-3\">\n <input\n type=\"checkbox\"\n checked={selectedIds.size === filtered.length && filtered.length > 0}\n onChange={toggleSelectAll}\n className=\"rounded\"\n />\n </th>\n <th className=\"p-3\">User</th>\n <th className=\"p-3\">Role</th>\n <th className=\"p-3\">Status</th>\n <th className=\"p-3\">Verified</th>\n <th className=\"p-3\">Joined</th>\n <th className=\"p-3\">Last Active</th>\n <th className=\"p-3 text-right\">Bookings</th>\n <th className=\"p-3 text-right\">Spent</th>\n <th className=\"p-3\">Rating</th>\n <th className=\"p-3 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filtered.length === 0 ? (\n <tr>\n <td colSpan={11} className=\"p-8 text-center text-text-secondary\">\n No users match your filters.\n </td>\n </tr>\n ) : (\n filtered.map((user) => {\n const sCfg = statusConfig[user.status];\n const StatusIcon = sCfg.icon;\n return (\n <tr key={user.id} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"p-3\">\n <input\n type=\"checkbox\"\n checked={selectedIds.has(user.id)}\n onChange={() => toggleSelect(user.id)}\n className=\"rounded\"\n />\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold text-primary\">\n {user.name.charAt(0)}\n </div>\n <div>\n <p className=\"font-medium\">{user.name}</p>\n <p className=\"text-xs text-text-secondary\">{user.email}</p>\n </div>\n </div>\n </td>\n <td className=\"p-3\">\n <Badge variant={roleConfig[user.role].variant}>\n {roleConfig[user.role].label}\n </Badge>\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-1\">\n <StatusIcon className={`h-4 w-4 ${sCfg.color}`} />\n <span className={`text-xs font-medium ${sCfg.color}`}>\n {sCfg.label}\n </span>\n </div>\n </td>\n <td className=\"p-3\">\n {user.verified ? (\n <CheckCircle2 className=\"h-4 w-4 text-green-500\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-gray-300\" />\n )}\n </td>\n <td className=\"p-3 text-text-secondary\">{user.joinedDate}</td>\n <td className=\"p-3 text-text-secondary\">{user.lastActive}</td>\n <td className=\"p-3 text-right\">{user.bookingsCount}</td>\n <td className=\"p-3 text-right\">\n {user.totalSpent > 0 ? `$${user.totalSpent.toLocaleString()}` : '-'}\n </td>\n <td className=\"p-3\">\n {user.rating ? (\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-yellow-400 text-yellow-400\" />\n <span className=\"text-xs font-medium\">{user.rating}</span>\n </div>\n ) : (\n <span className=\"text-xs text-text-secondary\">-</span>\n )}\n </td>\n <td className=\"p-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button variant=\"outline\" size=\"sm\">\n <Mail className=\"h-3 w-3\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <MoreHorizontal className=\"h-3 w-3\" />\n </Button>\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination hint */}\n <div className=\"mt-4 flex items-center justify-between border-t pt-4 text-sm text-text-secondary\">\n <span>\n Showing {filtered.length} of {adminUsers.length} users\n </span>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\" disabled>\n Previous\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled>\n Next\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -41,7 +41,7 @@ function S() {
|
|
|
41
41
|
className: "text-2xl font-semibold",
|
|
42
42
|
children: "Analytics"
|
|
43
43
|
}), /* @__PURE__ */ m("p", {
|
|
44
|
-
className: "text-sm text-
|
|
44
|
+
className: "text-sm text-text-secondary",
|
|
45
45
|
children: "Insights and performance metrics for your professional services."
|
|
46
46
|
})] }), /* @__PURE__ */ h("div", {
|
|
47
47
|
className: "flex items-center gap-2",
|
|
@@ -54,7 +54,7 @@ function S() {
|
|
|
54
54
|
"1y"
|
|
55
55
|
].map((e) => /* @__PURE__ */ m("button", {
|
|
56
56
|
onClick: () => A(e),
|
|
57
|
-
className: `px-3 py-1 text-xs font-medium ${k === e ? "bg-primary text-primary-
|
|
57
|
+
className: `px-3 py-1 text-xs font-medium ${k === e ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken/50"}`,
|
|
58
58
|
children: e.toUpperCase()
|
|
59
59
|
}, e))
|
|
60
60
|
}), /* @__PURE__ */ h(_, {
|
|
@@ -68,7 +68,7 @@ function S() {
|
|
|
68
68
|
className: "flex gap-1 border-b",
|
|
69
69
|
children: j.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ h("button", {
|
|
70
70
|
onClick: () => O(e),
|
|
71
|
-
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${D === e ? "border-primary text-primary" : "border-transparent text-
|
|
71
|
+
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${D === e ? "border-primary text-primary" : "border-transparent text-text-secondary hover:text-text-primary"}`,
|
|
72
72
|
children: [/* @__PURE__ */ m(n, { className: "h-4 w-4" }), t]
|
|
73
73
|
}, e))
|
|
74
74
|
}),
|
|
@@ -80,7 +80,7 @@ function S() {
|
|
|
80
80
|
/* @__PURE__ */ h("div", {
|
|
81
81
|
className: "flex items-center justify-between",
|
|
82
82
|
children: [/* @__PURE__ */ m("p", {
|
|
83
|
-
className: "text-xs font-medium text-
|
|
83
|
+
className: "text-xs font-medium text-text-secondary",
|
|
84
84
|
children: e.label
|
|
85
85
|
}), M(e.changeType)]
|
|
86
86
|
}),
|
|
@@ -112,16 +112,16 @@ function S() {
|
|
|
112
112
|
className: "flex items-center gap-3",
|
|
113
113
|
children: [
|
|
114
114
|
/* @__PURE__ */ m("span", {
|
|
115
|
-
className: "w-10 text-xs text-
|
|
115
|
+
className: "w-10 text-xs text-text-secondary",
|
|
116
116
|
children: e.name
|
|
117
117
|
}),
|
|
118
118
|
/* @__PURE__ */ h("div", {
|
|
119
|
-
className: "relative flex-1 h-6 bg-
|
|
119
|
+
className: "relative flex-1 h-6 bg-bg-sunken rounded overflow-hidden",
|
|
120
120
|
children: [e.prev !== void 0 && /* @__PURE__ */ m("div", {
|
|
121
121
|
className: "absolute h-6 bg-muted-foreground/10 rounded",
|
|
122
122
|
style: { width: `${e.prev / N * 100}%` }
|
|
123
123
|
}), /* @__PURE__ */ m("div", {
|
|
124
|
-
className: "relative h-6 bg-primary rounded",
|
|
124
|
+
className: "relative h-6 bg-action-primary-bg rounded",
|
|
125
125
|
style: { width: `${e.value / N * 100}%` }
|
|
126
126
|
})]
|
|
127
127
|
}),
|
|
@@ -150,11 +150,11 @@ function S() {
|
|
|
150
150
|
className: "flex items-center gap-3",
|
|
151
151
|
children: [
|
|
152
152
|
/* @__PURE__ */ m("span", {
|
|
153
|
-
className: "w-10 text-xs text-
|
|
153
|
+
className: "w-10 text-xs text-text-secondary",
|
|
154
154
|
children: e.name
|
|
155
155
|
}),
|
|
156
156
|
/* @__PURE__ */ m("div", {
|
|
157
|
-
className: "flex-1 h-6 bg-
|
|
157
|
+
className: "flex-1 h-6 bg-bg-sunken rounded overflow-hidden",
|
|
158
158
|
children: /* @__PURE__ */ m("div", {
|
|
159
159
|
className: "h-6 bg-blue-500 rounded",
|
|
160
160
|
style: { width: `${e.value / P * 100}%` }
|
|
@@ -182,13 +182,13 @@ function S() {
|
|
|
182
182
|
children: [/* @__PURE__ */ h("div", {
|
|
183
183
|
className: "flex items-center gap-4",
|
|
184
184
|
children: [/* @__PURE__ */ m("div", {
|
|
185
|
-
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10",
|
|
185
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10",
|
|
186
186
|
children: /* @__PURE__ */ m(o, { className: "h-5 w-5 text-primary" })
|
|
187
187
|
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("p", {
|
|
188
188
|
className: "font-medium",
|
|
189
189
|
children: [e.name, " 2025"]
|
|
190
190
|
}), /* @__PURE__ */ h("p", {
|
|
191
|
-
className: "text-sm text-
|
|
191
|
+
className: "text-sm text-text-secondary",
|
|
192
192
|
children: [
|
|
193
193
|
"Previous: $",
|
|
194
194
|
((e.prev ?? 0) / 1e3).toFixed(1),
|
|
@@ -240,7 +240,7 @@ function S() {
|
|
|
240
240
|
children: [e.value, " clients"]
|
|
241
241
|
})]
|
|
242
242
|
}), /* @__PURE__ */ m("div", {
|
|
243
|
-
className: "mt-2 h-2 overflow-hidden rounded-full bg-
|
|
243
|
+
className: "mt-2 h-2 overflow-hidden rounded-full bg-bg-sunken",
|
|
244
244
|
children: /* @__PURE__ */ m("div", {
|
|
245
245
|
className: "h-2 rounded-full bg-blue-500",
|
|
246
246
|
style: { width: `${e.value / P * 100}%` }
|
|
@@ -255,7 +255,7 @@ function S() {
|
|
|
255
255
|
})
|
|
256
256
|
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h("div", {
|
|
257
257
|
className: "flex items-center gap-2",
|
|
258
|
-
children: [/* @__PURE__ */ m(l, { className: "h-5 w-5 text-
|
|
258
|
+
children: [/* @__PURE__ */ m(l, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ m(x, {
|
|
259
259
|
className: "text-base",
|
|
260
260
|
children: "Geographic Distribution"
|
|
261
261
|
})]
|
|
@@ -264,7 +264,7 @@ function S() {
|
|
|
264
264
|
children: /* @__PURE__ */ h("table", {
|
|
265
265
|
className: "w-full text-sm",
|
|
266
266
|
children: [/* @__PURE__ */ m("thead", { children: /* @__PURE__ */ h("tr", {
|
|
267
|
-
className: "border-b text-left text-
|
|
267
|
+
className: "border-b text-left text-text-secondary",
|
|
268
268
|
children: [
|
|
269
269
|
/* @__PURE__ */ m("th", {
|
|
270
270
|
className: "p-3",
|
|
@@ -286,7 +286,7 @@ function S() {
|
|
|
286
286
|
}) }), /* @__PURE__ */ m("tbody", { children: E.map((e) => {
|
|
287
287
|
let t = E.reduce((e, t) => e + t.clients, 0), n = Math.round(e.clients / t * 100);
|
|
288
288
|
return /* @__PURE__ */ h("tr", {
|
|
289
|
-
className: "border-b hover:bg-
|
|
289
|
+
className: "border-b hover:bg-bg-sunken/50",
|
|
290
290
|
children: [
|
|
291
291
|
/* @__PURE__ */ m("td", {
|
|
292
292
|
className: "p-3 font-medium",
|
|
@@ -309,13 +309,13 @@ function S() {
|
|
|
309
309
|
children: /* @__PURE__ */ h("div", {
|
|
310
310
|
className: "flex items-center gap-2",
|
|
311
311
|
children: [/* @__PURE__ */ m("div", {
|
|
312
|
-
className: "h-2 flex-1 overflow-hidden rounded-full bg-
|
|
312
|
+
className: "h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken",
|
|
313
313
|
children: /* @__PURE__ */ m("div", {
|
|
314
|
-
className: "h-2 rounded-full bg-primary",
|
|
314
|
+
className: "h-2 rounded-full bg-action-primary-bg",
|
|
315
315
|
style: { width: `${n}%` }
|
|
316
316
|
})
|
|
317
317
|
}), /* @__PURE__ */ h("span", {
|
|
318
|
-
className: "w-10 text-right text-xs text-
|
|
318
|
+
className: "w-10 text-right text-xs text-text-secondary",
|
|
319
319
|
children: [n, "%"]
|
|
320
320
|
})]
|
|
321
321
|
})
|
|
@@ -334,7 +334,7 @@ function S() {
|
|
|
334
334
|
children: [...T].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ h("div", {
|
|
335
335
|
className: "flex items-center gap-4",
|
|
336
336
|
children: [/* @__PURE__ */ h("span", {
|
|
337
|
-
className: "w-6 text-sm font-medium text-
|
|
337
|
+
className: "w-6 text-sm font-medium text-text-secondary",
|
|
338
338
|
children: ["#", t + 1]
|
|
339
339
|
}), /* @__PURE__ */ h("div", {
|
|
340
340
|
className: "flex-1",
|
|
@@ -348,9 +348,9 @@ function S() {
|
|
|
348
348
|
children: e.value
|
|
349
349
|
})]
|
|
350
350
|
}), /* @__PURE__ */ m("div", {
|
|
351
|
-
className: "mt-1 h-3 overflow-hidden rounded-full bg-
|
|
351
|
+
className: "mt-1 h-3 overflow-hidden rounded-full bg-bg-sunken",
|
|
352
352
|
children: /* @__PURE__ */ m("div", {
|
|
353
|
-
className: "h-3 rounded-full bg-primary transition-all",
|
|
353
|
+
className: "h-3 rounded-full bg-action-primary-bg transition-all",
|
|
354
354
|
style: { width: `${e.value / F * 100}%` }
|
|
355
355
|
})
|
|
356
356
|
})]
|
|
@@ -372,7 +372,7 @@ function S() {
|
|
|
372
372
|
className: "font-medium",
|
|
373
373
|
children: e.name
|
|
374
374
|
}), /* @__PURE__ */ h("span", {
|
|
375
|
-
className: "text-
|
|
375
|
+
className: "text-text-secondary",
|
|
376
376
|
children: [
|
|
377
377
|
"$",
|
|
378
378
|
(e.value / 1e3).toFixed(1),
|
|
@@ -384,10 +384,10 @@ function S() {
|
|
|
384
384
|
}), /* @__PURE__ */ h("div", {
|
|
385
385
|
className: "flex gap-1",
|
|
386
386
|
children: [/* @__PURE__ */ m("div", {
|
|
387
|
-
className: "h-4 rounded bg-primary/30",
|
|
387
|
+
className: "h-4 rounded bg-action-primary-bg/30",
|
|
388
388
|
style: { width: `${(e.prev ?? 0) / N * 100}%` }
|
|
389
389
|
}), /* @__PURE__ */ m("div", {
|
|
390
|
-
className: "h-4 rounded bg-primary",
|
|
390
|
+
className: "h-4 rounded bg-action-primary-bg",
|
|
391
391
|
style: { width: `${e.value / N * 100 - (e.prev ?? 0) / N * 100}%` }
|
|
392
392
|
})]
|
|
393
393
|
})]
|
|
@@ -395,10 +395,10 @@ function S() {
|
|
|
395
395
|
className: "mt-4 flex items-center gap-4 text-xs",
|
|
396
396
|
children: [/* @__PURE__ */ h("span", {
|
|
397
397
|
className: "flex items-center gap-1",
|
|
398
|
-
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-primary/30" }), " Previous"]
|
|
398
|
+
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-action-primary-bg/30" }), " Previous"]
|
|
399
399
|
}), /* @__PURE__ */ h("span", {
|
|
400
400
|
className: "flex items-center gap-1",
|
|
401
|
-
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-primary" }), " Current"]
|
|
401
|
+
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-action-primary-bg" }), " Current"]
|
|
402
402
|
})]
|
|
403
403
|
})]
|
|
404
404
|
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
@@ -411,7 +411,7 @@ function S() {
|
|
|
411
411
|
children: [/* @__PURE__ */ h("div", {
|
|
412
412
|
className: "flex items-center gap-3",
|
|
413
413
|
children: [/* @__PURE__ */ m("div", {
|
|
414
|
-
className: `flex h-8 w-8 items-center justify-center rounded-full ${t === 0 ? "bg-yellow-100 text-yellow-700" : t === 1 ? "bg-gray-100 text-gray-600" : t === 2 ? "bg-orange-100 text-orange-600" : "bg-
|
|
414
|
+
className: `flex h-8 w-8 items-center justify-center rounded-full ${t === 0 ? "bg-yellow-100 text-yellow-700" : t === 1 ? "bg-gray-100 text-gray-600" : t === 2 ? "bg-orange-100 text-orange-600" : "bg-bg-sunken text-text-secondary"} text-sm font-bold`,
|
|
415
415
|
children: t + 1
|
|
416
416
|
}), /* @__PURE__ */ m("span", {
|
|
417
417
|
className: "font-medium text-sm",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n DollarSign,\n Users,\n BarChart3,\n Activity,\n Globe,\n Calendar,\n ArrowUpRight,\n ArrowDownRight,\n Download,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype AnalyticsTab = 'overview' | 'revenue' | 'clients' | 'services' | 'performance';\n\nexport function AnalyticsPage() {\n const { analyticsMetrics, revenueData, clientData, servicePerformance, geographicData } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<AnalyticsTab>('overview');\n const [timeRange, setTimeRange] = useState('6m');\n\n const tabs: { key: AnalyticsTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'clients', label: 'Clients', icon: Users },\n { key: 'services', label: 'Services', icon: Activity },\n { key: 'performance', label: 'Performance', icon: TrendingUp },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive') return <TrendingUp className=\"h-4 w-4 text-green-500\" />;\n if (type === 'negative') return <TrendingDown className=\"h-4 w-4 text-red-500\" />;\n return <Minus className=\"h-4 w-4 text-gray-400\" />;\n };\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxClients = Math.max(...clientData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n Insights and performance metrics for your professional services.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex rounded border\">\n {['1m', '3m', '6m', '1y'].map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n className={`px-3 py-1 text-xs font-medium ${\n timeRange === range\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted/50'\n }`}\n >\n {range.toUpperCase()}\n </button>\n ))}\n </div>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export\n </Button>\n </div>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </div>\n\n {/* Metric Cards (shown on all tabs) */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n {analyticsMetrics.map((metric, i) => (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs font-medium text-muted-foreground\">{metric.label}</p>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-1 text-2xl font-bold\">{metric.value}</p>\n <p\n className={`mt-0.5 text-xs ${\n metric.changeType === 'positive'\n ? 'text-green-600'\n : metric.changeType === 'negative'\n ? 'text-red-600'\n : 'text-gray-500'\n }`}\n >\n {metric.change} from last period\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue chart */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Revenue Trend</CardTitle>\n <Badge variant=\"secondary\">Last 6 months</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-muted-foreground\">{point.name}</span>\n <div className=\"relative flex-1 h-6 bg-muted rounded overflow-hidden\">\n {point.prev !== undefined && (\n <div\n className=\"absolute h-6 bg-muted-foreground/10 rounded\"\n style={{ width: `${(point.prev / maxRevenue) * 100}%` }}\n />\n )}\n <div\n className=\"relative h-6 bg-primary rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-20 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Client growth */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Client Growth</CardTitle>\n <Badge variant=\"secondary\">Monthly</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-muted-foreground\">{point.name}</span>\n <div className=\"flex-1 h-6 bg-muted rounded overflow-hidden\">\n <div\n className=\"h-6 bg-blue-500 rounded\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{point.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue Breakdown</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {revenueData.map((point) => {\n const growth = point.prev\n ? (((point.value - point.prev) / point.prev) * 100).toFixed(1)\n : '0';\n const isPositive = point.prev ? point.value >= point.prev : true;\n return (\n <div\n key={point.name}\n className=\"flex items-center justify-between rounded-lg border p-4\"\n >\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10\">\n <Calendar className=\"h-5 w-5 text-primary\" />\n </div>\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <p className=\"text-sm text-muted-foreground\">\n Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-lg font-bold\">${(point.value / 1000).toFixed(1)}k</p>\n <p\n className={`flex items-center gap-1 text-xs ${isPositive ? 'text-green-600' : 'text-red-600'}`}\n >\n {isPositive ? (\n <ArrowUpRight className=\"h-3 w-3\" />\n ) : (\n <ArrowDownRight className=\"h-3 w-3\" />\n )}\n {growth}%\n </p>\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Clients Tab */}\n {activeTab === 'clients' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Client Acquisition</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point, idx) => {\n const prev = idx > 0 ? (clientData[idx - 1]?.value ?? 0) : 0;\n const growth = prev > 0 ? (((point.value - prev) / prev) * 100).toFixed(0) : 'N/A';\n return (\n <div key={point.name} className=\"flex items-center gap-4 rounded-lg border p-3\">\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-lg font-bold\">{point.value} clients</span>\n </div>\n <div className=\"mt-2 h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-blue-500\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n </div>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {typeof growth === 'string' ? growth : `+${growth}%`}\n </Badge>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Geographic data */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Globe className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-base\">Geographic Distribution</CardTitle>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-muted-foreground\">\n <th className=\"p-3\">Country</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3\">Distribution</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalClients = geographicData.reduce((s, g) => s + g.clients, 0);\n const pct = Math.round((geo.clients / totalClients) * 100);\n return (\n <tr key={geo.country} className=\"border-b hover:bg-muted/50\">\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right\">{geo.clients}</td>\n <td className=\"p-3 text-right\">${(geo.revenue / 1000).toFixed(0)}k</td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs text-muted-foreground\">\n {pct}%\n </span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Services Tab */}\n {activeTab === 'services' && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Service Performance (Total Bookings)</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service, idx) => (\n <div key={service.name} className=\"flex items-center gap-4\">\n <span className=\"w-6 text-sm font-medium text-muted-foreground\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{service.name}</span>\n <span className=\"text-sm font-bold\">{service.value}</span>\n </div>\n <div className=\"mt-1 h-3 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-3 rounded-full bg-primary transition-all\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n )}\n\n {/* Performance Tab */}\n {activeTab === 'performance' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue vs Previous</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-muted-foreground\">\n ${(point.value / 1000).toFixed(1)}k vs $\n {((point.prev ?? 0) / 1000).toFixed(1)}k\n </span>\n </div>\n <div className=\"flex gap-1\">\n <div\n className=\"h-4 rounded bg-primary/30\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-primary\"\n style={{\n width: `${(point.value / maxRevenue) * 100 - ((point.prev ?? 0) / maxRevenue) * 100}%`,\n }}\n />\n </div>\n </div>\n ))}\n <div className=\"mt-4 flex items-center gap-4 text-xs\">\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-primary/30\" /> Previous\n </span>\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-primary\" /> Current\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Top Performing Services</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .slice(0, 5)\n .map((service, idx) => (\n <div\n key={service.name}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-8 w-8 items-center justify-center rounded-full ${\n idx === 0\n ? 'bg-yellow-100 text-yellow-700'\n : idx === 1\n ? 'bg-gray-100 text-gray-600'\n : idx === 2\n ? 'bg-orange-100 text-orange-600'\n : 'bg-muted text-muted-foreground'\n } text-sm font-bold`}\n >\n {idx + 1}\n </div>\n <span className=\"font-medium text-sm\">{service.name}</span>\n </div>\n <Badge variant=\"secondary\">{service.value} bookings</Badge>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n DollarSign,\n Users,\n BarChart3,\n Activity,\n Globe,\n Calendar,\n ArrowUpRight,\n ArrowDownRight,\n Download,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype AnalyticsTab = 'overview' | 'revenue' | 'clients' | 'services' | 'performance';\n\nexport function AnalyticsPage() {\n const { analyticsMetrics, revenueData, clientData, servicePerformance, geographicData } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<AnalyticsTab>('overview');\n const [timeRange, setTimeRange] = useState('6m');\n\n const tabs: { key: AnalyticsTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'clients', label: 'Clients', icon: Users },\n { key: 'services', label: 'Services', icon: Activity },\n { key: 'performance', label: 'Performance', icon: TrendingUp },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive') return <TrendingUp className=\"h-4 w-4 text-green-500\" />;\n if (type === 'negative') return <TrendingDown className=\"h-4 w-4 text-red-500\" />;\n return <Minus className=\"h-4 w-4 text-gray-400\" />;\n };\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxClients = Math.max(...clientData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Analytics</h1>\n <p className=\"text-sm text-text-secondary\">\n Insights and performance metrics for your professional services.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex rounded border\">\n {['1m', '3m', '6m', '1y'].map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n className={`px-3 py-1 text-xs font-medium ${\n timeRange === range\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken/50'\n }`}\n >\n {range.toUpperCase()}\n </button>\n ))}\n </div>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export\n </Button>\n </div>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </div>\n\n {/* Metric Cards (shown on all tabs) */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n {analyticsMetrics.map((metric, i) => (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs font-medium text-text-secondary\">{metric.label}</p>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-1 text-2xl font-bold\">{metric.value}</p>\n <p\n className={`mt-0.5 text-xs ${\n metric.changeType === 'positive'\n ? 'text-green-600'\n : metric.changeType === 'negative'\n ? 'text-red-600'\n : 'text-gray-500'\n }`}\n >\n {metric.change} from last period\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue chart */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Revenue Trend</CardTitle>\n <Badge variant=\"secondary\">Last 6 months</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-text-secondary\">{point.name}</span>\n <div className=\"relative flex-1 h-6 bg-bg-sunken rounded overflow-hidden\">\n {point.prev !== undefined && (\n <div\n className=\"absolute h-6 bg-muted-foreground/10 rounded\"\n style={{ width: `${(point.prev / maxRevenue) * 100}%` }}\n />\n )}\n <div\n className=\"relative h-6 bg-action-primary-bg rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-20 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Client growth */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Client Growth</CardTitle>\n <Badge variant=\"secondary\">Monthly</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-text-secondary\">{point.name}</span>\n <div className=\"flex-1 h-6 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-6 bg-blue-500 rounded\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{point.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue Breakdown</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-4\">\n {revenueData.map((point) => {\n const growth = point.prev\n ? (((point.value - point.prev) / point.prev) * 100).toFixed(1)\n : '0';\n const isPositive = point.prev ? point.value >= point.prev : true;\n return (\n <div\n key={point.name}\n className=\"flex items-center justify-between rounded-lg border p-4\"\n >\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10\">\n <Calendar className=\"h-5 w-5 text-primary\" />\n </div>\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <p className=\"text-sm text-text-secondary\">\n Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-lg font-bold\">${(point.value / 1000).toFixed(1)}k</p>\n <p\n className={`flex items-center gap-1 text-xs ${isPositive ? 'text-green-600' : 'text-red-600'}`}\n >\n {isPositive ? (\n <ArrowUpRight className=\"h-3 w-3\" />\n ) : (\n <ArrowDownRight className=\"h-3 w-3\" />\n )}\n {growth}%\n </p>\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Clients Tab */}\n {activeTab === 'clients' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Client Acquisition</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point, idx) => {\n const prev = idx > 0 ? (clientData[idx - 1]?.value ?? 0) : 0;\n const growth = prev > 0 ? (((point.value - prev) / prev) * 100).toFixed(0) : 'N/A';\n return (\n <div key={point.name} className=\"flex items-center gap-4 rounded-lg border p-3\">\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-lg font-bold\">{point.value} clients</span>\n </div>\n <div className=\"mt-2 h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-blue-500\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n </div>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {typeof growth === 'string' ? growth : `+${growth}%`}\n </Badge>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Geographic data */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Globe className=\"h-5 w-5 text-text-secondary\" />\n <CardTitle className=\"text-base\">Geographic Distribution</CardTitle>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-text-secondary\">\n <th className=\"p-3\">Country</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3\">Distribution</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalClients = geographicData.reduce((s, g) => s + g.clients, 0);\n const pct = Math.round((geo.clients / totalClients) * 100);\n return (\n <tr key={geo.country} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right\">{geo.clients}</td>\n <td className=\"p-3 text-right\">${(geo.revenue / 1000).toFixed(0)}k</td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs text-text-secondary\">\n {pct}%\n </span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Services Tab */}\n {activeTab === 'services' && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Service Performance (Total Bookings)</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service, idx) => (\n <div key={service.name} className=\"flex items-center gap-4\">\n <span className=\"w-6 text-sm font-medium text-text-secondary\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{service.name}</span>\n <span className=\"text-sm font-bold\">{service.value}</span>\n </div>\n <div className=\"mt-1 h-3 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-3 rounded-full bg-action-primary-bg transition-all\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n )}\n\n {/* Performance Tab */}\n {activeTab === 'performance' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue vs Previous</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-text-secondary\">\n ${(point.value / 1000).toFixed(1)}k vs $\n {((point.prev ?? 0) / 1000).toFixed(1)}k\n </span>\n </div>\n <div className=\"flex gap-1\">\n <div\n className=\"h-4 rounded bg-action-primary-bg/30\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{\n width: `${(point.value / maxRevenue) * 100 - ((point.prev ?? 0) / maxRevenue) * 100}%`,\n }}\n />\n </div>\n </div>\n ))}\n <div className=\"mt-4 flex items-center gap-4 text-xs\">\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-action-primary-bg/30\" /> Previous\n </span>\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-action-primary-bg\" /> Current\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Top Performing Services</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .slice(0, 5)\n .map((service, idx) => (\n <div\n key={service.name}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-8 w-8 items-center justify-center rounded-full ${\n idx === 0\n ? 'bg-yellow-100 text-yellow-700'\n : idx === 1\n ? 'bg-gray-100 text-gray-600'\n : idx === 2\n ? 'bg-orange-100 text-orange-600'\n : 'bg-bg-sunken text-text-secondary'\n } text-sm font-bold`}\n >\n {idx + 1}\n </div>\n <span className=\"font-medium text-sm\">{service.name}</span>\n </div>\n <Badge variant=\"secondary\">{service.value} bookings</Badge>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|