@littlebearapps/platform-admin-sdk 2.1.0 → 2.3.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 (122) hide show
  1. package/README.md +2 -5
  2. package/dist/check-upgrade.d.ts +29 -0
  3. package/dist/check-upgrade.js +97 -0
  4. package/dist/index.js +59 -4
  5. package/dist/manifest.d.ts +2 -0
  6. package/dist/scaffold.js +5 -1
  7. package/dist/templates.d.ts +6 -1
  8. package/dist/templates.js +141 -3
  9. package/dist/upgrade.d.ts +1 -0
  10. package/dist/upgrade.js +21 -2
  11. package/package.json +1 -1
  12. package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
  13. package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
  14. package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
  15. package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
  16. package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
  17. package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
  18. package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
  19. package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
  20. package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
  21. package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
  22. package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
  23. package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
  24. package/templates/full/dashboard/src/pages/feedback.astro +365 -0
  25. package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
  26. package/templates/full/dashboard/src/pages/map.astro +561 -0
  27. package/templates/full/dashboard/src/pages/revenue.astro +72 -0
  28. package/templates/full/dashboard/src/pages/tests.astro +431 -0
  29. package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
  30. package/templates/full/scripts/ops/verify-account-total.ts +256 -0
  31. package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
  32. package/templates/full/tests/integration/r2-archive.test.ts +108 -0
  33. package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
  34. package/templates/shared/.github/workflows/validate-controls.yml +27 -0
  35. package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
  36. package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
  37. package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
  38. package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
  39. package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
  40. package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
  41. package/templates/shared/dashboard/src/components/Toast.astro +170 -0
  42. package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
  43. package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
  44. package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
  45. package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
  46. package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
  47. package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
  48. package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
  49. package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
  50. package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
  51. package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
  52. package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
  53. package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
  54. package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
  55. package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
  56. package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
  57. package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
  58. package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
  59. package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
  60. package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
  61. package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
  62. package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
  63. package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
  64. package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
  65. package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
  66. package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
  67. package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
  68. package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
  69. package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
  70. package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
  71. package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
  72. package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
  73. package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
  74. package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
  75. package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
  76. package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
  77. package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
  78. package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
  79. package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
  80. package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
  81. package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
  82. package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
  83. package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
  84. package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
  85. package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
  86. package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
  87. package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
  88. package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
  89. package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
  90. package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
  91. package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
  92. package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
  93. package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
  94. package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
  95. package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
  96. package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
  97. package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
  98. package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
  99. package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
  100. package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
  101. package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
  102. package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
  103. package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
  104. package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
  105. package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
  106. package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
  107. package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
  108. package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
  109. package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
  110. package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
  111. package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
  112. package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
  113. package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
  114. package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
  115. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
  116. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
  117. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
  118. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
  119. package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
  120. package/templates/standard/tests/integration/connectors.test.ts +241 -0
  121. package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
  122. package/templates/standard/tests/integration/ingestion.test.ts +211 -0
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Usage Dashboard Actions
3
+ *
4
+ * Functions for interacting with the usage store.
5
+ * Handles API fetching, URL sync, and Melbourne timezone formatting.
6
+ * Part of task-19: Usage Dashboard Refactor Phase 1 - Foundation
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { syncFromURL, initSubscriptions, fetchDailyData } from './usageActions';
11
+ *
12
+ * // On page load
13
+ * syncFromURL();
14
+ * initSubscriptions();
15
+ *
16
+ * // Manually fetch data
17
+ * await fetchDailyData();
18
+ * ```
19
+ */
20
+
21
+ import {
22
+ $period,
23
+ $project,
24
+ $customDateRange,
25
+ $dailyData,
26
+ $isLoading,
27
+ $hasLegacyData,
28
+ $selectedDate,
29
+ $projects,
30
+ $allowances,
31
+ $projectedCost,
32
+ $projectsLoading,
33
+ $githubUsage,
34
+ $error,
35
+ type Period,
36
+ type ProjectData,
37
+ type AllowancesData,
38
+ type ProjectedCost,
39
+ type GitHubUsageData,
40
+ } from './usageStore';
41
+
42
+ // ============================================================================
43
+ // Melbourne Timezone Formatters
44
+ // ============================================================================
45
+
46
+ /**
47
+ * Format a date for Melbourne timezone (DD/MM/YYYY)
48
+ */
49
+ export function formatDateMelbourne(date: Date): string {
50
+ return date.toLocaleDateString('en-AU', {
51
+ timeZone: 'Australia/Melbourne',
52
+ year: 'numeric',
53
+ month: '2-digit',
54
+ day: '2-digit',
55
+ });
56
+ }
57
+
58
+ /**
59
+ * Format a date-time for Melbourne timezone
60
+ */
61
+ export function formatDateTimeMelbourne(date: Date): string {
62
+ return date.toLocaleString('en-AU', {
63
+ timeZone: 'Australia/Melbourne',
64
+ dateStyle: 'medium',
65
+ timeStyle: 'short',
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Format a date for display (e.g., "7 Jan")
71
+ */
72
+ export function formatDateShort(date: Date): string {
73
+ return date.toLocaleDateString('en-AU', {
74
+ timeZone: 'Australia/Melbourne',
75
+ day: 'numeric',
76
+ month: 'short',
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Get current Melbourne date as YYYY-MM-DD
82
+ */
83
+ export function getMelbourneDateString(): string {
84
+ const now = new Date();
85
+ return now.toLocaleDateString('en-CA', {
86
+ timeZone: 'Australia/Melbourne',
87
+ });
88
+ }
89
+
90
+ // ============================================================================
91
+ // URL Sync
92
+ // ============================================================================
93
+
94
+ /**
95
+ * Sync store state FROM URL (on page load)
96
+ */
97
+ export function syncFromURL(): void {
98
+ if (typeof window === 'undefined') return;
99
+
100
+ const params = new URLSearchParams(window.location.search);
101
+
102
+ // Period
103
+ const period = params.get('period') as Period | null;
104
+ if (period && ['24h', '7d', '30d', 'custom'].includes(period)) {
105
+ $period.set(period);
106
+ }
107
+
108
+ // Project
109
+ const project = params.get('project');
110
+ if (project) {
111
+ $project.set(project);
112
+ }
113
+
114
+ // Custom date range
115
+ const start = params.get('startDate');
116
+ const end = params.get('endDate');
117
+ if (start && end) {
118
+ $customDateRange.set({ start, end });
119
+ }
120
+
121
+ // Selected date
122
+ const selectedDate = params.get('selectedDate');
123
+ if (selectedDate) {
124
+ $selectedDate.set(selectedDate);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Sync store state TO URL (on state change)
130
+ */
131
+ export function syncToURL(): void {
132
+ if (typeof window === 'undefined') return;
133
+
134
+ const url = new URL(window.location.href);
135
+
136
+ // Period
137
+ url.searchParams.set('period', $period.get());
138
+
139
+ // Project
140
+ const project = $project.get();
141
+ if (project && project !== 'all') {
142
+ url.searchParams.set('project', project);
143
+ } else {
144
+ url.searchParams.delete('project');
145
+ }
146
+
147
+ // Custom date range
148
+ const { start, end } = $customDateRange.get();
149
+ if ($period.get() === 'custom' && start && end) {
150
+ url.searchParams.set('startDate', start);
151
+ url.searchParams.set('endDate', end);
152
+ } else {
153
+ url.searchParams.delete('startDate');
154
+ url.searchParams.delete('endDate');
155
+ }
156
+
157
+ // Selected date
158
+ const selected = $selectedDate.get();
159
+ if (selected) {
160
+ url.searchParams.set('selectedDate', selected);
161
+ } else {
162
+ url.searchParams.delete('selectedDate');
163
+ }
164
+
165
+ window.history.replaceState({}, '', url.toString());
166
+ }
167
+
168
+ // ============================================================================
169
+ // Data Fetching
170
+ // ============================================================================
171
+
172
+ /**
173
+ * Fetch daily data from API
174
+ *
175
+ * Uses /api/usage/daily endpoint which returns DailyCostData with:
176
+ * - days: Array of DailyCostBreakdown (date, services, total)
177
+ * - summary: { totalCost, averageDailyCost, highestDay, lowestDay }
178
+ */
179
+ export async function fetchDailyData(options?: { nocache?: boolean }): Promise<void> {
180
+ $isLoading.set(true);
181
+
182
+ try {
183
+ const params = new URLSearchParams({
184
+ period: $period.get(),
185
+ });
186
+
187
+ const { start, end } = $customDateRange.get();
188
+ if ($period.get() === 'custom' && start && end) {
189
+ params.set('startDate', start);
190
+ params.set('endDate', end);
191
+ }
192
+
193
+ // Add nocache parameter if requested (bypasses KV cache)
194
+ if (options?.nocache) {
195
+ params.set('nocache', 'true');
196
+ }
197
+
198
+ // Fetch from /api/usage/daily endpoint (not /api/usage)
199
+ // Include credentials to pass Cloudflare Access JWT cookie
200
+ const response = await fetch(`/api/usage/daily?${params}`, {
201
+ credentials: 'include',
202
+ });
203
+
204
+ if (!response.ok) {
205
+ throw new Error(`API error: ${response.status}`);
206
+ }
207
+
208
+ const json = await response.json();
209
+
210
+ // The /api/usage/daily endpoint returns { success, period, data: DailyCostData, cached, timestamp }
211
+ // DailyCostData has { days: DailyCostBreakdown[], summary: {...}, hasLegacyData?: boolean }
212
+ if (json.success && json.data?.days) {
213
+ $dailyData.set(json.data.days);
214
+ // Set legacy data flag - true if any day has rollupVersion=1 (inflated SUM aggregation)
215
+ $hasLegacyData.set(json.data.hasLegacyData === true);
216
+ } else {
217
+ console.warn('Daily data response missing expected structure:', json);
218
+ $dailyData.set([]);
219
+ $hasLegacyData.set(false);
220
+ }
221
+ } catch (error) {
222
+ console.error('Failed to fetch daily data:', error);
223
+ $dailyData.set([]);
224
+ $hasLegacyData.set(false);
225
+ } finally {
226
+ $isLoading.set(false);
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Fetch projects, allowances, and projected cost from API
232
+ *
233
+ * Uses /api/usage/projects endpoint which returns:
234
+ * - projects: Array of ProjectData with resourceCount
235
+ * - allowances: Service allowances object
236
+ * - projectedCost: MTD cost and projection
237
+ */
238
+ export async function fetchProjects(): Promise<void> {
239
+ $projectsLoading.set(true);
240
+ $error.set(null);
241
+
242
+ try {
243
+ const response = await fetch('/api/usage/projects', {
244
+ credentials: 'include',
245
+ });
246
+
247
+ if (!response.ok) {
248
+ throw new Error(`API error: ${response.status}`);
249
+ }
250
+
251
+ const json = await response.json();
252
+
253
+ if (json.success) {
254
+ // Update projects store
255
+ if (json.projects) {
256
+ $projects.set(json.projects as ProjectData[]);
257
+ }
258
+
259
+ // Update allowances store
260
+ if (json.allowances) {
261
+ $allowances.set(json.allowances as AllowancesData);
262
+ }
263
+
264
+ // Update projected cost store
265
+ if (json.projectedCost) {
266
+ $projectedCost.set(json.projectedCost as ProjectedCost);
267
+ }
268
+ } else {
269
+ console.warn('Projects response missing success:', json);
270
+ $error.set(json.error || 'Failed to fetch projects');
271
+ }
272
+ } catch (error) {
273
+ console.error('Failed to fetch projects:', error);
274
+ $error.set(error instanceof Error ? error.message : 'Failed to fetch projects');
275
+ } finally {
276
+ $projectsLoading.set(false);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Fetch GitHub usage data from utilisation endpoint
282
+ *
283
+ * Uses /api/usage/utilization endpoint which returns:
284
+ * - github: MTD usage and plan details
285
+ */
286
+ export async function fetchGitHubUsage(): Promise<void> {
287
+ try {
288
+ const response = await fetch('/api/usage/utilization', {
289
+ credentials: 'include',
290
+ });
291
+
292
+ if (!response.ok) {
293
+ console.warn('GitHub usage fetch failed:', response.status);
294
+ return;
295
+ }
296
+
297
+ const json = await response.json();
298
+
299
+ if (json.success && json.github) {
300
+ $githubUsage.set(json.github as GitHubUsageData);
301
+ }
302
+ } catch (error) {
303
+ console.error('Failed to fetch GitHub usage:', error);
304
+ // Don't set error state - GitHub data is supplementary
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Fetch all usage data in parallel
310
+ *
311
+ * Consolidates multiple API calls for initial page load:
312
+ * - fetchDailyData(): Daily cost breakdown
313
+ * - fetchProjects(): Projects, allowances, projected cost
314
+ * - fetchGitHubUsage(): GitHub MTD usage
315
+ *
316
+ * @example
317
+ * ```ts
318
+ * // On page load
319
+ * await fetchAll();
320
+ * ```
321
+ */
322
+ export async function fetchAll(): Promise<void> {
323
+ $isLoading.set(true);
324
+ $projectsLoading.set(true);
325
+ $error.set(null);
326
+
327
+ try {
328
+ // Fetch all data in parallel
329
+ await Promise.all([fetchDailyData(), fetchProjects(), fetchGitHubUsage()]);
330
+ } catch (error) {
331
+ console.error('Failed to fetch all usage data:', error);
332
+ $error.set(error instanceof Error ? error.message : 'Failed to fetch usage data');
333
+ }
334
+ // Loading states are managed by individual fetch functions
335
+ }
336
+
337
+ // ============================================================================
338
+ // Subscriptions
339
+ // ============================================================================
340
+
341
+ let unsubscribers: (() => void)[] = [];
342
+
343
+ /**
344
+ * Initialise store subscriptions for auto-fetching and URL sync.
345
+ * Call this once on page load.
346
+ */
347
+ export function initSubscriptions(): void {
348
+ // Prevent double initialisation
349
+ if (unsubscribers.length > 0) {
350
+ return;
351
+ }
352
+
353
+ // Auto-fetch when period changes
354
+ unsubscribers.push(
355
+ $period.subscribe(() => {
356
+ fetchDailyData();
357
+ syncToURL();
358
+ })
359
+ );
360
+
361
+ // Auto-fetch when project changes
362
+ unsubscribers.push(
363
+ $project.subscribe(() => {
364
+ fetchDailyData();
365
+ syncToURL();
366
+ })
367
+ );
368
+
369
+ // Auto-fetch when custom date range changes (only if period is 'custom')
370
+ unsubscribers.push(
371
+ $customDateRange.subscribe(() => {
372
+ if ($period.get() === 'custom') {
373
+ fetchDailyData();
374
+ syncToURL();
375
+ }
376
+ })
377
+ );
378
+
379
+ // Sync selected date to URL
380
+ unsubscribers.push($selectedDate.subscribe(() => syncToURL()));
381
+ }
382
+
383
+ /**
384
+ * Clean up subscriptions.
385
+ * Call this on page unload if needed.
386
+ */
387
+ export function destroySubscriptions(): void {
388
+ unsubscribers.forEach((unsub) => unsub());
389
+ unsubscribers = [];
390
+ }
391
+
392
+ // ============================================================================
393
+ // Actions (for UI components)
394
+ // ============================================================================
395
+
396
+ /**
397
+ * Set the period filter
398
+ */
399
+ export function setPeriod(period: Period): void {
400
+ $period.set(period);
401
+ }
402
+
403
+ /**
404
+ * Set the project filter
405
+ */
406
+ export function setProject(project: string): void {
407
+ $project.set(project);
408
+ }
409
+
410
+ /**
411
+ * Set custom date range
412
+ */
413
+ export function setCustomDateRange(start: string, end: string): void {
414
+ $customDateRange.set({ start, end });
415
+ }
416
+
417
+ /**
418
+ * Select a date (from chart click)
419
+ */
420
+ export function selectDate(date: string | null): void {
421
+ $selectedDate.set(date);
422
+ }
423
+
424
+ /**
425
+ * Clear the selected date
426
+ */
427
+ export function clearSelection(): void {
428
+ $selectedDate.set(null);
429
+ }
430
+
431
+ /**
432
+ * Reset all filters to defaults
433
+ */
434
+ export function resetFilters(): void {
435
+ $period.set('30d');
436
+ $project.set('all');
437
+ $customDateRange.set({ start: '', end: '' });
438
+ $selectedDate.set(null);
439
+ }