create-bluecopa-react-app 1.0.5 → 1.0.7

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.
Files changed (117) hide show
  1. package/README.md +47 -10
  2. package/bin/create-bluecopa-react-app.js +257 -51
  3. package/package.json +6 -5
  4. package/templates/latest/Agent.md +254 -0
  5. package/templates/latest/Dockerfile +22 -0
  6. package/templates/latest/README.md +157 -221
  7. package/templates/latest/app/app.css +134 -0
  8. package/templates/latest/app/app.tsx +46 -0
  9. package/templates/latest/app/components/app-sidebar.tsx +174 -0
  10. package/templates/latest/app/components/chart-area-interactive.tsx +290 -0
  11. package/templates/latest/app/components/data-table.tsx +807 -0
  12. package/templates/latest/app/components/nav-documents.tsx +92 -0
  13. package/templates/latest/app/components/nav-main.tsx +56 -0
  14. package/templates/latest/app/components/nav-secondary.tsx +42 -0
  15. package/templates/latest/app/components/nav-user.tsx +112 -0
  16. package/templates/latest/app/components/section-cards.tsx +102 -0
  17. package/templates/latest/app/components/site-header.tsx +19 -0
  18. package/templates/latest/app/components/ui/avatar.tsx +53 -0
  19. package/templates/latest/app/components/ui/badge.tsx +46 -0
  20. package/templates/latest/app/components/ui/breadcrumb.tsx +109 -0
  21. package/templates/latest/app/components/ui/button.tsx +58 -0
  22. package/templates/latest/app/components/ui/card.tsx +92 -0
  23. package/templates/latest/app/components/ui/chart.tsx +352 -0
  24. package/templates/latest/app/components/ui/checkbox.tsx +30 -0
  25. package/templates/latest/app/components/ui/drawer.tsx +139 -0
  26. package/templates/latest/app/components/ui/dropdown-menu.tsx +258 -0
  27. package/templates/latest/app/components/ui/input.tsx +21 -0
  28. package/templates/latest/app/components/ui/label.tsx +24 -0
  29. package/templates/latest/app/components/ui/select.tsx +183 -0
  30. package/templates/latest/app/components/ui/separator.tsx +26 -0
  31. package/templates/latest/app/components/ui/sheet.tsx +139 -0
  32. package/templates/latest/app/components/ui/sidebar.tsx +731 -0
  33. package/templates/latest/app/components/ui/skeleton.tsx +13 -0
  34. package/templates/latest/app/components/ui/sonner.tsx +23 -0
  35. package/templates/latest/app/components/ui/table.tsx +117 -0
  36. package/templates/latest/app/components/ui/tabs.tsx +66 -0
  37. package/templates/latest/app/components/ui/toggle-group.tsx +73 -0
  38. package/templates/latest/app/components/ui/toggle.tsx +47 -0
  39. package/templates/latest/app/components/ui/tooltip.tsx +59 -0
  40. package/templates/latest/app/dashboard/data.json +614 -0
  41. package/templates/latest/app/hooks/use-bluecopa-user.ts +37 -0
  42. package/templates/latest/app/hooks/use-mobile.ts +19 -0
  43. package/templates/latest/{src → app}/lib/utils.ts +1 -1
  44. package/templates/latest/app/main.tsx +12 -0
  45. package/templates/latest/app/routes/home.tsx +40 -0
  46. package/templates/latest/app/routes.tsx +15 -0
  47. package/templates/latest/{src → app}/single-spa.tsx +5 -5
  48. package/templates/latest/components.json +22 -0
  49. package/templates/latest/dist/assets/__federation_expose_App-DsPovvoo.js +147 -0
  50. package/templates/latest/dist/assets/__federation_fn_import-CzfA7kmP.js +438 -0
  51. package/templates/latest/dist/assets/__federation_shared_react-Bp6HVBS4.js +16 -0
  52. package/templates/latest/dist/assets/__federation_shared_react-dom-BCcRGiYp.js +17 -0
  53. package/templates/latest/dist/assets/client-BZh_TW_6.js +12662 -0
  54. package/templates/latest/dist/assets/home-CAuoIW4B.js +54951 -0
  55. package/templates/latest/dist/assets/index-BzNimew1.js +69 -0
  56. package/templates/latest/dist/assets/index-Clg7n7gy.js +60 -0
  57. package/templates/latest/dist/assets/index-DMFtQdNS.js +412 -0
  58. package/templates/latest/dist/assets/remoteEntry.css +3688 -0
  59. package/templates/latest/dist/assets/remoteEntry.js +88 -0
  60. package/templates/latest/dist/avatars/shadcn.svg +6 -0
  61. package/templates/latest/dist/favicon.ico +0 -0
  62. package/templates/latest/dist/index.html +19 -0
  63. package/templates/latest/index.html +1 -1
  64. package/templates/latest/package-lock.json +1227 -3353
  65. package/templates/latest/package.json +47 -43
  66. package/templates/latest/pnpm-lock.yaml +4767 -0
  67. package/templates/latest/preview/index.html +32 -2
  68. package/templates/latest/public/avatars/shadcn.svg +6 -0
  69. package/templates/latest/public/favicon.ico +0 -0
  70. package/templates/latest/tsconfig.json +19 -12
  71. package/templates/latest/vite.config.ts +47 -40
  72. package/templates/latest/.env.example +0 -14
  73. package/templates/latest/.eslintrc.cjs +0 -42
  74. package/templates/latest/AGENT.md +0 -282
  75. package/templates/latest/clean.sh +0 -40
  76. package/templates/latest/postcss.config.cjs +0 -6
  77. package/templates/latest/public/bluecopa-logo.svg +0 -30
  78. package/templates/latest/public/favicon-32x32.png +0 -0
  79. package/templates/latest/public/favicon-96x96.png +0 -0
  80. package/templates/latest/setup.sh +0 -56
  81. package/templates/latest/src/App.tsx +0 -19
  82. package/templates/latest/src/components/charts/AreaChart.tsx +0 -80
  83. package/templates/latest/src/components/charts/DonutChart.tsx +0 -73
  84. package/templates/latest/src/components/charts/SparkAreaChart.tsx +0 -52
  85. package/templates/latest/src/components/layout/dashboard-header.tsx +0 -139
  86. package/templates/latest/src/components/layout/dashboard-layout.tsx +0 -37
  87. package/templates/latest/src/components/layout/navbar.tsx +0 -106
  88. package/templates/latest/src/components/layout/sidebar.tsx +0 -55
  89. package/templates/latest/src/components/page/dashboard/DashboardMetrics.tsx +0 -97
  90. package/templates/latest/src/components/page/dashboard/PaymentMethodsAnalysis.tsx +0 -182
  91. package/templates/latest/src/components/page/dashboard/RevenueAnalytics.tsx +0 -505
  92. package/templates/latest/src/components/page/dashboard/SalesAnalytics.tsx +0 -313
  93. package/templates/latest/src/components/page/dashboard/TransactionsTable.tsx +0 -256
  94. package/templates/latest/src/components/page/dashboard/dashboard-utils.ts +0 -147
  95. package/templates/latest/src/components/page/dashboard/dashboard.tsx +0 -185
  96. package/templates/latest/src/components/tables/data-grid.tsx +0 -439
  97. package/templates/latest/src/components/ui/alert.tsx +0 -59
  98. package/templates/latest/src/components/ui/avatar.tsx +0 -50
  99. package/templates/latest/src/components/ui/badge.tsx +0 -36
  100. package/templates/latest/src/components/ui/bluecopa-logo.tsx +0 -57
  101. package/templates/latest/src/components/ui/button.tsx +0 -58
  102. package/templates/latest/src/components/ui/card.tsx +0 -79
  103. package/templates/latest/src/components/ui/dropdown-menu.tsx +0 -200
  104. package/templates/latest/src/components/ui/input.tsx +0 -24
  105. package/templates/latest/src/components/ui/label.tsx +0 -21
  106. package/templates/latest/src/components/ui/select.tsx +0 -27
  107. package/templates/latest/src/hooks/use-api.ts +0 -55
  108. package/templates/latest/src/index.css +0 -59
  109. package/templates/latest/src/main.tsx +0 -13
  110. package/templates/latest/src/pages/Dashboard.tsx +0 -13
  111. package/templates/latest/src/pages/Home.tsx +0 -622
  112. package/templates/latest/src/providers/query-provider.tsx +0 -48
  113. package/templates/latest/src/types/api.ts +0 -78
  114. package/templates/latest/src/vite-env.d.ts +0 -11
  115. package/templates/latest/tailwind.config.js +0 -87
  116. package/templates/latest/tsconfig.app.json +0 -32
  117. package/templates/latest/tsconfig.node.json +0 -14
