@burdenoff/microfe-proserviceworld 2026.624.1 → 2026.625.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 +365 -332
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +257 -237
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +343 -315
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/BookingsPage.js +191 -174
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js +383 -346
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +383 -318
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MessagesPage.js +70 -75
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +367 -340
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +428 -381
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +390 -346
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +245 -244
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +14 -14
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +338 -305
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServicesPage.js +156 -153
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/VerificationPage.js +260 -251
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminReportsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminReportsPage.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 Download,\n FileText,\n BarChart3,\n PieChart,\n Calendar,\n Filter,\n Users,\n DollarSign,\n ShoppingCart,\n Star,\n AlertTriangle,\n ArrowUpRight,\n Shield,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype ReportTab = 'overview' | 'users' | 'revenue' | 'bookings';\ntype TimeRange = '7d' | '30d' | '90d' | '1y';\n\nexport function AdminReportsPage() {\n const { adminReportMetrics, revenueData, servicePerformance, geographicData, adminUsers } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<ReportTab>('overview');\n const [timeRange, setTimeRange] = useState<TimeRange>('30d');\n\n const tabs: { key: ReportTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'bookings', label: 'Bookings', icon: ShoppingCart },\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 metricIcons = [Users, Shield, ShoppingCart, DollarSign, Star, AlertTriangle];\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n // Role distribution\n const roleCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.role] = (acc[u.role] ?? 0) + 1;\n return acc;\n }, {});\n\n // Status distribution\n const statusCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.status] = (acc[u.status] ?? 0) + 1;\n return acc;\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\">Admin Reports</h1>\n <p className=\"text-sm text-text-secondary\">\n Platform analytics, performance metrics, and operational reports.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <FileText className=\"mr-2 h-4 w-4\" /> Generate Report\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export CSV\n </Button>\n </div>\n </div>\n\n {/* Time Range + Tabs */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\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 <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-text-secondary\" />\n <div className=\"flex rounded border\">\n {(['7d', '30d', '90d', '1y'] as TimeRange[]).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}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {/* Metric Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {adminReportMetrics.map((metric, i) => {\n const MetricIcon = metricIcons[i % metricIcons.length] ?? BarChart3;\n return (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MetricIcon className=\"h-4 w-4 text-text-secondary\" />\n <p className=\"text-sm font-medium text-text-secondary\">{metric.label}</p>\n </div>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-2 text-3xl font-bold\">{metric.value}</p>\n <p\n className={`mt-1 flex items-center gap-1 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 <ArrowUpRight className=\"h-3 w-3\" />\n {metric.change} vs last period\n </p>\n </CardContent>\n </Card>\n );\n })}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue trend */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Platform Revenue</CardTitle>\n <Badge variant=\"secondary\">Monthly</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=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-action-primary-bg rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Service breakdown */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Service Bookings</CardTitle>\n <Badge variant=\"secondary\">Total</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-32 truncate text-xs text-text-secondary\">\n {service.name}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-blue-500 rounded\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{service.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Geographic revenue */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue by Region</CardTitle>\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\">Region</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3 text-right\">Avg. per Client</th>\n <th className=\"p-3\">Share</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalRevenue = geographicData.reduce((s, g) => s + g.revenue, 0);\n const pct = Math.round((geo.revenue / totalRevenue) * 100);\n const avgPerClient = Math.round(geo.revenue / geo.clients);\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 font-medium\">\n ${(geo.revenue / 1000).toFixed(0)}k\n </td>\n <td className=\"p-3 text-right text-text-secondary\">\n ${avgPerClient.toLocaleString()}\n </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\">{pct}%</span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Role Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(roleCounts).map(([role, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const roleColors: Record<string, string> = {\n client: 'bg-blue-500',\n provider: 'bg-green-500',\n admin: 'bg-red-500',\n team_member: 'bg-purple-500',\n };\n return (\n <div key={role} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">\n {role.replace('_', ' ')}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${roleColors[role] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusCounts).map(([status, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const statusColors: Record<string, string> = {\n active: 'bg-green-500',\n suspended: 'bg-orange-500',\n pending: 'bg-yellow-500',\n banned: 'bg-red-500',\n };\n return (\n <div key={status} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">{status}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${statusColors[status] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Top users by spend */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Top Users by Spending</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {[...adminUsers]\n .filter((u) => u.totalSpent > 0)\n .sort((a, b) => b.totalSpent - a.totalSpent)\n .slice(0, 5)\n .map((user, idx) => (\n <div\n key={user.id}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-bold text-primary\">\n {idx + 1}\n </span>\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 <div className=\"text-right\">\n <p className=\"font-bold\">${user.totalSpent.toLocaleString()}</p>\n <p className=\"text-xs text-text-secondary\">\n {user.bookingsCount} bookings\n </p>\n </div>\n </div>\n ))}\n </div>\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\">Monthly Revenue Comparison</CardTitle>\n </CardHeader>\n <CardContent 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 key={point.name} className=\"rounded-lg border p-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <div className=\"mt-1 flex items-center gap-4 text-sm text-text-secondary\">\n <span>Current: ${(point.value / 1000).toFixed(1)}k</span>\n <span>Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k</span>\n </div>\n </div>\n <Badge variant={isPositive ? 'default' : 'destructive'}>\n {isPositive ? '+' : ''}\n {growth}%\n </Badge>\n </div>\n <div className=\"mt-3 flex gap-1 h-4\">\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: `${Math.max(0, ((point.value - (point.prev ?? 0)) / maxRevenue) * 100)}%`,\n }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Bookings Tab */}\n {activeTab === 'bookings' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Bookings by Service Category</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-3\">\n <span className=\"w-6 text-sm font-bold text-text-secondary\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{service.name}</span>\n <span>{service.value} bookings</span>\n </div>\n <div className=\"mt-1 h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Quick Stats</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {[\n {\n label: 'Total Bookings',\n value: servicePerformance.reduce((s, p) => s + p.value, 0).toLocaleString(),\n icon: ShoppingCart,\n },\n { label: 'Avg. Booking Value', value: '$3,804', icon: DollarSign },\n { label: 'Completion Rate', value: '96.5%', icon: TrendingUp },\n { label: 'Client Satisfaction', value: '4.7/5.0', icon: Star },\n ].map(({ label, value, icon: Icon }) => (\n <div\n key={label}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <Icon className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-sm font-medium\">{label}</span>\n </div>\n <span className=\"text-lg font-bold\">{value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminReportsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminReportsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n AuroraBackground,\n GradientText,\n CTAOverflowMenu,\n} from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n Download,\n FileText,\n BarChart3,\n Calendar,\n Users,\n DollarSign,\n ShoppingCart,\n Star,\n AlertTriangle,\n ArrowUpRight,\n Shield,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype ReportTab = 'overview' | 'users' | 'revenue' | 'bookings';\ntype TimeRange = '7d' | '30d' | '90d' | '1y';\n\nexport function AdminReportsPage() {\n const { adminReportMetrics, revenueData, servicePerformance, geographicData, adminUsers } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<ReportTab>('overview');\n const [timeRange, setTimeRange] = useState<TimeRange>('30d');\n\n const tabs: { key: ReportTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'bookings', label: 'Bookings', icon: ShoppingCart },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive')\n return <TrendingUp className=\"h-4 w-4 text-status-success-text\" />;\n if (type === 'negative')\n return <TrendingDown className=\"h-4 w-4 text-status-error-text\" />;\n return <Minus className=\"h-4 w-4 text-text-secondary\" />;\n };\n\n const changeColor = (type: string) =>\n type === 'positive'\n ? 'text-status-success-text'\n : type === 'negative'\n ? 'text-status-error-text'\n : 'text-text-secondary';\n\n const metricIcons = [Users, Shield, ShoppingCart, DollarSign, Star, AlertTriangle];\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n // Role distribution\n const roleCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.role] = (acc[u.role] ?? 0) + 1;\n return acc;\n }, {});\n\n // Status distribution\n const statusCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.status] = (acc[u.status] ?? 0) + 1;\n return acc;\n }, {});\n\n const roleColors: Record<string, string> = {\n client: 'bg-chart-1',\n provider: 'bg-chart-2',\n admin: 'bg-chart-3',\n team_member: 'bg-chart-4',\n };\n\n const statusColors: Record<string, string> = {\n active: 'bg-status-success-text',\n suspended: 'bg-status-warning-text',\n pending: 'bg-chart-5',\n banned: 'bg-status-error-text',\n };\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n {/* Hero header — the ONE aurora backdrop for this page. */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\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 tracking-tight\">\n <GradientText>Admin Reports</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Platform analytics, performance metrics, and operational reports.\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: 'Generate Report',\n onSelect: () => {},\n icon: <FileText className=\"h-4 w-4\" />,\n }}\n actions={[\n {\n label: 'Export CSV',\n onSelect: () => {},\n icon: <Download className=\"h-4 w-4\" />,\n },\n ]}\n />\n </div>\n </div>\n\n {/* Time Range + Tabs */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex gap-1 border-b border-border-seam\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n aria-pressed={activeTab === key}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n activeTab === key\n ? 'border-accent-blue text-accent-blue'\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 <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-text-secondary\" />\n <div className=\"flex rounded border border-border-seam\">\n {(['7d', '30d', '90d', '1y'] as TimeRange[]).map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n aria-pressed={timeRange === range}\n className={`px-3 py-1 text-xs font-medium tabular-nums focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n timeRange === range\n ? 'bg-accent-blue text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken'\n }`}\n >\n {range}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {/* Metric Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {adminReportMetrics.map((metric, i) => {\n const MetricIcon = metricIcons[i % metricIcons.length] ?? BarChart3;\n return (\n <GlassCard key={i} treatment=\"metric\" className=\"border-border-seam\">\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MetricIcon className=\"h-4 w-4 text-text-secondary\" />\n <p className=\"text-sm font-medium text-text-secondary\">{metric.label}</p>\n </div>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-2 text-3xl font-bold tabular-nums\">{metric.value}</p>\n <p className={`mt-1 flex items-center gap-1 text-xs ${changeColor(metric.changeType)}`}>\n <ArrowUpRight className=\"h-3 w-3\" />\n {metric.change} vs last period\n </p>\n </CardContent>\n </GlassCard>\n );\n })}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue trend */}\n <Card className=\"border-border-seam\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Platform Revenue</CardTitle>\n <Badge variant=\"secondary\">Monthly</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=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-accent-blue rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-xs font-medium tabular-nums\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Service breakdown */}\n <Card className=\"border-border-seam\">\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Service Bookings</CardTitle>\n <Badge variant=\"secondary\">Total</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-32 truncate text-xs text-text-secondary\">\n {service.name}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-accent-purple rounded\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium tabular-nums\">\n {service.value}\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Geographic revenue */}\n <Card className=\"lg:col-span-2 border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue by Region</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-seam text-left text-text-secondary\">\n <th className=\"p-3\">Region</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3 text-right\">Avg. per Client</th>\n <th className=\"p-3\">Share</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalRevenue = geographicData.reduce((s, g) => s + g.revenue, 0);\n const pct = Math.round((geo.revenue / totalRevenue) * 100);\n const avgPerClient = Math.round(geo.revenue / geo.clients);\n return (\n <tr\n key={geo.country}\n className=\"border-b border-border-seam hover:bg-bg-sunken\"\n >\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right tabular-nums\">{geo.clients}</td>\n <td className=\"p-3 text-right font-medium tabular-nums\">\n ${(geo.revenue / 1000).toFixed(0)}k\n </td>\n <td className=\"p-3 text-right text-text-secondary tabular-nums\">\n ${avgPerClient.toLocaleString()}\n </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-accent-blue\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs tabular-nums\">{pct}%</span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">User Role Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(roleCounts).map(([role, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n return (\n <div key={role} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">\n {role.replace('_', ' ')}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${roleColors[role] ?? 'bg-chart-6'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm tabular-nums\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">User Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusCounts).map(([status, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n return (\n <div key={status} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">{status}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${statusColors[status] ?? 'bg-chart-6'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm tabular-nums\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Top users by spend */}\n <Card className=\"lg:col-span-2 border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">Top Users by Spending</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {[...adminUsers]\n .filter((u) => u.totalSpent > 0)\n .sort((a, b) => b.totalSpent - a.totalSpent)\n .slice(0, 5)\n .map((user, idx) => (\n <div\n key={user.id}\n className=\"flex items-center justify-between rounded-lg border border-border-seam p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-accent-soft text-sm font-bold text-text-primary tabular-nums\">\n {idx + 1}\n </span>\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 <div className=\"text-right\">\n <p className=\"font-bold tabular-nums\">\n ${user.totalSpent.toLocaleString()}\n </p>\n <p className=\"text-xs text-text-secondary tabular-nums\">\n {user.bookingsCount} bookings\n </p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue Comparison</CardTitle>\n </CardHeader>\n <CardContent 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 key={point.name} className=\"rounded-lg border border-border-seam p-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <div className=\"mt-1 flex items-center gap-4 text-sm text-text-secondary tabular-nums\">\n <span>Current: ${(point.value / 1000).toFixed(1)}k</span>\n <span>Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k</span>\n </div>\n </div>\n <Badge variant={isPositive ? 'default' : 'destructive'}>\n {isPositive ? '+' : ''}\n {growth}%\n </Badge>\n </div>\n <div className=\"mt-3 flex gap-1 h-4\">\n <div\n className=\"h-4 rounded bg-accent-blue-subtle\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-accent-blue\"\n style={{\n width: `${Math.max(0, ((point.value - (point.prev ?? 0)) / maxRevenue) * 100)}%`,\n }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Bookings Tab */}\n {activeTab === 'bookings' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">Bookings by Service Category</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-3\">\n <span className=\"w-6 text-sm font-bold text-text-secondary tabular-nums\">\n #{idx + 1}\n </span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{service.name}</span>\n <span className=\"tabular-nums\">{service.value} bookings</span>\n </div>\n <div className=\"mt-1 h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-accent-blue\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle className=\"text-base\">Quick Stats</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {[\n {\n label: 'Total Bookings',\n value: servicePerformance.reduce((s, p) => s + p.value, 0).toLocaleString(),\n icon: ShoppingCart,\n },\n { label: 'Avg. Booking Value', value: '$3,804', icon: DollarSign },\n { label: 'Completion Rate', value: '96.5%', icon: TrendingUp },\n { label: 'Client Satisfaction', value: '4.7/5.0', icon: Star },\n ].map(({ label, value, icon: Icon }) => (\n <div\n key={label}\n className=\"flex items-center justify-between rounded-lg border border-border-seam p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <Icon className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-sm font-medium\">{label}</span>\n </div>\n <span className=\"text-lg font-bold tabular-nums\">{value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|