@stacksjs/ts-analytics 0.1.6
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/CHANGELOG.md +68 -0
- package/LICENSE.md +21 -0
- package/README.md +361 -0
- package/bin/cli.ts +169 -0
- package/dist/Analytics.d.ts +558 -0
- package/dist/api.d.ts +109 -0
- package/dist/batching.d.ts +93 -0
- package/dist/chunk-2mx7fq49.js +4 -0
- package/dist/chunk-3z29508k.js +204 -0
- package/dist/chunk-deephkz6.js +56 -0
- package/dist/chunk-j261vgyp.js +305 -0
- package/dist/chunk-xga17tz7.js +207 -0
- package/dist/config.d.ts +132 -0
- package/dist/dashboard/components/index.d.ts +80 -0
- package/dist/dashboard/composables/useAnalytics.d.ts +99 -0
- package/dist/dashboard/index.d.ts +110 -0
- package/dist/dashboard/types/index.d.ts +144 -0
- package/dist/dashboard/utils/index.d.ts +81 -0
- package/dist/dynamodb.d.ts +86 -0
- package/dist/funnels.d.ts +104 -0
- package/dist/geolocation.d.ts +96 -0
- package/dist/index.d.ts +371 -0
- package/dist/index.js +10211 -0
- package/dist/infrastructure/cdk.d.ts +60 -0
- package/dist/infrastructure/cloudformation.d.ts +44 -0
- package/dist/infrastructure/index.d.ts +45 -0
- package/dist/infrastructure/setup.d.ts +133 -0
- package/dist/integrations/cloudflare.d.ts +76 -0
- package/dist/integrations/hono.d.ts +60 -0
- package/dist/integrations/index.d.ts +24 -0
- package/dist/integrations/nuxt.d.ts +7 -0
- package/dist/integrations/nuxt.js +42 -0
- package/dist/integrations/runtime/use-ts-analytics.d.ts +18 -0
- package/dist/integrations/runtime/use-ts-analytics.js +17 -0
- package/dist/integrations/stx.d.ts +88 -0
- package/dist/integrations/stx.js +18 -0
- package/dist/lib/crypto-random.d.ts +4 -0
- package/dist/lib/salt.d.ts +16 -0
- package/dist/local.d.ts +56 -0
- package/dist/model-connector.d.ts +145 -0
- package/dist/models/AggregatedStats.d.ts +9 -0
- package/dist/models/CampaignStats.d.ts +9 -0
- package/dist/models/Conversion.d.ts +11 -0
- package/dist/models/CustomEvent.d.ts +11 -0
- package/dist/models/DeviceStats.d.ts +9 -0
- package/dist/models/EventStats.d.ts +9 -0
- package/dist/models/GeoStats.d.ts +9 -0
- package/dist/models/Goal.d.ts +9 -0
- package/dist/models/GoalStats.d.ts +9 -0
- package/dist/models/PageStats.d.ts +11 -0
- package/dist/models/PageView.d.ts +13 -0
- package/dist/models/RealtimeStats.d.ts +9 -0
- package/dist/models/ReferrerStats.d.ts +9 -0
- package/dist/models/Session.d.ts +11 -0
- package/dist/models/Site.d.ts +11 -0
- package/dist/models/index.d.ts +28 -0
- package/dist/models/types.d.ts +60 -0
- package/dist/sqs-buffering.d.ts +243 -0
- package/dist/stacks-integration.d.ts +159 -0
- package/dist/tracking-script.d.ts +71 -0
- package/dist/tracking.d.ts +17 -0
- package/dist/tracking.js +26 -0
- package/dist/types.d.ts +595 -0
- package/dist/utils/geolocation.d.ts +111 -0
- package/dist/utils/user-agent.d.ts +17 -0
- package/dist/version.d.ts +7 -0
- package/package.json +119 -0
- package/src/Analytics.ts +3349 -0
- package/src/api.ts +1286 -0
- package/src/assets/crosswind.css +2220 -0
- package/src/batching.ts +452 -0
- package/src/components/dashboard/index.ts +18 -0
- package/src/config.ts +456 -0
- package/src/dashboard/Dashboard.stx +1517 -0
- package/src/dashboard/components/AlertCard.stx +177 -0
- package/src/dashboard/components/AnalyticsDashboard.stx +354 -0
- package/src/dashboard/components/AnimatedNumber.stx +86 -0
- package/src/dashboard/components/BarChart.stx +220 -0
- package/src/dashboard/components/BrowserBreakdown.stx +98 -0
- package/src/dashboard/components/BrowsersTable.stx +125 -0
- package/src/dashboard/components/CampaignBreakdown.stx +163 -0
- package/src/dashboard/components/CampaignTable.stx +238 -0
- package/src/dashboard/components/CountryList.stx +101 -0
- package/src/dashboard/components/DataTable.stx +226 -0
- package/src/dashboard/components/DateRangePicker.stx +77 -0
- package/src/dashboard/components/DeviceBreakdown.stx +94 -0
- package/src/dashboard/components/DevicesTable.stx +163 -0
- package/src/dashboard/components/DonutChart.stories.ts +55 -0
- package/src/dashboard/components/DonutChart.stx +157 -0
- package/src/dashboard/components/EmptyState.stx +90 -0
- package/src/dashboard/components/EngagementMetrics.stx +183 -0
- package/src/dashboard/components/EventsSection.stx +192 -0
- package/src/dashboard/components/FilterBar.stx +104 -0
- package/src/dashboard/components/FullAnalyticsDashboard.stx +455 -0
- package/src/dashboard/components/FunnelChart.stx +142 -0
- package/src/dashboard/components/GeoTable.stx +337 -0
- package/src/dashboard/components/GoalsPanel.stx +109 -0
- package/src/dashboard/components/Header.stx +39 -0
- package/src/dashboard/components/HeatmapChart.stx +140 -0
- package/src/dashboard/components/LiveActivityFeed.stx +172 -0
- package/src/dashboard/components/MetricComparison.stx +106 -0
- package/src/dashboard/components/MiniStats.stx +96 -0
- package/src/dashboard/components/OSBreakdown.stx +124 -0
- package/src/dashboard/components/PageDetailCard.stx +102 -0
- package/src/dashboard/components/PagesTable.stx +127 -0
- package/src/dashboard/components/ProgressRing.stx +89 -0
- package/src/dashboard/components/RealtimeCounter.stories.ts +45 -0
- package/src/dashboard/components/RealtimeCounter.stx +46 -0
- package/src/dashboard/components/ReferrersTable.stx +180 -0
- package/src/dashboard/components/SparklineChart.stx +160 -0
- package/src/dashboard/components/StatCard.stories.ts +58 -0
- package/src/dashboard/components/StatCard.stx +90 -0
- package/src/dashboard/components/SummaryStats.stx +81 -0
- package/src/dashboard/components/TabNav.stx +66 -0
- package/src/dashboard/components/ThemeSwitcher.stx +124 -0
- package/src/dashboard/components/TimeSeriesChart.stx +106 -0
- package/src/dashboard/components/TopList.stories.ts +58 -0
- package/src/dashboard/components/TopList.stx +74 -0
- package/src/dashboard/components/TrendIndicator.stx +84 -0
- package/src/dashboard/components/heatmap/ClickHeatmap.stx +264 -0
- package/src/dashboard/components/heatmap/ElementClickList.stx +125 -0
- package/src/dashboard/components/heatmap/HeatmapControls.stx +125 -0
- package/src/dashboard/components/heatmap/HeatmapLegend.stx +69 -0
- package/src/dashboard/components/heatmap/PageHeatmap.stx +264 -0
- package/src/dashboard/components/heatmap/ScrollHeatmap.stx +127 -0
- package/src/dashboard/components/heatmap/index.ts +12 -0
- package/src/dashboard/components/index.ts +86 -0
- package/src/dashboard/composables/useAnalytics.ts +465 -0
- package/src/dashboard/demo/DemoApp.stx +370 -0
- package/src/dashboard/demo/index.ts +8 -0
- package/src/dashboard/demo/mockData.ts +234 -0
- package/src/dashboard/index.ts +117 -0
- package/src/dashboard/stx-shim.d.ts +4 -0
- package/src/dashboard/types/index.ts +203 -0
- package/src/dashboard/utils/index.ts +426 -0
- package/src/dynamodb.ts +344 -0
- package/src/funnels.ts +534 -0
- package/src/geolocation.ts +515 -0
- package/src/handlers/alerts.ts +328 -0
- package/src/handlers/annotations.ts +128 -0
- package/src/handlers/api-keys.ts +240 -0
- package/src/handlers/auth.ts +1020 -0
- package/src/handlers/authz.ts +137 -0
- package/src/handlers/collect.ts +724 -0
- package/src/handlers/data.ts +625 -0
- package/src/handlers/experiments.ts +138 -0
- package/src/handlers/funnels.ts +216 -0
- package/src/handlers/goals.ts +218 -0
- package/src/handlers/heatmaps.ts +272 -0
- package/src/handlers/index.ts +56 -0
- package/src/handlers/lib/read-cache.ts +63 -0
- package/src/handlers/misc.ts +486 -0
- package/src/handlers/oauth.ts +233 -0
- package/src/handlers/performance.ts +388 -0
- package/src/handlers/sessions.ts +413 -0
- package/src/handlers/sharing.ts +198 -0
- package/src/handlers/stats.ts +1368 -0
- package/src/handlers/team.ts +161 -0
- package/src/handlers/uptime.ts +283 -0
- package/src/handlers/views.ts +390 -0
- package/src/handlers/webhooks.ts +226 -0
- package/src/heatmap/index.ts +32 -0
- package/src/heatmap/tracking-script.ts +452 -0
- package/src/heatmap/types.ts +79 -0
- package/src/index.ts +387 -0
- package/src/infrastructure/cdk.ts +496 -0
- package/src/infrastructure/cloudformation.ts +595 -0
- package/src/infrastructure/index.ts +48 -0
- package/src/infrastructure/setup.ts +611 -0
- package/src/integrations/cloudflare.ts +732 -0
- package/src/integrations/hono.ts +589 -0
- package/src/integrations/index.ts +27 -0
- package/src/integrations/nuxt.ts +78 -0
- package/src/integrations/runtime/use-ts-analytics.ts +32 -0
- package/src/integrations/stx.ts +138 -0
- package/src/jobs/index.ts +127 -0
- package/src/lib/crypto-random.ts +41 -0
- package/src/lib/ddb-errors.ts +20 -0
- package/src/lib/dynamodb.ts +216 -0
- package/src/lib/email.ts +38 -0
- package/src/lib/ga-import.ts +471 -0
- package/src/lib/ga4-api.ts +244 -0
- package/src/lib/goals.ts +205 -0
- package/src/lib/index.ts +6 -0
- package/src/lib/ingest-counters.ts +123 -0
- package/src/lib/log.ts +36 -0
- package/src/lib/membership.ts +98 -0
- package/src/lib/plans.ts +90 -0
- package/src/lib/quota.ts +46 -0
- package/src/lib/rate-limit.ts +27 -0
- package/src/lib/rollups.ts +472 -0
- package/src/lib/salt.ts +97 -0
- package/src/lib/scheduler.ts +97 -0
- package/src/lib/significance.ts +66 -0
- package/src/lib/site-retention.ts +56 -0
- package/src/local.ts +360 -0
- package/src/model-connector.ts +616 -0
- package/src/models/AggregatedStats.ts +162 -0
- package/src/models/CampaignStats.ts +141 -0
- package/src/models/Conversion.ts +135 -0
- package/src/models/CustomEvent.ts +123 -0
- package/src/models/DeviceStats.ts +105 -0
- package/src/models/EventStats.ts +108 -0
- package/src/models/GeoStats.ts +112 -0
- package/src/models/Goal.ts +105 -0
- package/src/models/GoalStats.ts +106 -0
- package/src/models/PageStats.ts +152 -0
- package/src/models/PageView.ts +277 -0
- package/src/models/RealtimeStats.ts +96 -0
- package/src/models/ReferrerStats.ts +115 -0
- package/src/models/Session.ts +235 -0
- package/src/models/Site.ts +115 -0
- package/src/models/index.ts +50 -0
- package/src/models/orm/index.ts +2043 -0
- package/src/models/types.ts +71 -0
- package/src/router.ts +521 -0
- package/src/sqs-buffering.ts +806 -0
- package/src/stacks-integration.ts +527 -0
- package/src/tracking-script.ts +944 -0
- package/src/tracking.ts +27 -0
- package/src/types/analytics.ts +219 -0
- package/src/types/api.ts +75 -0
- package/src/types/bun-router.d.ts +15 -0
- package/src/types/dashboard.ts +62 -0
- package/src/types/index.ts +66 -0
- package/src/types/stx.d.ts +27 -0
- package/src/types/window.d.ts +101 -0
- package/src/types.ts +911 -0
- package/src/utils/cache.ts +148 -0
- package/src/utils/date.ts +118 -0
- package/src/utils/filters.ts +71 -0
- package/src/utils/geolocation.ts +323 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/response.ts +180 -0
- package/src/utils/timezone-country.ts +242 -0
- package/src/utils/user-agent.ts +110 -0
- package/src/version.ts +7 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Analytics Dashboard Demo App
|
|
4
|
+
*
|
|
5
|
+
* Showcases all 31 dashboard components with mock data.
|
|
6
|
+
*/
|
|
7
|
+
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
|
8
|
+
import {
|
|
9
|
+
AlertCard,
|
|
10
|
+
AnimatedNumber,
|
|
11
|
+
AnalyticsDashboard,
|
|
12
|
+
BarChart,
|
|
13
|
+
BrowserBreakdown,
|
|
14
|
+
CampaignBreakdown,
|
|
15
|
+
CountryList,
|
|
16
|
+
DataTable,
|
|
17
|
+
DateRangePicker,
|
|
18
|
+
DeviceBreakdown,
|
|
19
|
+
DonutChart,
|
|
20
|
+
EmptyState,
|
|
21
|
+
EngagementMetrics,
|
|
22
|
+
FilterBar,
|
|
23
|
+
FunnelChart,
|
|
24
|
+
GoalsPanel,
|
|
25
|
+
HeatmapChart,
|
|
26
|
+
LiveActivityFeed,
|
|
27
|
+
MetricComparison,
|
|
28
|
+
MiniStats,
|
|
29
|
+
OSBreakdown,
|
|
30
|
+
PageDetailCard,
|
|
31
|
+
ProgressRing,
|
|
32
|
+
RealtimeCounter,
|
|
33
|
+
SparklineChart,
|
|
34
|
+
StatCard,
|
|
35
|
+
ThemeSwitcher,
|
|
36
|
+
TimeSeriesChart,
|
|
37
|
+
TopList,
|
|
38
|
+
TrendIndicator,
|
|
39
|
+
} from '../components'
|
|
40
|
+
import { mockData } from './mockData'
|
|
41
|
+
|
|
42
|
+
// Theme state
|
|
43
|
+
const isDark = ref(false)
|
|
44
|
+
|
|
45
|
+
// Date range state
|
|
46
|
+
const dateRange = ref({
|
|
47
|
+
start: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
|
|
48
|
+
end: new Date(),
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
// Active section for navigation
|
|
52
|
+
const activeSection = ref('overview')
|
|
53
|
+
|
|
54
|
+
// Realtime simulation
|
|
55
|
+
const realtimeCount = ref(42)
|
|
56
|
+
const activities = ref(mockData.activities)
|
|
57
|
+
|
|
58
|
+
let realtimeInterval: ReturnType<typeof setInterval> | null = null
|
|
59
|
+
|
|
60
|
+
onMounted(() => {
|
|
61
|
+
// Simulate realtime updates
|
|
62
|
+
realtimeInterval = setInterval(() => {
|
|
63
|
+
realtimeCount.value = Math.floor(Math.random() * 50) + 20
|
|
64
|
+
|
|
65
|
+
// Add random activity
|
|
66
|
+
if (Math.random() > 0.5) {
|
|
67
|
+
const types = ['pageview', 'event', 'conversion', 'session_start'] as const
|
|
68
|
+
const newActivity = {
|
|
69
|
+
id: `act-${Date.now()}`,
|
|
70
|
+
type: types[Math.floor(Math.random() * types.length)],
|
|
71
|
+
page: mockData.pages[Math.floor(Math.random() * mockData.pages.length)],
|
|
72
|
+
country: mockData.countries[Math.floor(Math.random() * mockData.countries.length)].name,
|
|
73
|
+
countryCode: mockData.countries[Math.floor(Math.random() * mockData.countries.length)].code,
|
|
74
|
+
device: (['desktop', 'mobile', 'tablet'] as const)[Math.floor(Math.random() * 3)],
|
|
75
|
+
timestamp: new Date(),
|
|
76
|
+
}
|
|
77
|
+
activities.value = [newActivity, ...activities.value.slice(0, 19)]
|
|
78
|
+
}
|
|
79
|
+
}, 3000)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
onUnmounted(() => {
|
|
83
|
+
if (realtimeInterval) clearInterval(realtimeInterval)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
const sections = [
|
|
87
|
+
{ id: 'overview', label: 'Overview' },
|
|
88
|
+
{ id: 'charts', label: 'Charts' },
|
|
89
|
+
{ id: 'breakdowns', label: 'Breakdowns' },
|
|
90
|
+
{ id: 'tables', label: 'Tables & Lists' },
|
|
91
|
+
{ id: 'engagement', label: 'Engagement' },
|
|
92
|
+
{ id: 'utilities', label: 'Utilities' },
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
function handleDateChange(range: { start: Date, end: Date }) {
|
|
96
|
+
dateRange.value = range
|
|
97
|
+
}
|
|
98
|
+
</script>
|
|
99
|
+
|
|
100
|
+
<template>
|
|
101
|
+
<div class="min-h-screen" x-class="isDark ? 'dark bg-gray-900' : 'bg-gray-50'">
|
|
102
|
+
<!-- Header -->
|
|
103
|
+
<header class="sticky top-0 z-50 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
|
104
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
105
|
+
<div class="flex items-center justify-between h-16">
|
|
106
|
+
<div class="flex items-center gap-4">
|
|
107
|
+
<h1 class="text-xl font-bold text-gray-900 dark:text-white">
|
|
108
|
+
Analytics Dashboard Demo
|
|
109
|
+
</h1>
|
|
110
|
+
<span class="px-2 py-1 text-xs font-medium bg-primary-100 text-primary-700 rounded-full">
|
|
111
|
+
31 Components
|
|
112
|
+
</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="flex items-center gap-4">
|
|
115
|
+
<DateRangePicker
|
|
116
|
+
:start="dateRange.start"
|
|
117
|
+
:end="dateRange.end"
|
|
118
|
+
@change="handleDateChange"
|
|
119
|
+
/>
|
|
120
|
+
<ThemeSwitcher v-model="isDark" />
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</header>
|
|
125
|
+
|
|
126
|
+
<!-- Navigation -->
|
|
127
|
+
<nav class="bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
|
128
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
129
|
+
<div class="flex gap-4 overflow-x-auto py-3">
|
|
130
|
+
<button
|
|
131
|
+
v-for="section in sections"
|
|
132
|
+
:key="section.id"
|
|
133
|
+
class="px-4 py-2 text-sm font-medium rounded-lg whitespace-nowrap transition-colors"
|
|
134
|
+
x-class="activeSection === section.id
|
|
135
|
+
? 'bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300'
|
|
136
|
+
: 'text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-700'"
|
|
137
|
+
@click="activeSection = section.id"
|
|
138
|
+
>
|
|
139
|
+
{{ section.label }}
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</nav>
|
|
144
|
+
|
|
145
|
+
<!-- Main Content -->
|
|
146
|
+
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
147
|
+
<!-- Overview Section -->
|
|
148
|
+
<section v-show="activeSection === 'overview'" class="space-y-8">
|
|
149
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Overview Components</h2>
|
|
150
|
+
|
|
151
|
+
<!-- Realtime Counter -->
|
|
152
|
+
<div class="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
|
153
|
+
<RealtimeCounter :count="realtimeCount" label="Active Visitors" />
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<!-- Stat Cards -->
|
|
157
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
158
|
+
<StatCard
|
|
159
|
+
v-for="stat in mockData.stats"
|
|
160
|
+
:key="stat.label"
|
|
161
|
+
:label="stat.label"
|
|
162
|
+
x-value="stat.value"
|
|
163
|
+
:change="stat.change"
|
|
164
|
+
:icon="stat.icon"
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<!-- Mini Stats -->
|
|
169
|
+
<MiniStats :stats="mockData.miniStats" />
|
|
170
|
+
|
|
171
|
+
<!-- Animated Numbers -->
|
|
172
|
+
<div class="card p-6">
|
|
173
|
+
<h3 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Animated Numbers</h3>
|
|
174
|
+
<div class="flex gap-8">
|
|
175
|
+
<div>
|
|
176
|
+
<div class="text-3xl font-bold text-gray-900 dark:text-white">
|
|
177
|
+
<AnimatedNumber x-value="12847" format="compact" />
|
|
178
|
+
</div>
|
|
179
|
+
<div class="text-sm text-gray-500">Total Users</div>
|
|
180
|
+
</div>
|
|
181
|
+
<div>
|
|
182
|
+
<div class="text-3xl font-bold text-green-600">
|
|
183
|
+
<AnimatedNumber x-value="0.0847" format="percentage" />
|
|
184
|
+
</div>
|
|
185
|
+
<div class="text-sm text-gray-500">Conversion Rate</div>
|
|
186
|
+
</div>
|
|
187
|
+
<div>
|
|
188
|
+
<div class="text-3xl font-bold text-primary-600">
|
|
189
|
+
<AnimatedNumber x-value="48293" format="currency" />
|
|
190
|
+
</div>
|
|
191
|
+
<div class="text-sm text-gray-500">Revenue</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<!-- Metric Comparison -->
|
|
197
|
+
<MetricComparison
|
|
198
|
+
:metrics="mockData.comparisonMetrics"
|
|
199
|
+
title="Period Comparison"
|
|
200
|
+
/>
|
|
201
|
+
</section>
|
|
202
|
+
|
|
203
|
+
<!-- Charts Section -->
|
|
204
|
+
<section v-show="activeSection === 'charts'" class="space-y-8">
|
|
205
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Chart Components</h2>
|
|
206
|
+
|
|
207
|
+
<!-- Time Series -->
|
|
208
|
+
<TimeSeriesChart
|
|
209
|
+
:data="mockData.timeSeries"
|
|
210
|
+
title="Visitors Over Time"
|
|
211
|
+
x-height="300"
|
|
212
|
+
/>
|
|
213
|
+
|
|
214
|
+
<!-- Chart Grid -->
|
|
215
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
216
|
+
<DonutChart
|
|
217
|
+
:data="mockData.donutData"
|
|
218
|
+
title="Traffic Sources"
|
|
219
|
+
/>
|
|
220
|
+
<BarChart
|
|
221
|
+
:data="mockData.barData"
|
|
222
|
+
title="Top Pages"
|
|
223
|
+
/>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
227
|
+
<FunnelChart
|
|
228
|
+
:steps="mockData.funnelSteps"
|
|
229
|
+
title="Conversion Funnel"
|
|
230
|
+
/>
|
|
231
|
+
<HeatmapChart
|
|
232
|
+
:data="mockData.heatmapData"
|
|
233
|
+
title="Activity by Hour"
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Sparklines -->
|
|
238
|
+
<div class="card p-6">
|
|
239
|
+
<h3 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Sparkline Charts</h3>
|
|
240
|
+
<div class="grid grid-cols-2 lg:grid-cols-4 gap-6">
|
|
241
|
+
<div v-for="spark in mockData.sparklines" :key="spark.label">
|
|
242
|
+
<div class="text-sm text-gray-500 mb-1">{{ spark.label }}</div>
|
|
243
|
+
<SparklineChart :data="spark.data" :color="spark.color" x-height="40" />
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
|
|
248
|
+
<!-- Progress Rings -->
|
|
249
|
+
<div class="card p-6">
|
|
250
|
+
<h3 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Progress Rings</h3>
|
|
251
|
+
<div class="flex gap-8">
|
|
252
|
+
<ProgressRing x-value="0.75" label="Goal Progress" :size="100" />
|
|
253
|
+
<ProgressRing x-value="0.42" label="Bounce Rate" color="#ef4444" :size="100" />
|
|
254
|
+
<ProgressRing x-value="0.91" label="Uptime" color="#10b981" :size="100" />
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
</section>
|
|
258
|
+
|
|
259
|
+
<!-- Breakdowns Section -->
|
|
260
|
+
<section v-show="activeSection === 'breakdowns'" class="space-y-8">
|
|
261
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Breakdown Components</h2>
|
|
262
|
+
|
|
263
|
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
264
|
+
<DeviceBreakdown :data="mockData.devices" />
|
|
265
|
+
<BrowserBreakdown :browsers="mockData.browsers" />
|
|
266
|
+
<OSBreakdown :data="mockData.operatingSystems" />
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
270
|
+
<CountryList :countries="mockData.countryList" />
|
|
271
|
+
<CampaignBreakdown :campaigns="mockData.campaigns" />
|
|
272
|
+
</div>
|
|
273
|
+
</section>
|
|
274
|
+
|
|
275
|
+
<!-- Tables Section -->
|
|
276
|
+
<section v-show="activeSection === 'tables'" class="space-y-8">
|
|
277
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Table & List Components</h2>
|
|
278
|
+
|
|
279
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
280
|
+
<TopList
|
|
281
|
+
:items="mockData.topPages"
|
|
282
|
+
title="Top Pages"
|
|
283
|
+
value-label="Views"
|
|
284
|
+
/>
|
|
285
|
+
<TopList
|
|
286
|
+
:items="mockData.topReferrers"
|
|
287
|
+
title="Top Referrers"
|
|
288
|
+
value-label="Visitors"
|
|
289
|
+
/>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<DataTable
|
|
293
|
+
:columns="mockData.tableColumns"
|
|
294
|
+
:rows="mockData.tableRows"
|
|
295
|
+
title="Detailed Analytics"
|
|
296
|
+
/>
|
|
297
|
+
|
|
298
|
+
<PageDetailCard :page="mockData.pageDetail" />
|
|
299
|
+
</section>
|
|
300
|
+
|
|
301
|
+
<!-- Engagement Section -->
|
|
302
|
+
<section v-show="activeSection === 'engagement'" class="space-y-8">
|
|
303
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Engagement Components</h2>
|
|
304
|
+
|
|
305
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
306
|
+
<GoalsPanel :goals="mockData.goals" />
|
|
307
|
+
<EngagementMetrics :metrics="mockData.engagementMetrics" />
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
311
|
+
<LiveActivityFeed :activities="activities" />
|
|
312
|
+
<AlertCard
|
|
313
|
+
v-for="alert in mockData.alerts"
|
|
314
|
+
:key="alert.id"
|
|
315
|
+
:title="alert.title"
|
|
316
|
+
:message="alert.message"
|
|
317
|
+
:type="alert.type"
|
|
318
|
+
:timestamp="alert.timestamp"
|
|
319
|
+
/>
|
|
320
|
+
</div>
|
|
321
|
+
</section>
|
|
322
|
+
|
|
323
|
+
<!-- Utilities Section -->
|
|
324
|
+
<section v-show="activeSection === 'utilities'" class="space-y-8">
|
|
325
|
+
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Utility Components</h2>
|
|
326
|
+
|
|
327
|
+
<!-- Filter Bar -->
|
|
328
|
+
<FilterBar
|
|
329
|
+
:filters="mockData.filters"
|
|
330
|
+
@change="(f) => console.log('Filters changed:', f)"
|
|
331
|
+
/>
|
|
332
|
+
|
|
333
|
+
<!-- Trend Indicators -->
|
|
334
|
+
<div class="card p-6">
|
|
335
|
+
<h3 class="text-sm font-medium text-gray-900 dark:text-white mb-4">Trend Indicators</h3>
|
|
336
|
+
<div class="flex gap-8">
|
|
337
|
+
<TrendIndicator x-value="0.15" label="vs last week" />
|
|
338
|
+
<TrendIndicator x-value="-0.08" label="vs last month" />
|
|
339
|
+
<TrendIndicator x-value="0" label="vs yesterday" />
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
|
|
343
|
+
<!-- Empty States -->
|
|
344
|
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
345
|
+
<EmptyState
|
|
346
|
+
title="No data available"
|
|
347
|
+
message="Start tracking to see your analytics data here."
|
|
348
|
+
icon="chart"
|
|
349
|
+
/>
|
|
350
|
+
<EmptyState
|
|
351
|
+
title="No visitors yet"
|
|
352
|
+
message="Share your site to start getting visitors."
|
|
353
|
+
icon="users"
|
|
354
|
+
/>
|
|
355
|
+
<EmptyState
|
|
356
|
+
title="No goals configured"
|
|
357
|
+
message="Set up conversion goals to track success."
|
|
358
|
+
icon="target"
|
|
359
|
+
action-label="Create Goal"
|
|
360
|
+
@action="() => console.log('Create goal clicked')"
|
|
361
|
+
/>
|
|
362
|
+
</div>
|
|
363
|
+
</section>
|
|
364
|
+
</main>
|
|
365
|
+
</div>
|
|
366
|
+
</template>
|
|
367
|
+
|
|
368
|
+
<style>
|
|
369
|
+
@import 'uno.css';
|
|
370
|
+
</style>
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Data for Dashboard Demo
|
|
3
|
+
*
|
|
4
|
+
* Realistic sample data for showcasing all dashboard components.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const mockData = {
|
|
8
|
+
// Stat Cards
|
|
9
|
+
stats: [
|
|
10
|
+
{ label: 'Total Visitors', value: 24847, change: 0.12, icon: 'users' },
|
|
11
|
+
{ label: 'Page Views', value: 89234, change: 0.08, icon: 'eye' },
|
|
12
|
+
{ label: 'Bounce Rate', value: '42.3%', change: -0.05, icon: 'arrow-down' },
|
|
13
|
+
{ label: 'Avg. Duration', value: '3m 24s', change: 0.15, icon: 'clock' },
|
|
14
|
+
],
|
|
15
|
+
|
|
16
|
+
// Mini Stats
|
|
17
|
+
miniStats: [
|
|
18
|
+
{ label: 'Sessions', value: 12847, change: 0.08 },
|
|
19
|
+
{ label: 'Users', value: 8234, change: 0.12 },
|
|
20
|
+
{ label: 'New Users', value: 3421, change: 0.24 },
|
|
21
|
+
{ label: 'Events', value: 45892, change: -0.03 },
|
|
22
|
+
],
|
|
23
|
+
|
|
24
|
+
// Comparison Metrics
|
|
25
|
+
comparisonMetrics: [
|
|
26
|
+
{ label: 'Visitors', current: 24847, previous: 22134, format: 'number' as const },
|
|
27
|
+
{ label: 'Bounce Rate', current: 0.423, previous: 0.456, format: 'percentage' as const, inverse: true },
|
|
28
|
+
{ label: 'Avg. Duration', current: 204000, previous: 178000, format: 'duration' as const },
|
|
29
|
+
{ label: 'Conversions', current: 847, previous: 723, format: 'number' as const },
|
|
30
|
+
],
|
|
31
|
+
|
|
32
|
+
// Time Series
|
|
33
|
+
timeSeries: Array.from({ length: 30 }, (_, i) => {
|
|
34
|
+
const date = new Date()
|
|
35
|
+
date.setDate(date.getDate() - (29 - i))
|
|
36
|
+
return {
|
|
37
|
+
date: date.toISOString().slice(0, 10),
|
|
38
|
+
visitors: Math.floor(Math.random() * 500) + 800,
|
|
39
|
+
pageViews: Math.floor(Math.random() * 1500) + 2000,
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
|
|
43
|
+
// Donut Chart
|
|
44
|
+
donutData: [
|
|
45
|
+
{ label: 'Direct', value: 4234, color: '#3b82f6' },
|
|
46
|
+
{ label: 'Organic Search', value: 3456, color: '#10b981' },
|
|
47
|
+
{ label: 'Social', value: 2134, color: '#8b5cf6' },
|
|
48
|
+
{ label: 'Referral', value: 1234, color: '#f59e0b' },
|
|
49
|
+
{ label: 'Email', value: 567, color: '#ef4444' },
|
|
50
|
+
],
|
|
51
|
+
|
|
52
|
+
// Bar Chart
|
|
53
|
+
barData: [
|
|
54
|
+
{ label: '/home', value: 12847 },
|
|
55
|
+
{ label: '/products', value: 8923 },
|
|
56
|
+
{ label: '/about', value: 5634 },
|
|
57
|
+
{ label: '/blog', value: 4521 },
|
|
58
|
+
{ label: '/contact', value: 2134 },
|
|
59
|
+
],
|
|
60
|
+
|
|
61
|
+
// Funnel Steps
|
|
62
|
+
funnelSteps: [
|
|
63
|
+
{ label: 'Visitors', value: 10000, color: '#3b82f6' },
|
|
64
|
+
{ label: 'Product Views', value: 6500, color: '#8b5cf6' },
|
|
65
|
+
{ label: 'Add to Cart', value: 3200, color: '#f59e0b' },
|
|
66
|
+
{ label: 'Checkout', value: 1800, color: '#10b981' },
|
|
67
|
+
{ label: 'Purchase', value: 850, color: '#ef4444' },
|
|
68
|
+
],
|
|
69
|
+
|
|
70
|
+
// Heatmap Data (7 days x 24 hours)
|
|
71
|
+
heatmapData: Array.from({ length: 7 }, (_, day) =>
|
|
72
|
+
Array.from({ length: 24 }, (_, hour) => ({
|
|
73
|
+
day,
|
|
74
|
+
hour,
|
|
75
|
+
value: Math.floor(Math.random() * 100),
|
|
76
|
+
}))).flat(),
|
|
77
|
+
|
|
78
|
+
// Sparklines
|
|
79
|
+
sparklines: [
|
|
80
|
+
{ label: 'Visitors', data: Array.from({ length: 14 }, () => Math.floor(Math.random() * 100) + 50), color: '#3b82f6' },
|
|
81
|
+
{ label: 'Conversions', data: Array.from({ length: 14 }, () => Math.floor(Math.random() * 30) + 10), color: '#10b981' },
|
|
82
|
+
{ label: 'Bounce Rate', data: Array.from({ length: 14 }, () => Math.floor(Math.random() * 20) + 30), color: '#ef4444' },
|
|
83
|
+
{ label: 'Duration', data: Array.from({ length: 14 }, () => Math.floor(Math.random() * 60) + 120), color: '#8b5cf6' },
|
|
84
|
+
],
|
|
85
|
+
|
|
86
|
+
// Device Breakdown
|
|
87
|
+
devices: [
|
|
88
|
+
{ type: 'desktop', count: 12847, percentage: 0.52 },
|
|
89
|
+
{ type: 'mobile', count: 9234, percentage: 0.37 },
|
|
90
|
+
{ type: 'tablet', count: 2766, percentage: 0.11 },
|
|
91
|
+
],
|
|
92
|
+
|
|
93
|
+
// Browser Breakdown
|
|
94
|
+
browsers: [
|
|
95
|
+
{ name: 'Chrome', count: 14523, percentage: 0.58, icon: 'chrome' },
|
|
96
|
+
{ name: 'Safari', count: 5234, percentage: 0.21, icon: 'safari' },
|
|
97
|
+
{ name: 'Firefox', count: 2847, percentage: 0.11, icon: 'firefox' },
|
|
98
|
+
{ name: 'Edge', count: 1823, percentage: 0.07, icon: 'edge' },
|
|
99
|
+
{ name: 'Other', count: 420, percentage: 0.03, icon: 'globe' },
|
|
100
|
+
],
|
|
101
|
+
|
|
102
|
+
// OS Breakdown
|
|
103
|
+
operatingSystems: [
|
|
104
|
+
{ name: 'Windows', count: 10234, percentage: 0.41 },
|
|
105
|
+
{ name: 'macOS', count: 7823, percentage: 0.31 },
|
|
106
|
+
{ name: 'iOS', count: 4234, percentage: 0.17 },
|
|
107
|
+
{ name: 'Android', count: 2134, percentage: 0.09 },
|
|
108
|
+
{ name: 'Linux', count: 422, percentage: 0.02 },
|
|
109
|
+
],
|
|
110
|
+
|
|
111
|
+
// Country List
|
|
112
|
+
countryList: [
|
|
113
|
+
{ name: 'United States', code: 'US', visitors: 8234, percentage: 0.33 },
|
|
114
|
+
{ name: 'United Kingdom', code: 'GB', visitors: 3456, percentage: 0.14 },
|
|
115
|
+
{ name: 'Germany', code: 'DE', visitors: 2847, percentage: 0.11 },
|
|
116
|
+
{ name: 'France', code: 'FR', visitors: 2134, percentage: 0.09 },
|
|
117
|
+
{ name: 'Canada', code: 'CA', visitors: 1823, percentage: 0.07 },
|
|
118
|
+
{ name: 'Australia', code: 'AU', visitors: 1234, percentage: 0.05 },
|
|
119
|
+
{ name: 'Japan', code: 'JP', visitors: 987, percentage: 0.04 },
|
|
120
|
+
{ name: 'Brazil', code: 'BR', visitors: 756, percentage: 0.03 },
|
|
121
|
+
],
|
|
122
|
+
|
|
123
|
+
// Campaigns
|
|
124
|
+
campaigns: [
|
|
125
|
+
{ name: 'Summer Sale', source: 'google', medium: 'cpc', visitors: 3456, conversions: 234, conversionRate: 0.068, revenue: 12340 },
|
|
126
|
+
{ name: 'Newsletter', source: 'email', medium: 'email', visitors: 2134, conversions: 187, conversionRate: 0.088, revenue: 8923 },
|
|
127
|
+
{ name: 'Social Launch', source: 'facebook', medium: 'social', visitors: 1823, conversions: 98, conversionRate: 0.054, revenue: 4521 },
|
|
128
|
+
{ name: 'Retargeting', source: 'google', medium: 'display', visitors: 1234, conversions: 76, conversionRate: 0.062, revenue: 3456 },
|
|
129
|
+
],
|
|
130
|
+
|
|
131
|
+
// Top Pages
|
|
132
|
+
topPages: [
|
|
133
|
+
{ name: '/home', value: 12847, change: 0.12 },
|
|
134
|
+
{ name: '/products', value: 8923, change: 0.08 },
|
|
135
|
+
{ name: '/products/widget', value: 5634, change: 0.24 },
|
|
136
|
+
{ name: '/about', value: 4521, change: -0.05 },
|
|
137
|
+
{ name: '/blog', value: 3847, change: 0.15 },
|
|
138
|
+
{ name: '/contact', value: 2134, change: 0.03 },
|
|
139
|
+
{ name: '/pricing', value: 1823, change: 0.18 },
|
|
140
|
+
{ name: '/docs', value: 1456, change: 0.09 },
|
|
141
|
+
],
|
|
142
|
+
|
|
143
|
+
// Top Referrers
|
|
144
|
+
topReferrers: [
|
|
145
|
+
{ name: 'google.com', value: 8234, change: 0.15 },
|
|
146
|
+
{ name: 'twitter.com', value: 3456, change: 0.22 },
|
|
147
|
+
{ name: 'facebook.com', value: 2847, change: -0.08 },
|
|
148
|
+
{ name: 'linkedin.com', value: 1823, change: 0.12 },
|
|
149
|
+
{ name: 'reddit.com', value: 1234, change: 0.45 },
|
|
150
|
+
{ name: 'github.com', value: 987, change: 0.08 },
|
|
151
|
+
],
|
|
152
|
+
|
|
153
|
+
// Table Data
|
|
154
|
+
tableColumns: [
|
|
155
|
+
{ key: 'page', label: 'Page', sortable: true },
|
|
156
|
+
{ key: 'visitors', label: 'Visitors', sortable: true, align: 'right' as const },
|
|
157
|
+
{ key: 'pageViews', label: 'Page Views', sortable: true, align: 'right' as const },
|
|
158
|
+
{ key: 'bounceRate', label: 'Bounce Rate', sortable: true, align: 'right' as const },
|
|
159
|
+
{ key: 'avgDuration', label: 'Avg. Duration', sortable: true, align: 'right' as const },
|
|
160
|
+
],
|
|
161
|
+
tableRows: [
|
|
162
|
+
{ page: '/home', visitors: 12847, pageViews: 24532, bounceRate: '38.2%', avgDuration: '2m 45s' },
|
|
163
|
+
{ page: '/products', visitors: 8923, pageViews: 15234, bounceRate: '42.1%', avgDuration: '3m 12s' },
|
|
164
|
+
{ page: '/about', visitors: 5634, pageViews: 7823, bounceRate: '52.3%', avgDuration: '1m 34s' },
|
|
165
|
+
{ page: '/blog', visitors: 4521, pageViews: 12847, bounceRate: '35.6%', avgDuration: '4m 56s' },
|
|
166
|
+
{ page: '/contact', visitors: 2134, pageViews: 3456, bounceRate: '48.7%', avgDuration: '1m 23s' },
|
|
167
|
+
],
|
|
168
|
+
|
|
169
|
+
// Page Detail
|
|
170
|
+
pageDetail: {
|
|
171
|
+
path: '/products/widget-pro',
|
|
172
|
+
title: 'Widget Pro - Premium Analytics Widget',
|
|
173
|
+
pageViews: 12847,
|
|
174
|
+
uniquePageViews: 8923,
|
|
175
|
+
avgTimeOnPage: 234000,
|
|
176
|
+
bounceRate: 0.382,
|
|
177
|
+
exitRate: 0.456,
|
|
178
|
+
entrances: 5634,
|
|
179
|
+
exits: 5823,
|
|
180
|
+
change: 0.15,
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
// Goals
|
|
184
|
+
goals: [
|
|
185
|
+
{ id: '1', name: 'Sign Up', conversions: 847, conversionRate: 0.034, value: 8470, target: 1000 },
|
|
186
|
+
{ id: '2', name: 'Purchase', conversions: 234, conversionRate: 0.0094, value: 23400, target: 300 },
|
|
187
|
+
{ id: '3', name: 'Newsletter', conversions: 1234, conversionRate: 0.05, value: 0, target: 1500 },
|
|
188
|
+
{ id: '4', name: 'Contact Form', conversions: 456, conversionRate: 0.018, value: 0, target: 500 },
|
|
189
|
+
],
|
|
190
|
+
|
|
191
|
+
// Engagement Metrics
|
|
192
|
+
engagementMetrics: {
|
|
193
|
+
avgSessionDuration: 234000,
|
|
194
|
+
pagesPerSession: 3.4,
|
|
195
|
+
bounceRate: 0.423,
|
|
196
|
+
returningVisitors: 0.34,
|
|
197
|
+
newVisitors: 0.66,
|
|
198
|
+
engagedSessions: 0.72,
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
// Live Activity
|
|
202
|
+
activities: [
|
|
203
|
+
{ id: '1', type: 'pageview' as const, page: '/products', country: 'United States', countryCode: 'US', device: 'desktop' as const, timestamp: new Date() },
|
|
204
|
+
{ id: '2', type: 'conversion' as const, eventName: 'Sign Up', country: 'Germany', countryCode: 'DE', device: 'mobile' as const, timestamp: new Date(Date.now() - 15000), value: 10 },
|
|
205
|
+
{ id: '3', type: 'event' as const, eventName: 'Add to Cart', country: 'United Kingdom', countryCode: 'GB', device: 'tablet' as const, timestamp: new Date(Date.now() - 30000) },
|
|
206
|
+
{ id: '4', type: 'session_start' as const, country: 'France', countryCode: 'FR', device: 'desktop' as const, timestamp: new Date(Date.now() - 45000) },
|
|
207
|
+
{ id: '5', type: 'pageview' as const, page: '/blog/analytics-tips', country: 'Canada', countryCode: 'CA', device: 'mobile' as const, timestamp: new Date(Date.now() - 60000) },
|
|
208
|
+
],
|
|
209
|
+
|
|
210
|
+
// Alerts
|
|
211
|
+
alerts: [
|
|
212
|
+
{ id: '1', title: 'Traffic Spike', message: 'Visitor count increased by 150% in the last hour.', type: 'success' as const, timestamp: new Date() },
|
|
213
|
+
{ id: '2', title: 'High Bounce Rate', message: '/landing page has 75% bounce rate today.', type: 'warning' as const, timestamp: new Date(Date.now() - 3600000) },
|
|
214
|
+
],
|
|
215
|
+
|
|
216
|
+
// Filters
|
|
217
|
+
filters: [
|
|
218
|
+
{ id: 'device', label: 'Device', options: ['All', 'Desktop', 'Mobile', 'Tablet'], value: 'All' },
|
|
219
|
+
{ id: 'country', label: 'Country', options: ['All', 'United States', 'United Kingdom', 'Germany'], value: 'All' },
|
|
220
|
+
{ id: 'source', label: 'Source', options: ['All', 'Direct', 'Organic', 'Social', 'Referral'], value: 'All' },
|
|
221
|
+
],
|
|
222
|
+
|
|
223
|
+
// Helper arrays
|
|
224
|
+
pages: ['/home', '/products', '/about', '/blog', '/contact', '/pricing', '/docs'],
|
|
225
|
+
countries: [
|
|
226
|
+
{ name: 'United States', code: 'US' },
|
|
227
|
+
{ name: 'United Kingdom', code: 'GB' },
|
|
228
|
+
{ name: 'Germany', code: 'DE' },
|
|
229
|
+
{ name: 'France', code: 'FR' },
|
|
230
|
+
{ name: 'Canada', code: 'CA' },
|
|
231
|
+
],
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export default mockData
|