@veristone/nuxt-v-app 0.1.0

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 (136) hide show
  1. package/README.md +42 -0
  2. package/app/app.vue +7 -0
  3. package/app/assets/css/v-app.css +313 -0
  4. package/app/components/V/A/Badge.vue +75 -0
  5. package/app/components/V/A/Btn/Add.vue +17 -0
  6. package/app/components/V/A/Btn/Back.vue +25 -0
  7. package/app/components/V/A/Btn/ConfirmDelete.vue +45 -0
  8. package/app/components/V/A/Btn/Edit.vue +35 -0
  9. package/app/components/V/A/Btn/Export.vue +28 -0
  10. package/app/components/V/A/Btn/Refresh.vue +21 -0
  11. package/app/components/V/A/Btn/Submit.vue +45 -0
  12. package/app/components/V/A/Btn/View.vue +23 -0
  13. package/app/components/V/A/Card.legacy.vue +291 -0
  14. package/app/components/V/A/Card.vue +108 -0
  15. package/app/components/V/A/CompanyMenu.vue +83 -0
  16. package/app/components/V/A/Data/KeyValue.vue +98 -0
  17. package/app/components/V/A/Data/StatusBadge.vue +44 -0
  18. package/app/components/V/A/DataField.vue +140 -0
  19. package/app/components/V/A/DataGrid.vue +43 -0
  20. package/app/components/V/A/DataTable.vue +144 -0
  21. package/app/components/V/A/EmptyState.vue +154 -0
  22. package/app/components/V/A/Fmt/Currency.vue +36 -0
  23. package/app/components/V/A/Fmt/DateTime.vue +34 -0
  24. package/app/components/V/A/Fmt/Percent.vue +47 -0
  25. package/app/components/V/A/LoadingState.vue +140 -0
  26. package/app/components/V/A/MetricCard.vue +129 -0
  27. package/app/components/V/A/Modal/Base.vue +195 -0
  28. package/app/components/V/A/Modal/Confirm.vue +92 -0
  29. package/app/components/V/A/Modal/Form.vue +105 -0
  30. package/app/components/V/A/Navigation.vue +110 -0
  31. package/app/components/V/A/QuickActions.vue +169 -0
  32. package/app/components/V/A/Slide.vue +109 -0
  33. package/app/components/V/A/Slideover.vue +259 -0
  34. package/app/components/V/A/State/Empty.vue +20 -0
  35. package/app/components/V/A/State/Error.vue +34 -0
  36. package/app/components/V/A/State/Loading.vue +33 -0
  37. package/app/components/V/A/StatsCard.vue +215 -0
  38. package/app/components/V/A/StatusBadge.vue +215 -0
  39. package/app/components/V/A/Table.vue +674 -0
  40. package/app/components/V/A/UserMenu.vue +127 -0
  41. package/app/components/V/A/WelcomeHeader.vue +96 -0
  42. package/app/components/V/Modal.vue +36 -0
  43. package/app/components/Va/Blocks/VaBlockGridCharts.vue +32 -0
  44. package/app/components/Va/Blocks/VaBlockGridKPI.vue +32 -0
  45. package/app/components/Va/Blocks/VaBlockGridTables.vue +23 -0
  46. package/app/components/Va/Blocks/VaBlockKpiGrid.vue +8 -0
  47. package/app/components/Va/Blocks/VaBlockSessionFilterBar.vue +8 -0
  48. package/app/components/Va/Cards/VaCardDonutChart.vue +59 -0
  49. package/app/components/Va/Cards/VaCardHeader.vue +10 -0
  50. package/app/components/Va/Cards/VaCardKpi.vue +17 -0
  51. package/app/components/Va/Cards/VaCardKpi2.vue +55 -0
  52. package/app/components/Va/Cards/VaCardLatestOrders.vue +82 -0
  53. package/app/components/Va/Cards/VaCardPopularProducts.vue +88 -0
  54. package/app/components/Va/Cards/VaCardRevenueBarChart.vue +49 -0
  55. package/app/components/Va/Cards/VaCardSubtitle.vue +5 -0
  56. package/app/components/Va/Cards/VaCardTitle.vue +5 -0
  57. package/app/components/Va/Cards/VaCardWithActiveUsers.vue +41 -0
  58. package/app/components/Va/Cards/VaCardWithChart.vue +135 -0
  59. package/app/components/Va/Cards/VaCardWithChartBlock.vue +26 -0
  60. package/app/components/Va/Cards/VaCardWithIndicator.vue +39 -0
  61. package/app/components/Va/Cards/VaCardWithProgressCircle.vue +34 -0
  62. package/app/components/Va/Cards/types.ts +11 -0
  63. package/app/components/Va/Charts/VaChartAppPerformanceBar.vue +118 -0
  64. package/app/components/Va/Charts/VaChartAppPerformanceBarChart.vue +118 -0
  65. package/app/components/Va/Charts/VaChartAreaMini.vue +127 -0
  66. package/app/components/Va/Charts/VaChartBarMini.vue +68 -0
  67. package/app/components/Va/Charts/VaChartCardinalMulti.vue +108 -0
  68. package/app/components/Va/Charts/VaChartColorBarChart.vue +78 -0
  69. package/app/components/Va/Charts/VaChartDonutHalf.vue +35 -0
  70. package/app/components/Va/Charts/VaChartDonutMini.vue +77 -0
  71. package/app/components/Va/Charts/VaChartExpensesBar.vue +58 -0
  72. package/app/components/Va/Charts/VaChartFinanceSummary.vue +96 -0
  73. package/app/components/Va/Charts/VaChartGoogleSearchConsole.vue +90 -0
  74. package/app/components/Va/Charts/VaChartIncomeBar.vue +82 -0
  75. package/app/components/Va/Charts/VaChartLegend.vue +25 -0
  76. package/app/components/Va/Charts/VaChartLineMini.vue +205 -0
  77. package/app/components/Va/Charts/VaChartRealtimeTraffic.vue +182 -0
  78. package/app/components/Va/Charts/VaChartRevenue.vue +43 -0
  79. package/app/components/Va/Charts/VaChartRevenueLine.vue +42 -0
  80. package/app/components/Va/Charts/VaChartRevenuevsCost.vue +84 -0
  81. package/app/components/Va/Charts/VaChartSearchIntent.vue +179 -0
  82. package/app/components/Va/Charts/VaChartSpendingTrend.vue +127 -0
  83. package/app/components/Va/Charts/VaChartStackedHorizontal.vue +64 -0
  84. package/app/components/Va/Charts/VaChartStepMinimal.vue +109 -0
  85. package/app/components/Va/Charts/VaChartStockComparisonLine.vue +86 -0
  86. package/app/components/Va/Charts/VaChartStocksPortfolioLine.vue +161 -0
  87. package/app/components/Va/Charts/VaChartStocksSectorLine.vue +223 -0
  88. package/app/components/Va/Charts/VaChartTasksCategories.vue +96 -0
  89. package/app/components/Va/Charts/VaChartTasksProgress.vue +130 -0
  90. package/app/components/Va/Charts/VaChartTrafficOverview.vue +112 -0
  91. package/app/components/Va/Charts/VaChartWebPerformanceLineChart.vue +114 -0
  92. package/app/components/Va/Charts/VaChartWinLostBar.vue +110 -0
  93. package/app/components/Va/Charts/VaChartWinLostDonut.vue +107 -0
  94. package/app/components/Va/Charts/VaChartWinLostLine.vue +111 -0
  95. package/app/components/Va/Charts/types.ts +10 -0
  96. package/app/components/Va/Dashboard/Navigation/types.ts +8 -0
  97. package/app/components/Va/Dashboard/VaDashboardKPICard.vue +31 -0
  98. package/app/components/Va/Dashboard/VaDashboardNavigation.vue +50 -0
  99. package/app/components/Va/Dashboard/VaDashboardPricePlan.vue +102 -0
  100. package/app/components/Va/Dashboard/VaDashboardUsageChart.vue +84 -0
  101. package/app/components/Va/Dashboard/VaDashboardUsageRequestChart.vue +46 -0
  102. package/app/components/Va/Layout/NotificationsSlideover.vue +169 -0
  103. package/app/components/Va/Layout/SideNav/types.ts +5 -0
  104. package/app/components/Va/Layout/SideNav.vue +108 -0
  105. package/app/components/Va/Layout/TeamsMenu.vue +57 -0
  106. package/app/components/Va/Layout/UserMenu.vue +57 -0
  107. package/app/composables/useDashboard.ts +25 -0
  108. package/app/composables/useVAAnimation.ts +324 -0
  109. package/app/composables/useVAUtils.ts +118 -0
  110. package/app/composables/useVCrud.ts +647 -0
  111. package/app/composables/useVFetch.ts +46 -0
  112. package/app/composables/useVFileUpload.ts +45 -0
  113. package/app/composables/useVToast.ts +73 -0
  114. package/app/composables/useXATableColumns.ts +456 -0
  115. package/app/data/BillingStats.ts +65 -0
  116. package/app/data/SearchData.ts +58 -0
  117. package/app/data/TasksData.ts +101 -0
  118. package/app/data/dashboardData.ts +113 -0
  119. package/app/layouts/default.vue +171 -0
  120. package/app/layouts/legacy.vue +61 -0
  121. package/app/pages/playground/base.vue +498 -0
  122. package/app/pages/playground/blocks.vue +108 -0
  123. package/app/pages/playground/buttons.vue +237 -0
  124. package/app/pages/playground/cards.vue +326 -0
  125. package/app/pages/playground/charts.vue +338 -0
  126. package/app/pages/playground/dashboard.vue +315 -0
  127. package/app/pages/playground/formatters.vue +329 -0
  128. package/app/pages/playground/index.vue +109 -0
  129. package/app/pages/playground/layout.vue +159 -0
  130. package/app/pages/playground/modals.vue +606 -0
  131. package/app/pages/playground/states.vue +282 -0
  132. package/app/pages/playground/tables.vue +618 -0
  133. package/app/pages/test-layout.vue +10 -0
  134. package/nuxt.config.ts +12 -0
  135. package/package.json +71 -0
  136. package/tsconfig.json +18 -0