@@ -0,0 +1,134 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme {
7
+ --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
8
+ "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
9
+ }
10
+
11
+ html,
12
+ body {
13
+ @apply bg-white dark:bg-gray-950;
14
+
15
+ @media (prefers-color-scheme: dark) {
16
+ color-scheme: dark;
17
+ }
18
+ }
19
+
20
+ @theme inline {
21
+ --radius-sm: calc(var(--radius) - 4px);
22
+ --radius-md: calc(var(--radius) - 2px);
23
+ --radius-lg: var(--radius);
24
+ --radius-xl: calc(var(--radius) + 4px);
25
+ --color-background: var(--background);
26
+ --color-foreground: var(--foreground);
27
+ --color-card: var(--card);
28
+ --color-card-foreground: var(--card-foreground);
29
+ --color-popover: var(--popover);
30
+ --color-popover-foreground: var(--popover-foreground);
31
+ --color-primary: var(--primary);
32
+ --color-primary-foreground: var(--primary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-muted: var(--muted);
36
+ --color-muted-foreground: var(--muted-foreground);
37
+ --color-accent: var(--accent);
38
+ --color-accent-foreground: var(--accent-foreground);
39
+ --color-destructive: var(--destructive);
40
+ --color-border: var(--border);
41
+ --color-input: var(--input);
42
+ --color-ring: var(--ring);
43
+ --color-chart-1: var(--chart-1);
44
+ --color-chart-2: var(--chart-2);
45
+ --color-chart-3: var(--chart-3);
46
+ --color-chart-4: var(--chart-4);
47
+ --color-chart-5: var(--chart-5);
48
+ --color-sidebar: var(--sidebar);
49
+ --color-sidebar-foreground: var(--sidebar-foreground);
50
+ --color-sidebar-primary: var(--sidebar-primary);
51
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
52
+ --color-sidebar-accent: var(--sidebar-accent);
53
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
54
+ --color-sidebar-border: var(--sidebar-border);
55
+ --color-sidebar-ring: var(--sidebar-ring);
56
+ }
57
+
58
+ :root {
59
+ --radius: 0.65rem;
60
+ --background: oklch(1 0 0);
61
+ --foreground: oklch(0.141 0.005 285.823);
62
+ --card: oklch(1 0 0);
63
+ --card-foreground: oklch(0.141 0.005 285.823);
64
+ --popover: oklch(1 0 0);
65
+ --popover-foreground: oklch(0.141 0.005 285.823);
66
+ --primary: oklch(0.623 0.214 259.815);
67
+ --primary-foreground: oklch(0.97 0.014 254.604);
68
+ --secondary: oklch(0.967 0.001 286.375);
69
+ --secondary-foreground: oklch(0.21 0.006 285.885);
70
+ --muted: oklch(0.967 0.001 286.375);
71
+ --muted-foreground: oklch(0.552 0.016 285.938);
72
+ --accent: oklch(0.967 0.001 286.375);
73
+ --accent-foreground: oklch(0.21 0.006 285.885);
74
+ --destructive: oklch(0.577 0.245 27.325);
75
+ --border: oklch(0.92 0.004 286.32);
76
+ --input: oklch(0.92 0.004 286.32);
77
+ --ring: oklch(0.623 0.214 259.815);
78
+ --chart-1: oklch(0.646 0.222 41.116);
79
+ --chart-2: oklch(0.6 0.118 184.704);
80
+ --chart-3: oklch(0.398 0.07 227.392);
81
+ --chart-4: oklch(0.828 0.189 84.429);
82
+ --chart-5: oklch(0.769 0.188 70.08);
83
+ --sidebar: oklch(0.985 0 0);
84
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
85
+ --sidebar-primary: oklch(0.623 0.214 259.815);
86
+ --sidebar-primary-foreground: oklch(0.97 0.014 254.604);
87
+ --sidebar-accent: oklch(0.967 0.001 286.375);
88
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
89
+ --sidebar-border: oklch(0.92 0.004 286.32);
90
+ --sidebar-ring: oklch(0.623 0.214 259.815);
91
+ }
92
+
93
+ .dark {
94
+ --background: oklch(0.141 0.005 285.823);
95
+ --foreground: oklch(0.985 0 0);
96
+ --card: oklch(0.21 0.006 285.885);
97
+ --card-foreground: oklch(0.985 0 0);
98
+ --popover: oklch(0.21 0.006 285.885);
99
+ --popover-foreground: oklch(0.985 0 0);
100
+ --primary: oklch(0.546 0.245 262.881);
101
+ --primary-foreground: oklch(0.379 0.146 265.522);
102
+ --secondary: oklch(0.274 0.006 286.033);
103
+ --secondary-foreground: oklch(0.985 0 0);
104
+ --muted: oklch(0.274 0.006 286.033);
105
+ --muted-foreground: oklch(0.705 0.015 286.067);
106
+ --accent: oklch(0.274 0.006 286.033);
107
+ --accent-foreground: oklch(0.985 0 0);
108
+ --destructive: oklch(0.704 0.191 22.216);
109
+ --border: oklch(1 0 0 / 10%);
110
+ --input: oklch(1 0 0 / 15%);
111
+ --ring: oklch(0.488 0.243 264.376);
112
+ --chart-1: oklch(0.488 0.243 264.376);
113
+ --chart-2: oklch(0.696 0.17 162.48);
114
+ --chart-3: oklch(0.769 0.188 70.08);
115
+ --chart-4: oklch(0.627 0.265 303.9);
116
+ --chart-5: oklch(0.645 0.246 16.439);
117
+ --sidebar: oklch(0.21 0.006 285.885);
118
+ --sidebar-foreground: oklch(0.985 0 0);
119
+ --sidebar-primary: oklch(0.546 0.245 262.881);
120
+ --sidebar-primary-foreground: oklch(0.379 0.146 265.522);
121
+ --sidebar-accent: oklch(0.274 0.006 286.033);
122
+ --sidebar-accent-foreground: oklch(0.985 0 0);
123
+ --sidebar-border: oklch(1 0 0 / 10%);
124
+ --sidebar-ring: oklch(0.488 0.243 264.376);
125
+ }
126
+
127
+ @layer base {
128
+ * {
129
+ @apply border-border outline-ring/50;
130
+ }
131
+ body {
132
+ @apply bg-background text-foreground;
133
+ }
134
+ }
@@ -0,0 +1,46 @@
1
+ import { Suspense, useEffect, useState } from 'react'
2
+ import { reactQuery, ReactQueryDevtools, copaSetConfig } from "@bluecopa/react";
3
+ import RouteConfig from './routes';
4
+
5
+ import './app.css'
6
+
7
+ const { QueryClient, QueryClientProvider } = reactQuery;
8
+
9
+ export default function App() {
10
+ const [queryClient] = useState(() => new QueryClient({
11
+ defaultOptions: {
12
+ queries: {
13
+ staleTime: 60 * 1000, // 1 minute
14
+ refetchOnWindowFocus: false,
15
+ },
16
+ },
17
+ }));
18
+
19
+ useEffect(() => {
20
+ // Configure Bluecopa API
21
+ let copaUser = {} as any;
22
+ try {
23
+ const copaToken = import.meta.env.VITE_BLUECOPA_API_TOKEN
24
+ ? atob(import.meta.env.VITE_BLUECOPA_API_TOKEN)
25
+ : '{}';
26
+ copaUser = JSON.parse(copaToken);
27
+ } catch (error) {
28
+ console.warn('Failed to parse VITE_BLUECOPA_API_TOKEN:', error);
29
+ }
30
+
31
+ copaSetConfig({
32
+ apiBaseUrl: import.meta.env.VITE_BLUECOPA_API_URL || 'https://develop.bluecopa.com',
33
+ workspaceId: import.meta.env.VITE_BLUECOPA_WORKSPACE_ID || '',
34
+ accessToken: copaUser?.accessToken || '',
35
+ });
36
+ }, []);
37
+
38
+ return (
39
+ <QueryClientProvider client={queryClient}>
40
+ <Suspense fallback={<div className="p-4 text-sm text-muted-foreground">Loading…</div>}>
41
+ <RouteConfig />
42
+ <ReactQueryDevtools initialIsOpen={false} />
43
+ </Suspense>
44
+ </QueryClientProvider>
45
+ )
46
+ }
@@ -0,0 +1,174 @@
1
+ import * as React from "react"
2
+ import {
3
+ IconCamera,
4
+ IconChartBar,
5
+ IconDashboard,
6
+ IconDatabase,
7
+ IconFileAi,
8
+ IconFileDescription,
9
+ IconFileWord,
10
+ IconFolder,
11
+ IconHelp,
12
+ IconInnerShadowTop,
13
+ IconListDetails,
14
+ IconReport,
15
+ IconSearch,
16
+ IconSettings,
17
+ IconUsers,
18
+ } from "@tabler/icons-react"
19
+
20
+ import { NavDocuments } from "~/components/nav-documents"
21
+ import { NavMain } from "~/components/nav-main"
22
+ import { NavSecondary } from "~/components/nav-secondary"
23
+ import { NavUser } from "~/components/nav-user"
24
+ import {
25
+ Sidebar,
26
+ SidebarContent,
27
+ SidebarFooter,
28
+ SidebarHeader,
29
+ SidebarMenu,
30
+ SidebarMenuButton,
31
+ SidebarMenuItem,
32
+ } from "~/components/ui/sidebar"
33
+
34
+ const data = {
35
+ navMain: [
36
+ {
37
+ title: "Dashboard",
38
+ url: "#",
39
+ icon: IconDashboard,
40
+ },
41
+ {
42
+ title: "Lifecycle",
43
+ url: "#",
44
+ icon: IconListDetails,
45
+ },
46
+ {
47
+ title: "Analytics",
48
+ url: "#",
49
+ icon: IconChartBar,
50
+ },
51
+ {
52
+ title: "Projects",
53
+ url: "#",
54
+ icon: IconFolder,
55
+ },
56
+ {
57
+ title: "Team",
58
+ url: "#",
59
+ icon: IconUsers,
60
+ },
61
+ ],
62
+ navClouds: [
63
+ {
64
+ title: "Capture",
65
+ icon: IconCamera,
66
+ isActive: true,
67
+ url: "#",
68
+ items: [
69
+ {
70
+ title: "Active Proposals",
71
+ url: "#",
72
+ },
73
+ {
74
+ title: "Archived",
75
+ url: "#",
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ title: "Proposal",
81
+ icon: IconFileDescription,
82
+ url: "#",
83
+ items: [
84
+ {
85
+ title: "Active Proposals",
86
+ url: "#",
87
+ },
88
+ {
89
+ title: "Archived",
90
+ url: "#",
91
+ },
92
+ ],
93
+ },
94
+ {
95
+ title: "Prompts",
96
+ icon: IconFileAi,
97
+ url: "#",
98
+ items: [
99
+ {
100
+ title: "Active Proposals",
101
+ url: "#",
102
+ },
103
+ {
104
+ title: "Archived",
105
+ url: "#",
106
+ },
107
+ ],
108
+ },
109
+ ],
110
+ navSecondary: [
111
+ {
112
+ title: "Settings",
113
+ url: "#",
114
+ icon: IconSettings,
115
+ },
116
+ {
117
+ title: "Get Help",
118
+ url: "#",
119
+ icon: IconHelp,
120
+ },
121
+ {
122
+ title: "Search",
123
+ url: "#",
124
+ icon: IconSearch,
125
+ },
126
+ ],
127
+ documents: [
128
+ {
129
+ name: "Data Library",
130
+ url: "#",
131
+ icon: IconDatabase,
132
+ },
133
+ {
134
+ name: "Reports",
135
+ url: "#",
136
+ icon: IconReport,
137
+ },
138
+ {
139
+ name: "Word Assistant",
140
+ url: "#",
141
+ icon: IconFileWord,
142
+ },
143
+ ],
144
+ }
145
+
146
+ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
147
+ return (
148
+ <Sidebar collapsible="offcanvas" {...props}>
149
+ <SidebarHeader>
150
+ <SidebarMenu>
151
+ <SidebarMenuItem>
152
+ <SidebarMenuButton
153
+ asChild
154
+ className="data-[slot=sidebar-menu-button]:!p-1.5"
155
+ >
156
+ <a href="https://bluecopa.com">
157
+ <IconInnerShadowTop className="!size-5" />
158
+ <span className="text-base font-semibold">Bluecopa</span>
159
+ </a>
160
+ </SidebarMenuButton>
161
+ </SidebarMenuItem>
162
+ </SidebarMenu>
163
+ </SidebarHeader>
164
+ <SidebarContent>
165
+ <NavMain items={data.navMain} />
166
+ <NavDocuments items={data.documents} />
167
+ <NavSecondary items={data.navSecondary} className="mt-auto" />
168
+ </SidebarContent>
169
+ <SidebarFooter>
170
+ <NavUser />
171
+ </SidebarFooter>
172
+ </Sidebar>
173
+ )
174
+ }
@@ -0,0 +1,290 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
5
+
6
+ import { useIsMobile } from "~/hooks/use-mobile"
7
+ import {
8
+ Card,
9
+ CardAction,
10
+ CardContent,
11
+ CardDescription,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "~/components/ui/card"
15
+ import {
16
+ ChartContainer,
17
+ ChartTooltip,
18
+ ChartTooltipContent,
19
+ } from "~/components/ui/chart"
20
+ import {
21
+ Select,
22
+ SelectContent,
23
+ SelectItem,
24
+ SelectTrigger,
25
+ SelectValue,
26
+ } from "~/components/ui/select"
27
+ import {
28
+ ToggleGroup,
29
+ ToggleGroupItem,
30
+ } from "~/components/ui/toggle-group"
31
+
32
+ export const description = "An interactive area chart"
33
+
34
+ const chartData = [
35
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
36
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
37
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
38
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
39
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
40
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
41
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
42
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
43
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
44
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
45
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
46
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
47
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
48
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
49
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
50
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
51
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
52
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
53
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
54
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
55
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
56
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
57
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
58
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
59
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
60
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
61
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
62
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
63
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
64
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
65
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
66
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
67
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
68
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
69
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
70
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
71
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
72
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
73
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
74
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
75
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
76
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
77
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
78
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
79
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
80
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
81
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
82
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
83
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
84
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
85
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
86
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
87
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
88
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
89
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
90
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
91
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
92
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
93
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
94
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
95
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
96
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
97
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
98
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
99
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
100
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
101
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
102
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
103
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
104
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
105
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
106
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
107
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
108
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
109
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
110
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
111
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
112
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
113
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
114
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
115
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
116
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
117
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
118
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
119
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
120
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
121
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
122
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
123
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
124
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
125
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
126
+ ]
127
+
128
+ const chartConfig = {
129
+ visitors: {
130
+ label: "Visitors",
131
+ },
132
+ desktop: {
133
+ label: "Desktop",
134
+ color: "var(--primary)",
135
+ },
136
+ mobile: {
137
+ label: "Mobile",
138
+ color: "var(--primary)",
139
+ },
140
+ }
141
+
142
+ export function ChartAreaInteractive() {
143
+ const isMobile = useIsMobile()
144
+ const [timeRange, setTimeRange] = React.useState("90d")
145
+
146
+ React.useEffect(() => {
147
+ if (isMobile) {
148
+ setTimeRange("7d")
149
+ }
150
+ }, [isMobile])
151
+
152
+ const filteredData = chartData.filter((item) => {
153
+ const date = new Date(item.date)
154
+ const referenceDate = new Date("2024-06-30")
155
+ let daysToSubtract = 90
156
+ if (timeRange === "30d") {
157
+ daysToSubtract = 30
158
+ } else if (timeRange === "7d") {
159
+ daysToSubtract = 7
160
+ }
161
+ const startDate = new Date(referenceDate)
162
+ startDate.setDate(startDate.getDate() - daysToSubtract)
163
+ return date >= startDate
164
+ })
165
+
166
+ return (
167
+ <Card className="@container/card">
168
+ <CardHeader>
169
+ <CardTitle>Total Visitors</CardTitle>
170
+ <CardDescription>
171
+ <span className="hidden @[540px]/card:block">
172
+ Total for the last 3 months
173
+ </span>
174
+ <span className="@[540px]/card:hidden">Last 3 months</span>
175
+ </CardDescription>
176
+ <CardAction>
177
+ <ToggleGroup
178
+ type="single"
179
+ value={timeRange}
180
+ onValueChange={setTimeRange}
181
+ variant="outline"
182
+ className="hidden *:data-[slot=toggle-group-item]:!px-4 @[767px]/card:flex"
183
+ >
184
+ <ToggleGroupItem value="90d">Last 3 months</ToggleGroupItem>
185
+ <ToggleGroupItem value="30d">Last 30 days</ToggleGroupItem>
186
+ <ToggleGroupItem value="7d">Last 7 days</ToggleGroupItem>
187
+ </ToggleGroup>
188
+ <Select value={timeRange} onValueChange={setTimeRange}>
189
+ <SelectTrigger
190
+ className="flex w-40 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate @[767px]/card:hidden"
191
+ size="sm"
192
+ aria-label="Select a value"
193
+ >
194
+ <SelectValue placeholder="Last 3 months" />
195
+ </SelectTrigger>
196
+ <SelectContent className="rounded-xl">
197
+ <SelectItem value="90d" className="rounded-lg">
198
+ Last 3 months
199
+ </SelectItem>
200
+ <SelectItem value="30d" className="rounded-lg">
201
+ Last 30 days
202
+ </SelectItem>
203
+ <SelectItem value="7d" className="rounded-lg">
204
+ Last 7 days
205
+ </SelectItem>
206
+ </SelectContent>
207
+ </Select>
208
+ </CardAction>
209
+ </CardHeader>
210
+ <CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
211
+ <ChartContainer
212
+ config={chartConfig}
213
+ className="aspect-auto h-[250px] w-full"
214
+ >
215
+ <AreaChart data={filteredData}>
216
+ <defs>
217
+ <linearGradient id="fillDesktop" x1="0" y1="0" x2="0" y2="1">
218
+ <stop
219
+ offset="5%"
220
+ stopColor="var(--color-desktop)"
221
+ stopOpacity={1.0}
222
+ />
223
+ <stop
224
+ offset="95%"
225
+ stopColor="var(--color-desktop)"
226
+ stopOpacity={0.1}
227
+ />
228
+ </linearGradient>
229
+ <linearGradient id="fillMobile" x1="0" y1="0" x2="0" y2="1">
230
+ <stop
231
+ offset="5%"
232
+ stopColor="var(--color-mobile)"
233
+ stopOpacity={0.8}
234
+ />
235
+ <stop
236
+ offset="95%"
237
+ stopColor="var(--color-mobile)"
238
+ stopOpacity={0.1}
239
+ />
240
+ </linearGradient>
241
+ </defs>
242
+ <CartesianGrid vertical={false} />
243
+ <XAxis
244
+ dataKey="date"
245
+ tickLine={false}
246
+ axisLine={false}
247
+ tickMargin={8}
248
+ minTickGap={32}
249
+ tickFormatter={(value) => {
250
+ const date = new Date(value)
251
+ return date.toLocaleDateString("en-US", {
252
+ month: "short",
253
+ day: "numeric",
254
+ })
255
+ }}
256
+ />
257
+ <ChartTooltip
258
+ cursor={false}
259
+ content={
260
+ <ChartTooltipContent
261
+ labelFormatter={(value) => {
262
+ return new Date(value).toLocaleDateString("en-US", {
263
+ month: "short",
264
+ day: "numeric",
265
+ })
266
+ }}
267
+ indicator="dot"
268
+ />
269
+ }
270
+ />
271
+ <Area
272
+ dataKey="mobile"
273
+ type="natural"
274
+ fill="url(#fillMobile)"
275
+ stroke="var(--color-mobile)"
276
+ stackId="a"
277
+ />
278
+ <Area
279
+ dataKey="desktop"
280
+ type="natural"
281
+ fill="url(#fillDesktop)"
282
+ stroke="var(--color-desktop)"
283
+ stackId="a"
284
+ />
285
+ </AreaChart>
286
+ </ChartContainer>
287
+ </CardContent>
288
+ </Card>
289
+ )
290
+ }