@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.
- package/README.md +42 -0
- package/app/app.vue +7 -0
- package/app/assets/css/v-app.css +313 -0
- package/app/components/V/A/Badge.vue +75 -0
- package/app/components/V/A/Btn/Add.vue +17 -0
- package/app/components/V/A/Btn/Back.vue +25 -0
- package/app/components/V/A/Btn/ConfirmDelete.vue +45 -0
- package/app/components/V/A/Btn/Edit.vue +35 -0
- package/app/components/V/A/Btn/Export.vue +28 -0
- package/app/components/V/A/Btn/Refresh.vue +21 -0
- package/app/components/V/A/Btn/Submit.vue +45 -0
- package/app/components/V/A/Btn/View.vue +23 -0
- package/app/components/V/A/Card.legacy.vue +291 -0
- package/app/components/V/A/Card.vue +108 -0
- package/app/components/V/A/CompanyMenu.vue +83 -0
- package/app/components/V/A/Data/KeyValue.vue +98 -0
- package/app/components/V/A/Data/StatusBadge.vue +44 -0
- package/app/components/V/A/DataField.vue +140 -0
- package/app/components/V/A/DataGrid.vue +43 -0
- package/app/components/V/A/DataTable.vue +144 -0
- package/app/components/V/A/EmptyState.vue +154 -0
- package/app/components/V/A/Fmt/Currency.vue +36 -0
- package/app/components/V/A/Fmt/DateTime.vue +34 -0
- package/app/components/V/A/Fmt/Percent.vue +47 -0
- package/app/components/V/A/LoadingState.vue +140 -0
- package/app/components/V/A/MetricCard.vue +129 -0
- package/app/components/V/A/Modal/Base.vue +195 -0
- package/app/components/V/A/Modal/Confirm.vue +92 -0
- package/app/components/V/A/Modal/Form.vue +105 -0
- package/app/components/V/A/Navigation.vue +110 -0
- package/app/components/V/A/QuickActions.vue +169 -0
- package/app/components/V/A/Slide.vue +109 -0
- package/app/components/V/A/Slideover.vue +259 -0
- package/app/components/V/A/State/Empty.vue +20 -0
- package/app/components/V/A/State/Error.vue +34 -0
- package/app/components/V/A/State/Loading.vue +33 -0
- package/app/components/V/A/StatsCard.vue +215 -0
- package/app/components/V/A/StatusBadge.vue +215 -0
- package/app/components/V/A/Table.vue +674 -0
- package/app/components/V/A/UserMenu.vue +127 -0
- package/app/components/V/A/WelcomeHeader.vue +96 -0
- package/app/components/V/Modal.vue +36 -0
- package/app/components/Va/Blocks/VaBlockGridCharts.vue +32 -0
- package/app/components/Va/Blocks/VaBlockGridKPI.vue +32 -0
- package/app/components/Va/Blocks/VaBlockGridTables.vue +23 -0
- package/app/components/Va/Blocks/VaBlockKpiGrid.vue +8 -0
- package/app/components/Va/Blocks/VaBlockSessionFilterBar.vue +8 -0
- package/app/components/Va/Cards/VaCardDonutChart.vue +59 -0
- package/app/components/Va/Cards/VaCardHeader.vue +10 -0
- package/app/components/Va/Cards/VaCardKpi.vue +17 -0
- package/app/components/Va/Cards/VaCardKpi2.vue +55 -0
- package/app/components/Va/Cards/VaCardLatestOrders.vue +82 -0
- package/app/components/Va/Cards/VaCardPopularProducts.vue +88 -0
- package/app/components/Va/Cards/VaCardRevenueBarChart.vue +49 -0
- package/app/components/Va/Cards/VaCardSubtitle.vue +5 -0
- package/app/components/Va/Cards/VaCardTitle.vue +5 -0
- package/app/components/Va/Cards/VaCardWithActiveUsers.vue +41 -0
- package/app/components/Va/Cards/VaCardWithChart.vue +135 -0
- package/app/components/Va/Cards/VaCardWithChartBlock.vue +26 -0
- package/app/components/Va/Cards/VaCardWithIndicator.vue +39 -0
- package/app/components/Va/Cards/VaCardWithProgressCircle.vue +34 -0
- package/app/components/Va/Cards/types.ts +11 -0
- package/app/components/Va/Charts/VaChartAppPerformanceBar.vue +118 -0
- package/app/components/Va/Charts/VaChartAppPerformanceBarChart.vue +118 -0
- package/app/components/Va/Charts/VaChartAreaMini.vue +127 -0
- package/app/components/Va/Charts/VaChartBarMini.vue +68 -0
- package/app/components/Va/Charts/VaChartCardinalMulti.vue +108 -0
- package/app/components/Va/Charts/VaChartColorBarChart.vue +78 -0
- package/app/components/Va/Charts/VaChartDonutHalf.vue +35 -0
- package/app/components/Va/Charts/VaChartDonutMini.vue +77 -0
- package/app/components/Va/Charts/VaChartExpensesBar.vue +58 -0
- package/app/components/Va/Charts/VaChartFinanceSummary.vue +96 -0
- package/app/components/Va/Charts/VaChartGoogleSearchConsole.vue +90 -0
- package/app/components/Va/Charts/VaChartIncomeBar.vue +82 -0
- package/app/components/Va/Charts/VaChartLegend.vue +25 -0
- package/app/components/Va/Charts/VaChartLineMini.vue +205 -0
- package/app/components/Va/Charts/VaChartRealtimeTraffic.vue +182 -0
- package/app/components/Va/Charts/VaChartRevenue.vue +43 -0
- package/app/components/Va/Charts/VaChartRevenueLine.vue +42 -0
- package/app/components/Va/Charts/VaChartRevenuevsCost.vue +84 -0
- package/app/components/Va/Charts/VaChartSearchIntent.vue +179 -0
- package/app/components/Va/Charts/VaChartSpendingTrend.vue +127 -0
- package/app/components/Va/Charts/VaChartStackedHorizontal.vue +64 -0
- package/app/components/Va/Charts/VaChartStepMinimal.vue +109 -0
- package/app/components/Va/Charts/VaChartStockComparisonLine.vue +86 -0
- package/app/components/Va/Charts/VaChartStocksPortfolioLine.vue +161 -0
- package/app/components/Va/Charts/VaChartStocksSectorLine.vue +223 -0
- package/app/components/Va/Charts/VaChartTasksCategories.vue +96 -0
- package/app/components/Va/Charts/VaChartTasksProgress.vue +130 -0
- package/app/components/Va/Charts/VaChartTrafficOverview.vue +112 -0
- package/app/components/Va/Charts/VaChartWebPerformanceLineChart.vue +114 -0
- package/app/components/Va/Charts/VaChartWinLostBar.vue +110 -0
- package/app/components/Va/Charts/VaChartWinLostDonut.vue +107 -0
- package/app/components/Va/Charts/VaChartWinLostLine.vue +111 -0
- package/app/components/Va/Charts/types.ts +10 -0
- package/app/components/Va/Dashboard/Navigation/types.ts +8 -0
- package/app/components/Va/Dashboard/VaDashboardKPICard.vue +31 -0
- package/app/components/Va/Dashboard/VaDashboardNavigation.vue +50 -0
- package/app/components/Va/Dashboard/VaDashboardPricePlan.vue +102 -0
- package/app/components/Va/Dashboard/VaDashboardUsageChart.vue +84 -0
- package/app/components/Va/Dashboard/VaDashboardUsageRequestChart.vue +46 -0
- package/app/components/Va/Layout/NotificationsSlideover.vue +169 -0
- package/app/components/Va/Layout/SideNav/types.ts +5 -0
- package/app/components/Va/Layout/SideNav.vue +108 -0
- package/app/components/Va/Layout/TeamsMenu.vue +57 -0
- package/app/components/Va/Layout/UserMenu.vue +57 -0
- package/app/composables/useDashboard.ts +25 -0
- package/app/composables/useVAAnimation.ts +324 -0
- package/app/composables/useVAUtils.ts +118 -0
- package/app/composables/useVCrud.ts +647 -0
- package/app/composables/useVFetch.ts +46 -0
- package/app/composables/useVFileUpload.ts +45 -0
- package/app/composables/useVToast.ts +73 -0
- package/app/composables/useXATableColumns.ts +456 -0
- package/app/data/BillingStats.ts +65 -0
- package/app/data/SearchData.ts +58 -0
- package/app/data/TasksData.ts +101 -0
- package/app/data/dashboardData.ts +113 -0
- package/app/layouts/default.vue +171 -0
- package/app/layouts/legacy.vue +61 -0
- package/app/pages/playground/base.vue +498 -0
- package/app/pages/playground/blocks.vue +108 -0
- package/app/pages/playground/buttons.vue +237 -0
- package/app/pages/playground/cards.vue +326 -0
- package/app/pages/playground/charts.vue +338 -0
- package/app/pages/playground/dashboard.vue +315 -0
- package/app/pages/playground/formatters.vue +329 -0
- package/app/pages/playground/index.vue +109 -0
- package/app/pages/playground/layout.vue +159 -0
- package/app/pages/playground/modals.vue +606 -0
- package/app/pages/playground/states.vue +282 -0
- package/app/pages/playground/tables.vue +618 -0
- package/app/pages/test-layout.vue +10 -0
- package/nuxt.config.ts +12 -0
- package/package.json +71 -0
- 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>
|