@littlebearapps/platform-admin-sdk 2.0.0 → 2.2.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 +4 -7
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +206 -4
- package/package.json +1 -1
- package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
- package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
- package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
- package/templates/full/dashboard/src/components/reports/DigestStats.tsx +151 -0
- package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
- package/templates/full/dashboard/src/components/reports/HealthTrendsReport.tsx +192 -0
- package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
- package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
- package/templates/full/dashboard/src/components/usage/AIModelBreakdown.tsx +364 -0
- package/templates/full/dashboard/src/components/usage/unified/Recommendations.tsx +149 -0
- package/templates/full/dashboard/src/lib/cloudflare/alerting.ts +486 -0
- package/templates/full/dashboard/src/lib/cloudflare/graphql.ts +4785 -0
- package/templates/full/dashboard/src/lib/cloudflare/project-registry.ts +451 -0
- package/templates/full/dashboard/src/lib/notifications/api.ts +197 -0
- package/templates/full/dashboard/src/lib/notifications/types.ts.hbs +97 -0
- package/templates/full/dashboard/src/lib/patterns/api.ts +120 -0
- package/templates/full/dashboard/src/lib/patterns/types.ts +127 -0
- package/templates/full/dashboard/src/lib/reports/types.ts +231 -0
- package/templates/full/dashboard/src/lib/search/api.ts +258 -0
- package/templates/full/dashboard/src/lib/search/types.ts.hbs +115 -0
- package/templates/full/dashboard/src/lib/settings/api.ts.hbs +201 -0
- package/templates/full/dashboard/src/lib/settings/types.ts.hbs +104 -0
- package/templates/full/dashboard/src/lib/usage/allowance-config.ts.hbs +547 -0
- package/templates/full/dashboard/src/lib/usage/providers.ts +331 -0
- package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
- package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
- package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
- package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
- package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
- package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
- package/templates/full/dashboard/src/pages/feedback.astro +365 -0
- package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
- package/templates/full/dashboard/src/pages/map.astro +561 -0
- package/templates/full/dashboard/src/pages/revenue.astro +72 -0
- package/templates/full/dashboard/src/pages/tests.astro +431 -0
- package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
- package/templates/full/scripts/ops/verify-account-total.ts +256 -0
- package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
- package/templates/full/tests/integration/r2-archive.test.ts +108 -0
- package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
- package/templates/shared/.github/workflows/validate-controls.yml +27 -0
- package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
- package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
- package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
- package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
- package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
- package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
- package/templates/shared/dashboard/src/components/Toast.astro +170 -0
- package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
- package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
- package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
- package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
- package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
- package/templates/shared/dashboard/src/components/reports/ReportInfoButton.tsx +98 -0
- package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
- package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
- package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
- package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
- package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
- package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
- package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
- package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
- package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
- package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
- package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
- package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
- package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
- package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
- package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
- package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
- package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
- package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
- package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
- package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
- package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
- package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
- package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
- package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
- package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
- package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
- package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
- package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
- package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
- package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
- package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
- package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
- package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
- package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
- package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
- package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
- package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
- package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
- package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
- package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
- package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
- package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
- package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
- package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
- package/templates/shared/dashboard/src/components/usage/react/DashboardShell.tsx +263 -0
- package/templates/shared/dashboard/src/components/usage/react/StatusBadge.tsx +77 -0
- package/templates/shared/dashboard/src/components/usage/react/UsageChart.tsx +391 -0
- package/templates/shared/dashboard/src/components/usage/react/index.ts.hbs +30 -0
- package/templates/shared/dashboard/src/components/usage/react/types.ts +137 -0
- package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
- package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
- package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
- package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
- package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
- package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
- package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
- package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
- package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
- package/templates/shared/dashboard/src/components/usage/transformers.ts +478 -0
- package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
- package/templates/shared/dashboard/src/components/usage/unified/AlertBanner.tsx +172 -0
- package/templates/shared/dashboard/src/components/usage/unified/HeroCardsRow.tsx +757 -0
- package/templates/shared/dashboard/src/components/usage/unified/LiveHeader.tsx +169 -0
- package/templates/shared/dashboard/src/components/usage/unified/ProjectsTable.tsx +448 -0
- package/templates/shared/dashboard/src/components/usage/unified/ResourceBreakdown.tsx +236 -0
- package/templates/shared/dashboard/src/components/usage/unified/Sparkline.tsx +127 -0
- package/templates/shared/dashboard/src/components/usage/unified/UnifiedShell.tsx +893 -0
- package/templates/shared/dashboard/src/components/usage/unified/index.ts.hbs +50 -0
- package/templates/shared/dashboard/src/components/usage/unified/types.ts +416 -0
- package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
- package/templates/shared/dashboard/src/lib/cloudflare/analytics.ts +310 -0
- package/templates/shared/dashboard/src/lib/cloudflare/d1.ts +55 -0
- package/templates/shared/dashboard/src/lib/cloudflare/index.ts.hbs +120 -0
- package/templates/shared/dashboard/src/lib/infrastructure/types.ts +116 -0
- package/templates/shared/dashboard/src/lib/usage/fetchWithDedup.ts +101 -0
- package/templates/shared/dashboard/src/lib/usage/index.ts.hbs +12 -0
- package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
- package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
- package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
- package/templates/shared/tests/e2e/usage-api.test.ts +909 -0
- package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
- package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
- package/templates/shared/tests/helpers/mock-storage.ts +166 -0
- package/templates/shared/tests/integration/kv-cache.test.ts +252 -0
- package/templates/shared/tests/integration/platform-usage.test.ts +956 -0
- package/templates/shared/tests/unit/billing.test.ts +331 -0
- package/templates/shared/tests/unit/cloudflare/graphql.test.ts +217 -0
- package/templates/shared/tests/unit/components/usage-transformers.test.ts +473 -0
- package/templates/shared/tests/unit/control.test.ts +226 -0
- package/templates/shared/tests/unit/cost-calculator.test.ts +141 -0
- package/templates/shared/tests/unit/economics.test.ts +365 -0
- package/templates/shared/tests/unit/telemetry-sampling.test.ts +401 -0
- package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
- package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
- package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
- package/templates/standard/dashboard/src/components/reports/CircuitBreakerReport.tsx +474 -0
- package/templates/standard/dashboard/src/components/reports/CostTrendsReport.tsx +229 -0
- package/templates/standard/dashboard/src/components/reports/ErrorTrendsReport.tsx +244 -0
- package/templates/standard/dashboard/src/components/reports/ProjectHealthTable.tsx +251 -0
- package/templates/standard/dashboard/src/components/reports/WarningDigestsTable.tsx +298 -0
- package/templates/standard/dashboard/src/components/usage/react/UsageTable.tsx +385 -0
- package/templates/standard/dashboard/src/components/usage/unified/CircuitBreakerEvents.tsx +305 -0
- package/templates/standard/dashboard/src/components/usage/unified/FeatureBudgets.tsx +472 -0
- package/templates/standard/dashboard/src/lib/errors/api.ts +84 -0
- package/templates/standard/dashboard/src/lib/errors/types.ts +75 -0
- package/templates/standard/dashboard/src/lib/infrastructure/api.ts +141 -0
- package/templates/standard/dashboard/src/lib/infrastructure/gatus.ts.hbs +112 -0
- package/templates/standard/dashboard/src/lib/services/proxy/index.ts +20 -0
- package/templates/standard/dashboard/src/lib/services/proxy/proxy.ts +244 -0
- package/templates/standard/dashboard/src/lib/services/proxy/types.ts +81 -0
- package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
- package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
- package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
- package/templates/standard/tests/integration/connectors.test.ts +241 -0
- package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
- package/templates/standard/tests/integration/ingestion.test.ts +211 -0
- package/templates/standard/tests/integration/platform-sentinel.test.ts +497 -0
- package/templates/standard/tests/unit/cloudflare/alerting.test.ts +480 -0
- package/templates/standard/tests/unit/error-collector/dedup.test.ts +350 -0
- package/templates/standard/tests/unit/error-collector/github.test.ts +187 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage Dashboard State Store
|
|
3
|
+
*
|
|
4
|
+
* Centralised nanostores for reactive state management.
|
|
5
|
+
* Part of task-19: Usage Dashboard Refactor Phase 1 - Foundation
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { $period, $dailyData, $totalCost } from './usageStore';
|
|
10
|
+
*
|
|
11
|
+
* // Read current value
|
|
12
|
+
* console.log($period.get()); // '30d'
|
|
13
|
+
*
|
|
14
|
+
* // Subscribe to changes
|
|
15
|
+
* $dailyData.subscribe(data => console.log('Data updated:', data.length));
|
|
16
|
+
*
|
|
17
|
+
* // Set value
|
|
18
|
+
* $period.set('7d');
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { atom, map, computed } from 'nanostores';
|
|
23
|
+
import type { DailyCostBreakdown } from '../../../lib/cloudflare/graphql';
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Period Selection
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Available time periods for filtering
|
|
31
|
+
*/
|
|
32
|
+
export type Period = '24h' | '7d' | '30d' | 'custom';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Current period selection
|
|
36
|
+
* @default '30d'
|
|
37
|
+
*/
|
|
38
|
+
export const $period = atom<Period>('30d');
|
|
39
|
+
|
|
40
|
+
// ============================================================================
|
|
41
|
+
// Project Filter
|
|
42
|
+
// ============================================================================
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Current project filter
|
|
46
|
+
* @default 'all'
|
|
47
|
+
*/
|
|
48
|
+
export const $project = atom<string>('all');
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Custom Date Range
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Custom date range (used when period === 'custom')
|
|
56
|
+
*/
|
|
57
|
+
export const $customDateRange = map<{ start: string; end: string }>({
|
|
58
|
+
start: '',
|
|
59
|
+
end: '',
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// Timezone
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Timezone for date formatting
|
|
68
|
+
* @default 'Australia/Melbourne'
|
|
69
|
+
*/
|
|
70
|
+
export const $timezone = atom<string>('Australia/Melbourne');
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Daily Cost Data
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Daily cost breakdown data from API
|
|
78
|
+
*/
|
|
79
|
+
export const $dailyData = atom<DailyCostBreakdown[]>([]);
|
|
80
|
+
|
|
81
|
+
// ============================================================================
|
|
82
|
+
// Loading State
|
|
83
|
+
// ============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether data is currently being fetched
|
|
87
|
+
*/
|
|
88
|
+
export const $isLoading = atom<boolean>(false);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Whether any day in the current data has legacy/inflated values (rollupVersion=1)
|
|
92
|
+
* Legacy data was aggregated using SUM() on cumulative hourly values, resulting in ~15x inflated costs.
|
|
93
|
+
* Accurate data (rollupVersion=2) uses MAX() aggregation.
|
|
94
|
+
*/
|
|
95
|
+
export const $hasLegacyData = atom<boolean>(false);
|
|
96
|
+
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// Selection State
|
|
99
|
+
// ============================================================================
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Currently selected date (from chart click)
|
|
103
|
+
* @default null
|
|
104
|
+
*/
|
|
105
|
+
export const $selectedDate = atom<string | null>(null);
|
|
106
|
+
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Computed Values
|
|
109
|
+
// ============================================================================
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Total cost across all days
|
|
113
|
+
*/
|
|
114
|
+
export const $totalCost = computed($dailyData, (data) => data.reduce((sum, d) => sum + d.total, 0));
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Date range display string formatted for Melbourne timezone
|
|
118
|
+
*/
|
|
119
|
+
export const $dateRangeDisplay = computed([$dailyData, $timezone], (data, tz) => {
|
|
120
|
+
if (data.length === 0) return '';
|
|
121
|
+
|
|
122
|
+
const first = data[0].date;
|
|
123
|
+
const last = data[data.length - 1].date;
|
|
124
|
+
|
|
125
|
+
const opts: Intl.DateTimeFormatOptions = {
|
|
126
|
+
timeZone: tz,
|
|
127
|
+
day: 'numeric',
|
|
128
|
+
month: 'short',
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const startStr = new Date(first).toLocaleDateString('en-AU', opts);
|
|
132
|
+
const endStr = new Date(last).toLocaleDateString('en-AU', opts);
|
|
133
|
+
|
|
134
|
+
return `${startStr} - ${endStr}`;
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Number of days in current data
|
|
139
|
+
*/
|
|
140
|
+
export const $dayCount = computed($dailyData, (data) => data.length);
|
|
141
|
+
|
|
142
|
+
// ============================================================================
|
|
143
|
+
// GitHub Usage Data
|
|
144
|
+
// ============================================================================
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* GitHub usage data from the utilization endpoint
|
|
148
|
+
*/
|
|
149
|
+
export interface GitHubUsageData {
|
|
150
|
+
mtdUsage: {
|
|
151
|
+
actionsMinutes: number;
|
|
152
|
+
actionsMinutesIncluded: number;
|
|
153
|
+
actionsMinutesUsagePct: number;
|
|
154
|
+
actionsStorageGbHours: number;
|
|
155
|
+
actionsStorageGbIncluded: number;
|
|
156
|
+
ghecUserMonths: number;
|
|
157
|
+
ghasCodeSecuritySeats: number;
|
|
158
|
+
ghasSecretProtectionSeats: number;
|
|
159
|
+
totalCost: number;
|
|
160
|
+
};
|
|
161
|
+
plan: {
|
|
162
|
+
name: string;
|
|
163
|
+
filledSeats: number;
|
|
164
|
+
totalSeats: number;
|
|
165
|
+
};
|
|
166
|
+
lastUpdated: string | null;
|
|
167
|
+
isStale: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* GitHub usage data store (separate from period-filtered Cloudflare data)
|
|
172
|
+
* This data is fetched once and doesn't change with period selection.
|
|
173
|
+
*/
|
|
174
|
+
export const $githubUsage = atom<GitHubUsageData | null>(null);
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Average daily cost
|
|
178
|
+
*/
|
|
179
|
+
export const $averageDailyCost = computed($dailyData, (data) => {
|
|
180
|
+
if (data.length === 0) return 0;
|
|
181
|
+
const total = data.reduce((sum, d) => sum + d.total, 0);
|
|
182
|
+
return total / data.length;
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Cost breakdown by resource type (totals)
|
|
187
|
+
*/
|
|
188
|
+
export const $resourceTotals = computed($dailyData, (data) => {
|
|
189
|
+
const totals = {
|
|
190
|
+
workers: 0,
|
|
191
|
+
d1: 0,
|
|
192
|
+
kv: 0,
|
|
193
|
+
r2: 0,
|
|
194
|
+
vectorize: 0,
|
|
195
|
+
aiGateway: 0,
|
|
196
|
+
durableObjects: 0,
|
|
197
|
+
workersAI: 0,
|
|
198
|
+
queues: 0,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
for (const day of data) {
|
|
202
|
+
totals.workers += day.workers;
|
|
203
|
+
totals.d1 += day.d1;
|
|
204
|
+
totals.kv += day.kv;
|
|
205
|
+
totals.r2 += day.r2;
|
|
206
|
+
totals.vectorize += day.vectorize;
|
|
207
|
+
totals.aiGateway += day.aiGateway;
|
|
208
|
+
totals.durableObjects += day.durableObjects;
|
|
209
|
+
totals.workersAI += day.workersAI;
|
|
210
|
+
totals.queues += day.queues;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return totals;
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Top spender - service type with highest cost in the period
|
|
218
|
+
*/
|
|
219
|
+
export interface TopSpenderData {
|
|
220
|
+
/** Service type name */
|
|
221
|
+
service: string;
|
|
222
|
+
/** Display label */
|
|
223
|
+
label: string;
|
|
224
|
+
/** Total cost for this service */
|
|
225
|
+
cost: number;
|
|
226
|
+
/** Percentage of total cost */
|
|
227
|
+
percentageOfTotal: number;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const SERVICE_LABELS: Record<string, string> = {
|
|
231
|
+
workers: 'Workers',
|
|
232
|
+
d1: 'D1 Database',
|
|
233
|
+
kv: 'KV Storage',
|
|
234
|
+
r2: 'R2 Storage',
|
|
235
|
+
vectorize: 'Vectorize',
|
|
236
|
+
aiGateway: 'AI Gateway',
|
|
237
|
+
durableObjects: 'Durable Objects',
|
|
238
|
+
workersAI: 'Workers AI',
|
|
239
|
+
queues: 'Queues',
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const $topSpender = computed($resourceTotals, (totals): TopSpenderData | null => {
|
|
243
|
+
const entries = Object.entries(totals).filter(([_, cost]) => cost > 0);
|
|
244
|
+
if (entries.length === 0) return null;
|
|
245
|
+
|
|
246
|
+
const totalCost = entries.reduce((sum, [_, cost]) => sum + cost, 0);
|
|
247
|
+
const [topService, topCost] = entries.reduce(
|
|
248
|
+
(max, entry) => (entry[1] > max[1] ? entry : max),
|
|
249
|
+
entries[0]
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
service: topService,
|
|
254
|
+
label: SERVICE_LABELS[topService] || topService,
|
|
255
|
+
cost: topCost,
|
|
256
|
+
percentageOfTotal: totalCost > 0 ? (topCost / totalCost) * 100 : 0,
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// Projects & Allowances (from /api/usage/projects)
|
|
262
|
+
// ============================================================================
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Service allowance definition
|
|
266
|
+
*/
|
|
267
|
+
export interface AllowanceInfo {
|
|
268
|
+
limit: number;
|
|
269
|
+
unit: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Allowances object from API
|
|
274
|
+
*/
|
|
275
|
+
export interface AllowancesData {
|
|
276
|
+
workers: AllowanceInfo;
|
|
277
|
+
d1_writes: AllowanceInfo;
|
|
278
|
+
kv_writes: AllowanceInfo;
|
|
279
|
+
r2_storage: AllowanceInfo;
|
|
280
|
+
durableObjects: AllowanceInfo;
|
|
281
|
+
vectorize: AllowanceInfo;
|
|
282
|
+
github_actions_minutes: AllowanceInfo;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Projected cost calculation
|
|
287
|
+
*/
|
|
288
|
+
export interface ProjectedCost {
|
|
289
|
+
/** Current month-to-date cost in USD */
|
|
290
|
+
currentCost: number;
|
|
291
|
+
/** Number of days elapsed this month */
|
|
292
|
+
daysPassed: number;
|
|
293
|
+
/** Total days in the current month */
|
|
294
|
+
daysInMonth: number;
|
|
295
|
+
/** Projected end-of-month cost based on current burn rate */
|
|
296
|
+
projectedMonthlyCost: number;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Project from D1 registry with resource count
|
|
301
|
+
*/
|
|
302
|
+
export interface ProjectData {
|
|
303
|
+
projectId: string;
|
|
304
|
+
displayName: string;
|
|
305
|
+
description: string | null;
|
|
306
|
+
color: string | null;
|
|
307
|
+
icon: string | null;
|
|
308
|
+
owner: string | null;
|
|
309
|
+
repoPath: string | null;
|
|
310
|
+
status: 'active' | 'archived' | 'development';
|
|
311
|
+
primaryResource: string | null;
|
|
312
|
+
customLimit: number | null;
|
|
313
|
+
/** Full GitHub repository URL */
|
|
314
|
+
repoUrl: string | null;
|
|
315
|
+
/** GitHub repository identifier */
|
|
316
|
+
githubRepoId: string | null;
|
|
317
|
+
/** Number of CF resources mapped to this project */
|
|
318
|
+
resourceCount: number;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Projects list store (from /api/usage/projects)
|
|
323
|
+
*/
|
|
324
|
+
export const $projects = atom<ProjectData[]>([]);
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Service allowances store
|
|
328
|
+
*/
|
|
329
|
+
export const $allowances = atom<AllowancesData | null>(null);
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Projected cost store
|
|
333
|
+
*/
|
|
334
|
+
export const $projectedCost = atom<ProjectedCost | null>(null);
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Projects loading state
|
|
338
|
+
*/
|
|
339
|
+
export const $projectsLoading = atom<boolean>(false);
|
|
340
|
+
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Filter State (for resource table)
|
|
343
|
+
// ============================================================================
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Resource table filter state (search, type filter, toggles)
|
|
347
|
+
*/
|
|
348
|
+
export interface ResourceFilterState {
|
|
349
|
+
/** Search query string */
|
|
350
|
+
search: string;
|
|
351
|
+
/** Resource type filter ('all' or specific type) */
|
|
352
|
+
typeFilter: string;
|
|
353
|
+
/** Show only resources with cost changes */
|
|
354
|
+
showOnlyChanged: boolean;
|
|
355
|
+
/** Show only resources with non-zero cost */
|
|
356
|
+
showNonZeroCost: boolean;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Resource filter state store
|
|
361
|
+
*/
|
|
362
|
+
export const $resourceFilters = map<ResourceFilterState>({
|
|
363
|
+
search: '',
|
|
364
|
+
typeFilter: 'all',
|
|
365
|
+
showOnlyChanged: false,
|
|
366
|
+
showNonZeroCost: false,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// ============================================================================
|
|
370
|
+
// Error State
|
|
371
|
+
// ============================================================================
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Error message store (null when no error)
|
|
375
|
+
*/
|
|
376
|
+
export const $error = atom<string | null>(null);
|