@@ -0,0 +1,161 @@
1
+ <script lang="ts" setup>
2
+ interface DailyData {
3
+ day: string
4
+ portfolioValue: number
5
+ }
6
+
7
+ const chartData = ref<DailyData[]>([
8
+ {
9
+ day: 'Day 1',
10
+ portfolioValue: 150000
11
+ },
12
+ {
13
+ day: 'Day 2',
14
+ portfolioValue: 148000
15
+ },
16
+ {
17
+ day: 'Day 3',
18
+ portfolioValue: 147500
19
+ },
20
+ {
21
+ day: 'Day 4',
22
+ portfolioValue: 149000
23
+ },
24
+ {
25
+ day: 'Day 5',
26
+ portfolioValue: 148200
27
+ },
28
+ {
29
+ day: 'Day 6',
30
+ portfolioValue: 149800
31
+ },
32
+ {
33
+ day: 'Day 7',
34
+ portfolioValue: 148900
35
+ },
36
+ {
37
+ day: 'Day 8',
38
+ portfolioValue: 149500
39
+ },
40
+ {
41
+ day: 'Day 9',
42
+ portfolioValue: 150200
43
+ },
44
+ {
45
+ day: 'Day 10',
46
+ portfolioValue: 149800
47
+ },
48
+ {
49
+ day: 'Day 11',
50
+ portfolioValue: 150500
51
+ },
52
+ {
53
+ day: 'Day 12',
54
+ portfolioValue: 150800
55
+ },
56
+ {
57
+ day: 'Day 13',
58
+ portfolioValue: 151000
59
+ },
60
+ {
61
+ day: 'Day 14',
62
+ portfolioValue: 150700
63
+ },
64
+ {
65
+ day: 'Day 15',
66
+ portfolioValue: 151200
67
+ },
68
+ {
69
+ day: 'Day 16',
70
+ portfolioValue: 151500
71
+ },
72
+ {
73
+ day: 'Day 17',
74
+ portfolioValue: 151800
75
+ },
76
+ {
77
+ day: 'Day 18',
78
+ portfolioValue: 152000
79
+ },
80
+ {
81
+ day: 'Day 19',
82
+ portfolioValue: 153500
83
+ },
84
+ {
85
+ day: 'Day 20',
86
+ portfolioValue: 156000
87
+ },
88
+ {
89
+ day: 'Day 21',
90
+ portfolioValue: 158500
91
+ },
92
+ {
93
+ day: 'Day 22',
94
+ portfolioValue: 157000
95
+ },
96
+ {
97
+ day: 'Day 23',
98
+ portfolioValue: 158800
99
+ },
100
+ {
101
+ day: 'Day 24',
102
+ portfolioValue: 160000
103
+ },
104
+ {
105
+ day: 'Day 25',
106
+ portfolioValue: 161500
107
+ },
108
+ {
109
+ day: 'Day 26',
110
+ portfolioValue: 160000
111
+ },
112
+ {
113
+ day: 'Day 27',
114
+ portfolioValue: 162000
115
+ },
116
+ {
117
+ day: 'Day 28',
118
+ portfolioValue: 163500
119
+ },
120
+ {
121
+ day: 'Day 29',
122
+ portfolioValue: 165000
123
+ },
124
+ {
125
+ day: 'Day 30',
126
+ portfolioValue: 167000
127
+ }
128
+ ])
129
+
130
+ const chartCategories: Record<string, BulletLegendItemInterface> = {
131
+ portfolioValue: {
132
+ name: 'Portfolio Value',
133
+ color: '#10b981'
134
+ }
135
+ }
136
+
137
+ const xAxisFormatter = (tick: number): string => {
138
+ // tick is the index of the data point
139
+ const days = chartData.value.map(item => item.day)
140
+ return days[tick] || ''
141
+ }
142
+ </script>
143
+
144
+ <template>
145
+ <LineChart
146
+ :data="chartData"
147
+ :categories="chartCategories"
148
+ :y-axis="['portfolioValue']"
149
+ :x-formatter="xAxisFormatter"
150
+ :y-formatter="(i: number) => i / 1000 + 'K'"
151
+ :height="220"
152
+ :radius="3"
153
+ :group-padding="0.2"
154
+ :bar-padding="0.2"
155
+ :x-grid-line="true"
156
+ :y-grid-line="true"
157
+ :x-domain-line="true"
158
+ :y-domain-line="true"
159
+ :y-num-ticks="4"
160
+ />
161
+ </template>
@@ -0,0 +1,223 @@
1
+ <script lang="ts" setup>
2
+ // 1. Update the interface to include two stock categories
3
+ interface DailyData {
4
+ day: string
5
+ domestic: number
6
+ international: number
7
+ portfolioValue: number
8
+ }
9
+
10
+ // Defining a type for chart categories based on nuxt-charts' expected format
11
+ interface BulletLegendItemInterface {
12
+ name: string
13
+ color: string
14
+ }
15
+
16
+ // Function to generate sample daily values for the two categories
17
+ const generateCategoryData = (
18
+ baseValue: number,
19
+ dayIndex: number
20
+ ): [number, number] => {
21
+ // Introduce a stronger upward trend for each category, relative to the dayIndex
22
+ // and ensure their sum is close to the baseValue with minimal fluctuation
23
+ const domesticRatio = 0.85 + (dayIndex / 30) * 0.08 + Math.sin(dayIndex * 0.5) * 0.005 // Base 85%, stronger growth, very small fluctuation
24
+ const internationalRatio = 0.15 + (dayIndex / 30) * 0.04 + Math.cos(dayIndex * 0.6) * 0.002 // Base 15%, stronger growth, even smaller fluctuation
25
+
26
+ // Calculate raw values
27
+ let domestic = baseValue * domesticRatio
28
+ let international = baseValue * internationalRatio
29
+
30
+ // Normalize to ensure domestic + international equals baseValue
31
+ const currentSum = domestic + international
32
+ if (currentSum > 0) {
33
+ const normalizationFactor = baseValue / currentSum
34
+ domestic *= normalizationFactor
35
+ international *= normalizationFactor
36
+ }
37
+
38
+ return [
39
+ Math.round(domestic),
40
+ Math.round(international)
41
+ ]
42
+ }
43
+
44
+ const initialData: Omit<
45
+ DailyData,
46
+ 'domestic' | 'international'
47
+ >[] = [
48
+ {
49
+ day: 'Day 1',
50
+ portfolioValue: 150000
51
+ },
52
+ {
53
+ day: 'Day 2',
54
+ portfolioValue: 148000
55
+ },
56
+ {
57
+ day: 'Day 3',
58
+ portfolioValue: 147500
59
+ },
60
+ {
61
+ day: 'Day 4',
62
+ portfolioValue: 149000
63
+ },
64
+ {
65
+ day: 'Day 5',
66
+ portfolioValue: 148200
67
+ },
68
+ {
69
+ day: 'Day 6',
70
+ portfolioValue: 149800
71
+ },
72
+ {
73
+ day: 'Day 7',
74
+ portfolioValue: 148900
75
+ },
76
+ {
77
+ day: 'Day 8',
78
+ portfolioValue: 149500
79
+ },
80
+ {
81
+ day: 'Day 9',
82
+ portfolioValue: 150200
83
+ },
84
+ {
85
+ day: 'Day 10',
86
+ portfolioValue: 149800
87
+ },
88
+ {
89
+ day: 'Day 11',
90
+ portfolioValue: 150500
91
+ },
92
+ {
93
+ day: 'Day 12',
94
+ portfolioValue: 150800
95
+ },
96
+ {
97
+ day: 'Day 13',
98
+ portfolioValue: 151000
99
+ },
100
+ {
101
+ day: 'Day 14',
102
+ portfolioValue: 150700
103
+ },
104
+ {
105
+ day: 'Day 15',
106
+ portfolioValue: 151200
107
+ },
108
+ {
109
+ day: 'Day 16',
110
+ portfolioValue: 151500
111
+ },
112
+ {
113
+ day: 'Day 17',
114
+ portfolioValue: 151800
115
+ },
116
+ {
117
+ day: 'Day 18',
118
+ portfolioValue: 152000
119
+ },
120
+ {
121
+ day: 'Day 19',
122
+ portfolioValue: 153500
123
+ },
124
+ {
125
+ day: 'Day 20',
126
+ portfolioValue: 156000
127
+ },
128
+ {
129
+ day: 'Day 21',
130
+ portfolioValue: 158500
131
+ },
132
+ {
133
+ day: 'Day 22',
134
+ portfolioValue: 157000
135
+ },
136
+ {
137
+ day: 'Day 23',
138
+ portfolioValue: 158800
139
+ },
140
+ {
141
+ day: 'Day 24',
142
+ portfolioValue: 160000
143
+ },
144
+ {
145
+ day: 'Day 25',
146
+ portfolioValue: 161500
147
+ },
148
+ {
149
+ day: 'Day 26',
150
+ portfolioValue: 160000
151
+ },
152
+ {
153
+ day: 'Day 27',
154
+ portfolioValue: 162000
155
+ },
156
+ {
157
+ day: 'Day 28',
158
+ portfolioValue: 163500
159
+ },
160
+ {
161
+ day: 'Day 29',
162
+ portfolioValue: 165000
163
+ },
164
+ {
165
+ day: 'Day 30',
166
+ portfolioValue: 167000
167
+ }
168
+ ]
169
+
170
+ // 2. Generate the full chart data with categories
171
+ const chartData = ref<DailyData[]>(
172
+ initialData.map((item, index) => {
173
+ const [domestic, international] = generateCategoryData(
174
+ item.portfolioValue,
175
+ index
176
+ )
177
+ return {
178
+ ...item,
179
+ domestic,
180
+ international,
181
+ // Recalculate portfolioValue as the sum of categories for consistency
182
+ portfolioValue: domestic + international
183
+ }
184
+ })
185
+ )
186
+
187
+ // 3. Update categories with two dimensions and specified colors
188
+ const chartCategories: Record<string, BulletLegendItemInterface> = {
189
+ domestic: {
190
+ name: 'Domestic',
191
+ color: '#facc15' // amber-400
192
+ },
193
+ international: {
194
+ name: 'International',
195
+ color: '#d97706' // amber-600
196
+ }
197
+ }
198
+
199
+ const xAxisFormatter = (tick: number): string => {
200
+ // tick is the index of the data point
201
+ const days = chartData.value.map(item => item.day)
202
+ return days[tick] || ''
203
+ }
204
+ </script>
205
+
206
+ <template>
207
+ <LineChart
208
+ :data="chartData"
209
+ :categories="chartCategories"
210
+ :y-axis="['domestic', 'international']"
211
+ :x-formatter="xAxisFormatter"
212
+ :y-formatter="(i: number) => i / 1000 + 'K'"
213
+ :height="220"
214
+ :radius="3"
215
+ :group-padding="0.2"
216
+ :bar-padding="0.2"
217
+ :x-grid-line="true"
218
+ :y-grid-line="true"
219
+ :x-domain-line="true"
220
+ :y-domain-line="true"
221
+ :y-num-ticks="4"
222
+ />
223
+ </template>
@@ -0,0 +1,96 @@
1
+ <script lang="ts" setup>
2
+ import { TasksData } from '~/data/TasksData'
3
+
4
+ interface TaskStatusItem {
5
+ status: string
6
+ toDo: number
7
+ inProgress: number
8
+ blockedOnHold: number
9
+ }
10
+
11
+ // Generate task status breakdown data
12
+ const TaskStatusData: TaskStatusItem[] = [
13
+ {
14
+ status: 'Week 1',
15
+ toDo: TasksData.find(d => d.title === 'Backlog')?.tasks.length || 3,
16
+ inProgress: TasksData.find(d => d.title === 'In Progress')?.tasks.length || 2,
17
+ blockedOnHold: TasksData.find(d => d.title === 'In Review')?.tasks.length || 3
18
+ },
19
+ {
20
+ status: 'Week 2',
21
+ toDo: 5,
22
+ inProgress: 3,
23
+ blockedOnHold: 2
24
+ },
25
+ {
26
+ status: 'Week 3',
27
+ toDo: 4,
28
+ inProgress: 4,
29
+ blockedOnHold: 1
30
+ },
31
+ {
32
+ status: 'Week 4',
33
+ toDo: 6,
34
+ inProgress: 2,
35
+ blockedOnHold: 2
36
+ },
37
+ {
38
+ status: 'Week 5',
39
+ toDo: 3,
40
+ inProgress: 5,
41
+ blockedOnHold: 1
42
+ },
43
+ {
44
+ status: 'Week 6',
45
+ toDo: 2,
46
+ inProgress: 4,
47
+ blockedOnHold: 3
48
+ }
49
+ ]
50
+
51
+ const TaskStatusCategories = computed(() => ({
52
+ toDo: {
53
+ name: 'To Do',
54
+ color: '#CE824E'
55
+ },
56
+ inProgress: {
57
+ name: 'In Progress',
58
+ color: '#5D5AA1'
59
+ },
60
+ blockedOnHold: {
61
+ name: 'Blocked / On Hold',
62
+ color: '#3C3C3C'
63
+ }
64
+ }))
65
+
66
+ const xFormatter = (i: number): string => `${TaskStatusData[i]?.status}`
67
+ const yFormatter = (value: number): string => `${value}`
68
+ </script>
69
+
70
+ <template>
71
+ <div>
72
+ <BarChart
73
+ :data="TaskStatusData"
74
+ :height="320"
75
+ :categories="TaskStatusCategories"
76
+ :y-axis="['toDo', 'inProgress', 'blockedOnHold']"
77
+ :x-formatter="xFormatter"
78
+ :y-formatter="yFormatter"
79
+ :legend-position="LegendPosition.TopRight"
80
+ :hide-legend="false"
81
+ :y-grid-line="true"
82
+ :x-grid-line="false"
83
+ :y-domain-line="false"
84
+ :x-domain-line="false"
85
+ :y-tick-line="false"
86
+ :x-tick-line="false"
87
+ :radius="6"
88
+ :group-padding="0.15"
89
+ :bar-padding="0.2"
90
+ :x-num-ticks="6"
91
+ :y-num-ticks="6"
92
+ x-label="Time Period"
93
+ y-label="Number of Tasks"
94
+ />
95
+ </div>
96
+ </template>
@@ -0,0 +1,130 @@
1
+ <script lang="ts" setup>
2
+ const colorMode = useColorMode()
3
+
4
+ interface BurndownItem {
5
+ day: string
6
+ ideal: number
7
+ actual: number
8
+ }
9
+
10
+ // Burndown chart data - shows remaining tasks over time
11
+ const SystemMetricsData: BurndownItem[] = [
12
+ {
13
+ day: 'Day 1',
14
+ ideal: 120,
15
+ actual: 120
16
+ },
17
+ {
18
+ day: 'Day 2',
19
+ ideal: 110,
20
+ actual: 110
21
+ },
22
+ {
23
+ day: 'Day 3',
24
+ ideal: 100,
25
+ actual: 110
26
+ },
27
+ {
28
+ day: 'Day 4',
29
+ ideal: 90,
30
+ actual: 100
31
+ },
32
+ {
33
+ day: 'Day 5',
34
+ ideal: 80,
35
+ actual: 80
36
+ },
37
+ {
38
+ day: 'Day 6',
39
+ ideal: 70,
40
+ actual: 90
41
+ },
42
+ {
43
+ day: 'Day 7',
44
+ ideal: 60,
45
+ actual: 90
46
+ },
47
+ {
48
+ day: 'Day 8',
49
+ ideal: 50,
50
+ actual: 70
51
+ },
52
+ {
53
+ day: 'Day 9',
54
+ ideal: 40,
55
+ actual: 60
56
+ },
57
+ {
58
+ day: 'Day 10',
59
+ ideal: 30,
60
+ actual: 35
61
+ },
62
+ {
63
+ day: 'Day 11',
64
+ ideal: 20,
65
+ actual: 30
66
+ },
67
+ {
68
+ day: 'Day 12',
69
+ ideal: 10,
70
+ actual: 30
71
+ },
72
+ {
73
+ day: 'Day 13',
74
+ ideal: 0,
75
+ actual: 20
76
+ },
77
+ {
78
+ day: 'Day 14',
79
+ ideal: 0,
80
+ actual: 15
81
+ }
82
+ ]
83
+
84
+ const SystemCategories = computed(() => ({
85
+ ideal: {
86
+ name: 'Ideal Progress',
87
+ color: colorMode.value === 'dark' ? '#5D5AA1' : '#5D5AA1'
88
+ },
89
+ actual: {
90
+ name: 'Actual Progress',
91
+ color: colorMode.value === 'dark' ? '#CE824E' : '#CE824E'
92
+ }
93
+ }))
94
+
95
+ const xFormatter = (i: number): string => `${SystemMetricsData[i]?.day}`
96
+ const yFormatter = (value: number): string => `${value}`
97
+
98
+ const GradientStops = [
99
+ {
100
+ offset: '0%',
101
+ stopOpacity: 1
102
+ },
103
+ {
104
+ offset: '50%',
105
+ stopOpacity: 0
106
+ }
107
+ ]
108
+ </script>
109
+
110
+ <template>
111
+ <AreaChart
112
+ :key="colorMode.value"
113
+ :data="SystemMetricsData"
114
+ :height="320"
115
+ :categories="SystemCategories"
116
+ :curve-type="CurveType.StepAfter"
117
+ :x-formatter="xFormatter"
118
+ :y-formatter="yFormatter"
119
+ :legend-position="LegendPosition.TopRight"
120
+ :hide-legend="false"
121
+ :y-grid-line="true"
122
+ :x-grid-line="true"
123
+ :y-domain-line="true"
124
+ :x-domain-line="true"
125
+ :x-tick-line="true"
126
+ :y-tick-line="false"
127
+ x-label="Sprint Days"
128
+ :gradient-stops="GradientStops"
129
+ />
130
+ </template>
@@ -0,0 +1,112 @@
1
+ <script lang="ts" setup>
2
+ // Use useSlots() instead of defineSlots() to avoid Vue 3.5 runtime error
3
+ const slots = useSlots()
4
+
5
+ const daysInPeriod = 31
6
+ const avgEngagementMinutes = 4.5
7
+
8
+ function getDateStr(dayOffset: number) {
9
+ const date = new Date()
10
+
11
+ date.setDate(date.getDate() - (daysInPeriod - 1) + dayOffset)
12
+ return date.toLocaleDateString('en-US', {
13
+ month: 'short',
14
+ day: '2-digit'
15
+ })
16
+ }
17
+
18
+ const engagementData = Array.from({ length: daysInPeriod }, (_, i) => {
19
+ const dailyEngagement = avgEngagementMinutes + (Math.random() - 0.5) * 3
20
+ return {
21
+ date: getDateStr(i),
22
+ Engagement: Math.round(dailyEngagement * 10) / 10
23
+ }
24
+ })
25
+
26
+ // Previous period data (same range, previous period)
27
+ function getPrevDateStr(dayOffset: number) {
28
+ const date = new Date()
29
+ date.setDate(date.getDate() - daysInPeriod - (daysInPeriod - 1) + dayOffset)
30
+ return date.toLocaleDateString('en-US', {
31
+ month: 'short',
32
+ day: '2-digit'
33
+ })
34
+ }
35
+
36
+ const prevEngagementData = Array.from({ length: daysInPeriod }, (_, i) => {
37
+ // Simulate previous period with slightly lower engagement
38
+ const dailyEngagement
39
+ = avgEngagementMinutes - 0.7 + (Math.random() - 0.5) * 3
40
+ return {
41
+ 'date': getPrevDateStr(i),
42
+ 'Previous Period': Math.round(dailyEngagement * 10) / 10
43
+ }
44
+ })
45
+
46
+ // Merge both datasets by date
47
+ const mergedData = engagementData.map((curr, i) => ({
48
+ 'date': curr.date,
49
+ 'Engagement': curr.Engagement,
50
+ 'Previous Period': prevEngagementData[i]?.['Previous Period'] ?? null
51
+ }))
52
+
53
+ interface Category {
54
+ name: string
55
+ color: string
56
+ }
57
+ const categories: Record<string, Category> = {
58
+ 'Engagement': {
59
+ name: 'Avg. Engagement (min)',
60
+ color: 'var(--ui-primary)'
61
+ },
62
+ 'Previous Period': {
63
+ name: 'Previous Period',
64
+ color: 'var(--color-amber-400)'
65
+ }
66
+ }
67
+
68
+ const xFormatter = (i: number) => `${engagementData[i]?.date}`
69
+
70
+ const yFormatter = (value: number) => value + 'K'
71
+ </script>
72
+
73
+ <template>
74
+ <UCard :ui="{ footer: 'flex justify-end' }">
75
+ <template #header>
76
+ <div class="space-y-2">
77
+ <CardsSubtitle>Total Users</CardsSubtitle>
78
+ <CardsTitle>135K</CardsTitle>
79
+ </div>
80
+
81
+ <UButton
82
+ color="success"
83
+ variant="soft"
84
+ size="sm"
85
+ icon="i-lucide-circle-check"
86
+ aria-label="Status OK"
87
+ class="shrink-0"
88
+ />
89
+ </template>
90
+
91
+ <LineChart
92
+ :data="mergedData"
93
+ :height="200"
94
+ :categories="categories"
95
+ :y-num-ticks="3"
96
+ :x-formatter="xFormatter"
97
+ :y-formatter="yFormatter"
98
+ :y-min="0"
99
+ :y-max="600000"
100
+ :x-grid-line="true"
101
+ :legend-position="LegendPosition.BottomCenter"
102
+ :curve-type="CurveType.Linear"
103
+ />
104
+
105
+ <template
106
+ v-if="slots.action"
107
+ #footer
108
+ >
109
+ <slot name="action" />
110
+ </template>
111
+ </UCard>
112
+ </template>