@littlebearapps/platform-admin-sdk 2.1.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 +2 -5
- package/dist/templates.d.ts +1 -1
- package/dist/templates.js +121 -3
- 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/FeatureUsageReport.tsx +339 -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/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/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/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/types.ts +283 -0
- package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -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-export.test.ts +784 -0
- package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -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/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/README.md
CHANGED
|
@@ -276,13 +276,10 @@ See [Upgrade Guide](../../docs/admin-sdk/upgrade-guide.md) for detailed instruct
|
|
|
276
276
|
|
|
277
277
|
## What's Not Included
|
|
278
278
|
|
|
279
|
-
The scaffolder generates core infrastructure
|
|
279
|
+
The scaffolder generates core infrastructure, an Astro SSR dashboard (273 templates), test suites, and CI workflows. It does **not** create:
|
|
280
280
|
|
|
281
|
-
-
|
|
282
|
-
- Email workers or notification templates
|
|
281
|
+
- Email workers or notification email templates
|
|
283
282
|
- Data connectors (Stripe, GA4, Plausible, etc.)
|
|
284
|
-
- Test suites
|
|
285
|
-
- CI/CD workflows (use the [consumer-check.yml](../../docs/admin-sdk/ci-workflow.md) reusable workflow)
|
|
286
283
|
|
|
287
284
|
These are project-specific — build them as you need them.
|
|
288
285
|
|
package/dist/templates.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Tier } from './prompts.js';
|
|
8
8
|
/** Single source of truth for the SDK version. */
|
|
9
|
-
export declare const SDK_VERSION = "2.
|
|
9
|
+
export declare const SDK_VERSION = "2.2.0";
|
|
10
10
|
/** Returns true if `to` is the same or higher tier than `from`. */
|
|
11
11
|
export declare function isTierUpgradeOrSame(from: Tier, to: Tier): boolean;
|
|
12
12
|
/** Check if a template file is a numbered migration (not seed.sql). */
|
package/dist/templates.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* All other files are copied verbatim.
|
|
6
6
|
*/
|
|
7
7
|
/** Single source of truth for the SDK version. */
|
|
8
|
-
export const SDK_VERSION = '2.
|
|
8
|
+
export const SDK_VERSION = '2.2.0';
|
|
9
9
|
/** Tier ordering for upgrade validation. */
|
|
10
10
|
const TIER_ORDER = { minimal: 0, standard: 1, full: 2 };
|
|
11
11
|
/** Returns true if `to` is the same or higher tier than `from`. */
|
|
@@ -28,6 +28,10 @@ const SHARED_FILES = [
|
|
|
28
28
|
{ src: 'shared/scripts/ops/reset-budget-state.ts', dest: 'scripts/ops/reset-budget-state.ts', template: false },
|
|
29
29
|
{ src: 'shared/scripts/ops/verify-account-completeness.ts', dest: 'scripts/ops/verify-account-completeness.ts', template: false },
|
|
30
30
|
{ src: 'shared/scripts/ops/validate-pipeline.ts', dest: 'scripts/ops/validate-pipeline.ts', template: false },
|
|
31
|
+
{ src: 'shared/scripts/test-telemetry-flow.ts', dest: 'scripts/test-telemetry-flow.ts', template: false },
|
|
32
|
+
// CI/CD Workflows (v2.2.0)
|
|
33
|
+
{ src: 'shared/.github/workflows/validate-controls.yml', dest: '.github/workflows/validate-controls.yml', template: false },
|
|
34
|
+
{ src: 'shared/.github/workflows/dependabot-automerge.yml', dest: '.github/workflows/dependabot-automerge.yml', template: false },
|
|
31
35
|
// Contracts — JSON schemas + TypeScript types
|
|
32
36
|
{ src: 'shared/contracts/schemas/envelope.v1.schema.json', dest: 'contracts/schemas/envelope.v1.schema.json', template: false },
|
|
33
37
|
{ src: 'shared/contracts/schemas/error_report.v1.schema.json', dest: 'contracts/schemas/error_report.v1.schema.json', template: false },
|
|
@@ -143,10 +147,82 @@ const SHARED_FILES = [
|
|
|
143
147
|
{ src: 'shared/dashboard/src/components/resources/CostCentreOverview.tsx', dest: 'dashboard/src/components/resources/CostCentreOverview.tsx', template: false },
|
|
144
148
|
{ src: 'shared/dashboard/src/components/resources/AllowanceStatus.tsx', dest: 'dashboard/src/components/resources/AllowanceStatus.tsx', template: false },
|
|
145
149
|
{ src: 'shared/dashboard/src/components/resources/index.ts', dest: 'dashboard/src/components/resources/index.ts', template: false },
|
|
150
|
+
{ src: 'shared/dashboard/src/components/resources/CostChart.tsx', dest: 'dashboard/src/components/resources/CostChart.tsx', template: false },
|
|
151
|
+
{ src: 'shared/dashboard/src/components/resources/ProviderCard.tsx', dest: 'dashboard/src/components/resources/ProviderCard.tsx', template: false },
|
|
152
|
+
{ src: 'shared/dashboard/src/components/resources/ProviderDetail.tsx', dest: 'dashboard/src/components/resources/ProviderDetail.tsx', template: false },
|
|
146
153
|
{ src: 'shared/dashboard/src/components/settings/SettingsCard.tsx', dest: 'dashboard/src/components/settings/SettingsCard.tsx', template: false },
|
|
154
|
+
{ src: 'shared/dashboard/src/components/settings/SettingsCard.astro', dest: 'dashboard/src/components/settings/SettingsCard.astro', template: false },
|
|
147
155
|
{ src: 'shared/dashboard/src/components/settings/index.ts', dest: 'dashboard/src/components/settings/index.ts', template: false },
|
|
156
|
+
{ src: 'shared/dashboard/src/components/overview/AlertBanner.tsx', dest: 'dashboard/src/components/overview/AlertBanner.tsx', template: false },
|
|
157
|
+
{ src: 'shared/dashboard/src/components/overview/index.ts', dest: 'dashboard/src/components/overview/index.ts', template: false },
|
|
158
|
+
{ src: 'shared/dashboard/src/components/costs/index.ts', dest: 'dashboard/src/components/costs/index.ts', template: false },
|
|
159
|
+
{ src: 'shared/dashboard/src/components/costs/ProviderCostsGrid.tsx', dest: 'dashboard/src/components/costs/ProviderCostsGrid.tsx', template: false },
|
|
148
160
|
{ src: 'shared/dashboard/src/lib/types.ts', dest: 'dashboard/src/lib/types.ts', template: false },
|
|
149
161
|
{ src: 'shared/dashboard/src/lib/fetch.ts', dest: 'dashboard/src/lib/fetch.ts', template: false },
|
|
162
|
+
// Dashboard — shared tier: Astro shell components (v2.2.0)
|
|
163
|
+
{ src: 'shared/dashboard/src/components/Breadcrumbs.astro', dest: 'dashboard/src/components/Breadcrumbs.astro', template: false },
|
|
164
|
+
{ src: 'shared/dashboard/src/components/EmptyState.astro', dest: 'dashboard/src/components/EmptyState.astro', template: false },
|
|
165
|
+
{ src: 'shared/dashboard/src/components/ErrorBoundary.astro', dest: 'dashboard/src/components/ErrorBoundary.astro', template: false },
|
|
166
|
+
{ src: 'shared/dashboard/src/components/LoadingSkeleton.astro', dest: 'dashboard/src/components/LoadingSkeleton.astro', template: false },
|
|
167
|
+
{ src: 'shared/dashboard/src/components/PageShell.astro', dest: 'dashboard/src/components/PageShell.astro', template: false },
|
|
168
|
+
{ src: 'shared/dashboard/src/components/SkipLinks.astro', dest: 'dashboard/src/components/SkipLinks.astro', template: false },
|
|
169
|
+
{ src: 'shared/dashboard/src/components/Toast.astro', dest: 'dashboard/src/components/Toast.astro', template: false },
|
|
170
|
+
{ src: 'shared/dashboard/src/components/ToastContainer.astro', dest: 'dashboard/src/components/ToastContainer.astro', template: false },
|
|
171
|
+
// Dashboard — shared tier: usage Astro components (v2.2.0)
|
|
172
|
+
{ src: 'shared/dashboard/src/components/usage/types.ts', dest: 'dashboard/src/components/usage/types.ts', template: false },
|
|
173
|
+
{ src: 'shared/dashboard/src/components/usage/design-tokens.ts', dest: 'dashboard/src/components/usage/design-tokens.ts', template: false },
|
|
174
|
+
{ src: 'shared/dashboard/src/components/usage/usage-colors.ts', dest: 'dashboard/src/components/usage/usage-colors.ts', template: false },
|
|
175
|
+
{ src: 'shared/dashboard/src/components/usage/AllowanceGauge.astro', dest: 'dashboard/src/components/usage/AllowanceGauge.astro', template: false },
|
|
176
|
+
{ src: 'shared/dashboard/src/components/usage/AnomalyAlerts.astro', dest: 'dashboard/src/components/usage/AnomalyAlerts.astro', template: false },
|
|
177
|
+
{ src: 'shared/dashboard/src/components/usage/BillingCycleCountdown.astro', dest: 'dashboard/src/components/usage/BillingCycleCountdown.astro', template: false },
|
|
178
|
+
{ src: 'shared/dashboard/src/components/usage/BurnRateHero.astro', dest: 'dashboard/src/components/usage/BurnRateHero.astro', template: false },
|
|
179
|
+
{ src: 'shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro', dest: 'dashboard/src/components/usage/CircuitBreakerEventLog.astro', template: false },
|
|
180
|
+
{ src: 'shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx', dest: 'dashboard/src/components/usage/CircuitBreakerPanel.tsx', template: false },
|
|
181
|
+
{ src: 'shared/dashboard/src/components/usage/CircuitBreakerStatus.astro', dest: 'dashboard/src/components/usage/CircuitBreakerStatus.astro', template: false },
|
|
182
|
+
{ src: 'shared/dashboard/src/components/usage/CompactThresholdBanner.astro', dest: 'dashboard/src/components/usage/CompactThresholdBanner.astro', template: false },
|
|
183
|
+
{ src: 'shared/dashboard/src/components/usage/ComparisonModeSelector.astro', dest: 'dashboard/src/components/usage/ComparisonModeSelector.astro', template: false },
|
|
184
|
+
{ src: 'shared/dashboard/src/components/usage/CostBreakdownChart.astro', dest: 'dashboard/src/components/usage/CostBreakdownChart.astro', template: false },
|
|
185
|
+
{ src: 'shared/dashboard/src/components/usage/CostBreakdownTable.astro', dest: 'dashboard/src/components/usage/CostBreakdownTable.astro', template: false },
|
|
186
|
+
{ src: 'shared/dashboard/src/components/usage/CostDataTable.astro', dest: 'dashboard/src/components/usage/CostDataTable.astro', template: false },
|
|
187
|
+
{ src: 'shared/dashboard/src/components/usage/CostDonutChart.astro', dest: 'dashboard/src/components/usage/CostDonutChart.astro', template: false },
|
|
188
|
+
{ src: 'shared/dashboard/src/components/usage/DailyCostChart.astro', dest: 'dashboard/src/components/usage/DailyCostChart.astro', template: false },
|
|
189
|
+
{ src: 'shared/dashboard/src/components/usage/ExportButton.astro', dest: 'dashboard/src/components/usage/ExportButton.astro', template: false },
|
|
190
|
+
{ src: 'shared/dashboard/src/components/usage/FeatureBudgetsTable.astro', dest: 'dashboard/src/components/usage/FeatureBudgetsTable.astro', template: false },
|
|
191
|
+
{ src: 'shared/dashboard/src/components/usage/FilterBar.astro', dest: 'dashboard/src/components/usage/FilterBar.astro', template: false },
|
|
192
|
+
{ src: 'shared/dashboard/src/components/usage/FilterToggles.astro', dest: 'dashboard/src/components/usage/FilterToggles.astro', template: false },
|
|
193
|
+
{ src: 'shared/dashboard/src/components/usage/GitHubUsageCard.astro', dest: 'dashboard/src/components/usage/GitHubUsageCard.astro', template: false },
|
|
194
|
+
{ src: 'shared/dashboard/src/components/usage/OverageCostCard.astro', dest: 'dashboard/src/components/usage/OverageCostCard.astro', template: false },
|
|
195
|
+
{ src: 'shared/dashboard/src/components/usage/PlanUtilizationCard.astro', dest: 'dashboard/src/components/usage/PlanUtilizationCard.astro', template: false },
|
|
196
|
+
{ src: 'shared/dashboard/src/components/usage/ProjectCard.astro', dest: 'dashboard/src/components/usage/ProjectCard.astro', template: false },
|
|
197
|
+
{ src: 'shared/dashboard/src/components/usage/ProjectCardsGrid.astro', dest: 'dashboard/src/components/usage/ProjectCardsGrid.astro', template: false },
|
|
198
|
+
{ src: 'shared/dashboard/src/components/usage/ResourceSearch.astro', dest: 'dashboard/src/components/usage/ResourceSearch.astro', template: false },
|
|
199
|
+
{ src: 'shared/dashboard/src/components/usage/ServiceUtilizationList.astro', dest: 'dashboard/src/components/usage/ServiceUtilizationList.astro', template: false },
|
|
200
|
+
{ src: 'shared/dashboard/src/components/usage/SparklineCard.astro', dest: 'dashboard/src/components/usage/SparklineCard.astro', template: false },
|
|
201
|
+
{ src: 'shared/dashboard/src/components/usage/StatsHero.astro', dest: 'dashboard/src/components/usage/StatsHero.astro', template: false },
|
|
202
|
+
{ src: 'shared/dashboard/src/components/usage/TableFilters.astro', dest: 'dashboard/src/components/usage/TableFilters.astro', template: false },
|
|
203
|
+
{ src: 'shared/dashboard/src/components/usage/ThresholdAlert.astro', dest: 'dashboard/src/components/usage/ThresholdAlert.astro', template: false },
|
|
204
|
+
{ src: 'shared/dashboard/src/components/usage/ThresholdSettings.astro', dest: 'dashboard/src/components/usage/ThresholdSettings.astro', template: false },
|
|
205
|
+
{ src: 'shared/dashboard/src/components/usage/TopSpenderCard.astro', dest: 'dashboard/src/components/usage/TopSpenderCard.astro', template: false },
|
|
206
|
+
{ src: 'shared/dashboard/src/components/usage/UnifiedResourceTable.astro', dest: 'dashboard/src/components/usage/UnifiedResourceTable.astro', template: false },
|
|
207
|
+
{ src: 'shared/dashboard/src/components/usage/UsageCard.astro', dest: 'dashboard/src/components/usage/UsageCard.astro', template: false },
|
|
208
|
+
{ src: 'shared/dashboard/src/components/usage/UsageHealthBanner.astro', dest: 'dashboard/src/components/usage/UsageHealthBanner.astro', template: false },
|
|
209
|
+
{ src: 'shared/dashboard/src/components/usage/UtilizationBar.astro', dest: 'dashboard/src/components/usage/UtilizationBar.astro', template: false },
|
|
210
|
+
{ src: 'shared/dashboard/src/components/usage/WorkersBreakdownTable.astro', dest: 'dashboard/src/components/usage/WorkersBreakdownTable.astro', template: false },
|
|
211
|
+
{ src: 'shared/dashboard/src/components/usage/daily/CostChart.astro', dest: 'dashboard/src/components/usage/daily/CostChart.astro', template: false },
|
|
212
|
+
{ src: 'shared/dashboard/src/components/usage/daily/CostTable.astro', dest: 'dashboard/src/components/usage/daily/CostTable.astro', template: false },
|
|
213
|
+
{ src: 'shared/dashboard/src/components/usage/daily/DailyOverview.astro', dest: 'dashboard/src/components/usage/daily/DailyOverview.astro', template: false },
|
|
214
|
+
{ src: 'shared/dashboard/src/components/usage/filters/InlineDateRange.astro', dest: 'dashboard/src/components/usage/filters/InlineDateRange.astro', template: false },
|
|
215
|
+
{ src: 'shared/dashboard/src/components/usage/filters/PeriodButtons.astro', dest: 'dashboard/src/components/usage/filters/PeriodButtons.astro', template: false },
|
|
216
|
+
{ src: 'shared/dashboard/src/components/usage/filters/ProjectSelect.astro', dest: 'dashboard/src/components/usage/filters/ProjectSelect.astro', template: false },
|
|
217
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts', dest: 'dashboard/src/components/usage/scripts/ai-tab-controller.ts', template: false },
|
|
218
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/constants.ts', dest: 'dashboard/src/components/usage/scripts/constants.ts', template: false },
|
|
219
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/formatters.ts', dest: 'dashboard/src/components/usage/scripts/formatters.ts', template: false },
|
|
220
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/overview-controller.ts', dest: 'dashboard/src/components/usage/scripts/overview-controller.ts', template: false },
|
|
221
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/resource-table-builder.ts', dest: 'dashboard/src/components/usage/scripts/resource-table-builder.ts', template: false },
|
|
222
|
+
{ src: 'shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts', dest: 'dashboard/src/components/usage/scripts/tabs-filters-controller.ts', template: false },
|
|
223
|
+
{ src: 'shared/dashboard/src/components/usage/state/index.ts', dest: 'dashboard/src/components/usage/state/index.ts', template: false },
|
|
224
|
+
{ src: 'shared/dashboard/src/components/usage/state/usageActions.ts', dest: 'dashboard/src/components/usage/state/usageActions.ts', template: false },
|
|
225
|
+
{ src: 'shared/dashboard/src/components/usage/state/usageStore.ts', dest: 'dashboard/src/components/usage/state/usageStore.ts', template: false },
|
|
150
226
|
// Dashboard — shared tier: UI primitives (v1.6.0)
|
|
151
227
|
{ src: 'shared/dashboard/src/components/ui/EmptyState.tsx', dest: 'dashboard/src/components/ui/EmptyState.tsx', template: false },
|
|
152
228
|
{ src: 'shared/dashboard/src/components/ui/LoadingSkeleton.tsx', dest: 'dashboard/src/components/ui/LoadingSkeleton.tsx', template: false },
|
|
@@ -159,6 +235,8 @@ const SHARED_FILES = [
|
|
|
159
235
|
{ src: 'shared/dashboard/src/pages/api/usage/projects.ts', dest: 'dashboard/src/pages/api/usage/projects.ts', template: false },
|
|
160
236
|
{ src: 'shared/dashboard/src/pages/api/usage/allowances.ts', dest: 'dashboard/src/pages/api/usage/allowances.ts', template: false },
|
|
161
237
|
{ src: 'shared/dashboard/src/pages/api/usage/anomalies.ts', dest: 'dashboard/src/pages/api/usage/anomalies.ts', template: false },
|
|
238
|
+
{ src: 'shared/dashboard/src/pages/api/usage/ai-models.ts', dest: 'dashboard/src/pages/api/usage/ai-models.ts', template: false },
|
|
239
|
+
{ src: 'shared/dashboard/src/pages/api/usage/billing-context.ts', dest: 'dashboard/src/pages/api/usage/billing-context.ts', template: false },
|
|
162
240
|
// Dashboard — shared tier: costs API routes (v1.6.0)
|
|
163
241
|
{ src: 'shared/dashboard/src/pages/api/costs/overview.ts', dest: 'dashboard/src/pages/api/costs/overview.ts', template: false },
|
|
164
242
|
{ src: 'shared/dashboard/src/pages/api/costs/providers.ts', dest: 'dashboard/src/pages/api/costs/providers.ts', template: false },
|
|
@@ -247,6 +325,8 @@ const SHARED_FILES = [
|
|
|
247
325
|
{ src: 'shared/tests/integration/platform-usage.test.ts', dest: 'tests/integration/platform-usage.test.ts', template: false },
|
|
248
326
|
{ src: 'shared/tests/integration/kv-cache.test.ts', dest: 'tests/integration/kv-cache.test.ts', template: false },
|
|
249
327
|
{ src: 'shared/tests/e2e/usage-api.test.ts', dest: 'tests/e2e/usage-api.test.ts', template: false },
|
|
328
|
+
{ src: 'shared/tests/e2e/usage-export.test.ts', dest: 'tests/e2e/usage-export.test.ts', template: false },
|
|
329
|
+
{ src: 'shared/tests/e2e/usage-mobile.test.ts', dest: 'tests/e2e/usage-mobile.test.ts', template: false },
|
|
250
330
|
// CI — contract check + security workflows (v2.0.0)
|
|
251
331
|
{ src: 'shared/.github/workflows/contract-check.yml.hbs', dest: '.github/workflows/contract-check.yml', template: true },
|
|
252
332
|
{ src: 'shared/.github/workflows/security.yml', dest: '.github/workflows/security.yml', template: false },
|
|
@@ -284,9 +364,10 @@ const STANDARD_FILES = [
|
|
|
284
364
|
// SDK test client (telemetry + circuit breaker validation)
|
|
285
365
|
{ src: 'standard/wrangler.sdk-test-client.jsonc.hbs', dest: 'wrangler.{{projectSlug}}-sdk-test-client.jsonc', template: true },
|
|
286
366
|
{ src: 'standard/workers/platform-sdk-test-client.ts', dest: 'workers/platform-sdk-test-client.ts', template: false },
|
|
287
|
-
// Dashboard — standard tier (error management, health, DLQ)
|
|
367
|
+
// Dashboard — standard tier (error management, health, DLQ, analytics)
|
|
288
368
|
{ src: 'standard/dashboard/src/pages/health.astro', dest: 'dashboard/src/pages/health.astro', template: false },
|
|
289
369
|
{ src: 'standard/dashboard/src/pages/errors.astro', dest: 'dashboard/src/pages/errors.astro', template: false },
|
|
370
|
+
{ src: 'standard/dashboard/src/pages/analytics.astro', dest: 'dashboard/src/pages/analytics.astro', template: false },
|
|
290
371
|
{ src: 'standard/dashboard/src/pages/api/errors/index.ts', dest: 'dashboard/src/pages/api/errors/index.ts', template: false },
|
|
291
372
|
{ src: 'standard/dashboard/src/pages/api/errors/stats.ts', dest: 'dashboard/src/pages/api/errors/stats.ts', template: false },
|
|
292
373
|
{ src: 'standard/dashboard/src/pages/api/health/dlq.ts', dest: 'dashboard/src/pages/api/health/dlq.ts', template: false },
|
|
@@ -318,10 +399,21 @@ const STANDARD_FILES = [
|
|
|
318
399
|
{ src: 'standard/tests/unit/error-collector/capture.test.ts', dest: 'tests/unit/error-collector/capture.test.ts', template: false },
|
|
319
400
|
// Dashboard — standard tier: audit history API route (v2.0.0)
|
|
320
401
|
{ src: 'standard/dashboard/src/pages/api/health/audit-history.ts', dest: 'dashboard/src/pages/api/health/audit-history.ts', template: false },
|
|
402
|
+
// Dashboard — standard tier: infrastructure API routes (v2.2.0)
|
|
403
|
+
{ src: 'standard/dashboard/src/pages/api/infrastructure/alerts.ts', dest: 'dashboard/src/pages/api/infrastructure/alerts.ts', template: false },
|
|
404
|
+
{ src: 'standard/dashboard/src/pages/api/infrastructure/healthchecks.ts', dest: 'dashboard/src/pages/api/infrastructure/healthchecks.ts', template: false },
|
|
405
|
+
{ src: 'standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts', dest: 'dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts', template: false },
|
|
406
|
+
{ src: 'standard/dashboard/src/pages/api/infrastructure/uptime.ts', dest: 'dashboard/src/pages/api/infrastructure/uptime.ts', template: false },
|
|
407
|
+
{ src: 'standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts', dest: 'dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts', template: false },
|
|
408
|
+
{ src: 'standard/dashboard/src/pages/api/test/service-auth.ts', dest: 'dashboard/src/pages/api/test/service-auth.ts', template: false },
|
|
321
409
|
// Dashboard — standard tier: usage components (v2.1.0)
|
|
322
410
|
{ src: 'standard/dashboard/src/components/usage/react/UsageTable.tsx', dest: 'dashboard/src/components/usage/react/UsageTable.tsx', template: false },
|
|
323
411
|
{ src: 'standard/dashboard/src/components/usage/unified/FeatureBudgets.tsx', dest: 'dashboard/src/components/usage/unified/FeatureBudgets.tsx', template: false },
|
|
324
412
|
{ src: 'standard/dashboard/src/components/usage/unified/CircuitBreakerEvents.tsx', dest: 'dashboard/src/components/usage/unified/CircuitBreakerEvents.tsx', template: false },
|
|
413
|
+
// Dashboard — standard tier: infrastructure components (v2.2.0)
|
|
414
|
+
{ src: 'standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx', dest: 'dashboard/src/components/infrastructure/HealthchecksStatus.tsx', template: false },
|
|
415
|
+
{ src: 'standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx', dest: 'dashboard/src/components/infrastructure/InfrastructureTabs.tsx', template: false },
|
|
416
|
+
{ src: 'standard/dashboard/src/components/errors/PriorityBadge.astro', dest: 'dashboard/src/components/errors/PriorityBadge.astro', template: false },
|
|
325
417
|
// Dashboard — standard tier: reports components (v2.1.0)
|
|
326
418
|
{ src: 'standard/dashboard/src/components/reports/CircuitBreakerReport.tsx', dest: 'dashboard/src/components/reports/CircuitBreakerReport.tsx', template: false },
|
|
327
419
|
{ src: 'standard/dashboard/src/components/reports/ErrorTrendsReport.tsx', dest: 'dashboard/src/components/reports/ErrorTrendsReport.tsx', template: false },
|
|
@@ -333,6 +425,9 @@ const STANDARD_FILES = [
|
|
|
333
425
|
{ src: 'standard/tests/unit/error-collector/dedup.test.ts', dest: 'tests/unit/error-collector/dedup.test.ts', template: false },
|
|
334
426
|
{ src: 'standard/tests/unit/error-collector/github.test.ts', dest: 'tests/unit/error-collector/github.test.ts', template: false },
|
|
335
427
|
{ src: 'standard/tests/integration/platform-sentinel.test.ts', dest: 'tests/integration/platform-sentinel.test.ts', template: false },
|
|
428
|
+
{ src: 'standard/tests/integration/connectors.test.ts', dest: 'tests/integration/connectors.test.ts', template: false },
|
|
429
|
+
{ src: 'standard/tests/integration/github-monitor.test.ts', dest: 'tests/integration/github-monitor.test.ts', template: false },
|
|
430
|
+
{ src: 'standard/tests/integration/ingestion.test.ts', dest: 'tests/integration/ingestion.test.ts', template: false },
|
|
336
431
|
];
|
|
337
432
|
const FULL_FILES = [
|
|
338
433
|
// Additional migrations
|
|
@@ -382,8 +477,15 @@ const FULL_FILES = [
|
|
|
382
477
|
{ src: 'full/scripts/ops/set-kv-pricing.ts', dest: 'scripts/ops/set-kv-pricing.ts', template: false },
|
|
383
478
|
// Universal backfill cascade (v1.8.0)
|
|
384
479
|
{ src: 'full/scripts/ops/universal-backfill.ts', dest: 'scripts/ops/universal-backfill.ts', template: false },
|
|
385
|
-
|
|
480
|
+
{ src: 'full/scripts/ops/verify-account-total.ts', dest: 'scripts/ops/verify-account-total.ts', template: false },
|
|
481
|
+
{ src: 'full/scripts/ops/audit-cost-anomaly.ts', dest: 'scripts/ops/audit-cost-anomaly.ts', template: false },
|
|
482
|
+
// Dashboard — full tier (patterns, notifications, search, additional pages)
|
|
386
483
|
{ src: 'full/dashboard/src/pages/notifications.astro', dest: 'dashboard/src/pages/notifications.astro', template: false },
|
|
484
|
+
{ src: 'full/dashboard/src/pages/map.astro', dest: 'dashboard/src/pages/map.astro', template: false },
|
|
485
|
+
{ src: 'full/dashboard/src/pages/revenue.astro', dest: 'dashboard/src/pages/revenue.astro', template: false },
|
|
486
|
+
{ src: 'full/dashboard/src/pages/tests.astro', dest: 'dashboard/src/pages/tests.astro', template: false },
|
|
487
|
+
{ src: 'full/dashboard/src/pages/kiosk.astro', dest: 'dashboard/src/pages/kiosk.astro', template: false },
|
|
488
|
+
{ src: 'full/dashboard/src/pages/feedback.astro', dest: 'dashboard/src/pages/feedback.astro', template: false },
|
|
387
489
|
{ src: 'full/dashboard/src/pages/api/patterns/index.ts', dest: 'dashboard/src/pages/api/patterns/index.ts', template: false },
|
|
388
490
|
{ src: 'full/dashboard/src/pages/api/patterns/approve.ts', dest: 'dashboard/src/pages/api/patterns/approve.ts', template: false },
|
|
389
491
|
{ src: 'full/dashboard/src/pages/api/patterns/reject.ts', dest: 'dashboard/src/pages/api/patterns/reject.ts', template: false },
|
|
@@ -395,8 +497,12 @@ const FULL_FILES = [
|
|
|
395
497
|
{ src: 'full/dashboard/src/components/patterns/index.ts', dest: 'dashboard/src/components/patterns/index.ts', template: false },
|
|
396
498
|
{ src: 'full/dashboard/src/components/notifications/NotificationBell.tsx', dest: 'dashboard/src/components/notifications/NotificationBell.tsx', template: false },
|
|
397
499
|
{ src: 'full/dashboard/src/components/notifications/NotificationList.tsx', dest: 'dashboard/src/components/notifications/NotificationList.tsx', template: false },
|
|
500
|
+
{ src: 'full/dashboard/src/components/notifications/NotificationDropdown.tsx', dest: 'dashboard/src/components/notifications/NotificationDropdown.tsx', template: false },
|
|
501
|
+
{ src: 'full/dashboard/src/components/notifications/NotificationItem.tsx', dest: 'dashboard/src/components/notifications/NotificationItem.tsx', template: false },
|
|
398
502
|
{ src: 'full/dashboard/src/components/notifications/index.ts', dest: 'dashboard/src/components/notifications/index.ts', template: false },
|
|
399
503
|
{ src: 'full/dashboard/src/components/search/SearchModal.tsx', dest: 'dashboard/src/components/search/SearchModal.tsx', template: false },
|
|
504
|
+
{ src: 'full/dashboard/src/components/search/SearchResultGroup.tsx', dest: 'dashboard/src/components/search/SearchResultGroup.tsx', template: false },
|
|
505
|
+
{ src: 'full/dashboard/src/components/search/SearchResultItem.tsx', dest: 'dashboard/src/components/search/SearchResultItem.tsx', template: false },
|
|
400
506
|
// Dashboard — full tier: pattern tabs component (v1.6.0)
|
|
401
507
|
{ src: 'full/dashboard/src/components/patterns/PatternTabs.tsx', dest: 'dashboard/src/components/patterns/PatternTabs.tsx', template: false },
|
|
402
508
|
// Dashboard — full tier: additional pattern API routes (v2.0.0)
|
|
@@ -417,15 +523,24 @@ const FULL_FILES = [
|
|
|
417
523
|
// Dashboard — full tier: reports API routes (v2.0.0)
|
|
418
524
|
{ src: 'full/dashboard/src/pages/api/reports/audit.ts', dest: 'dashboard/src/pages/api/reports/audit.ts', template: false },
|
|
419
525
|
{ src: 'full/dashboard/src/pages/api/reports/usage.ts', dest: 'dashboard/src/pages/api/reports/usage.ts', template: false },
|
|
526
|
+
{ src: 'full/dashboard/src/pages/api/reports/digests.ts', dest: 'dashboard/src/pages/api/reports/digests.ts', template: false },
|
|
527
|
+
{ src: 'full/dashboard/src/pages/api/reports/digests/stats.ts', dest: 'dashboard/src/pages/api/reports/digests/stats.ts', template: false },
|
|
420
528
|
// Dashboard — full tier: topology API route (v2.0.0)
|
|
421
529
|
{ src: 'full/dashboard/src/pages/api/topology/index.ts', dest: 'dashboard/src/pages/api/topology/index.ts', template: false },
|
|
530
|
+
// Dashboard — full tier: test reports + parameterized pattern/search routes (v2.2.0)
|
|
531
|
+
{ src: 'full/dashboard/src/pages/api/test-reports/[id].ts', dest: 'dashboard/src/pages/api/test-reports/[id].ts', template: false },
|
|
532
|
+
{ src: 'full/dashboard/src/pages/api/patterns/[id]/approve.ts', dest: 'dashboard/src/pages/api/patterns/[id]/approve.ts', template: false },
|
|
533
|
+
{ src: 'full/dashboard/src/pages/api/patterns/[id]/reject.ts', dest: 'dashboard/src/pages/api/patterns/[id]/reject.ts', template: false },
|
|
534
|
+
{ src: 'full/dashboard/src/pages/api/search/reindex/[type].ts', dest: 'dashboard/src/pages/api/search/reindex/[type].ts', template: false },
|
|
422
535
|
// Dashboard — full tier: additional pattern components (v2.0.0)
|
|
423
536
|
{ src: 'full/dashboard/src/components/patterns/ActivePatterns.tsx', dest: 'dashboard/src/components/patterns/ActivePatterns.tsx', template: false },
|
|
424
537
|
{ src: 'full/dashboard/src/components/patterns/SystemPatterns.tsx', dest: 'dashboard/src/components/patterns/SystemPatterns.tsx', template: false },
|
|
425
538
|
// Dashboard — full tier: reports components (v2.0.0)
|
|
426
539
|
{ src: 'full/dashboard/src/components/reports/SdkAuditReport.tsx', dest: 'dashboard/src/components/reports/SdkAuditReport.tsx', template: false },
|
|
427
540
|
{ src: 'full/dashboard/src/components/reports/GapDetectionReport.tsx', dest: 'dashboard/src/components/reports/GapDetectionReport.tsx', template: false },
|
|
541
|
+
{ src: 'full/dashboard/src/components/reports/FeatureUsageReport.tsx', dest: 'dashboard/src/components/reports/FeatureUsageReport.tsx', template: false },
|
|
428
542
|
{ src: 'full/dashboard/src/components/reports/index.ts', dest: 'dashboard/src/components/reports/index.ts', template: false },
|
|
543
|
+
{ src: 'full/dashboard/src/components/patterns/PatternInfoButton.tsx', dest: 'dashboard/src/components/patterns/PatternInfoButton.tsx', template: false },
|
|
429
544
|
// Dashboard — full tier: lib modules (v2.1.0)
|
|
430
545
|
{ src: 'full/dashboard/src/lib/cloudflare/graphql.ts', dest: 'dashboard/src/lib/cloudflare/graphql.ts', template: false },
|
|
431
546
|
{ src: 'full/dashboard/src/lib/cloudflare/alerting.ts', dest: 'dashboard/src/lib/cloudflare/alerting.ts', template: false },
|
|
@@ -447,6 +562,9 @@ const FULL_FILES = [
|
|
|
447
562
|
// Dashboard — full tier: reports components (v2.1.0)
|
|
448
563
|
{ src: 'full/dashboard/src/components/reports/HealthTrendsReport.tsx', dest: 'dashboard/src/components/reports/HealthTrendsReport.tsx', template: false },
|
|
449
564
|
{ src: 'full/dashboard/src/components/reports/DigestStats.tsx', dest: 'dashboard/src/components/reports/DigestStats.tsx', template: false },
|
|
565
|
+
// Tests — full tier (v2.2.0)
|
|
566
|
+
{ src: 'full/tests/integration/r2-archive.test.ts', dest: 'tests/integration/r2-archive.test.ts', template: false },
|
|
567
|
+
{ src: 'full/tests/integration/feedback-schema.test.ts', dest: 'tests/integration/feedback-schema.test.ts', template: false },
|
|
450
568
|
];
|
|
451
569
|
export function getFilesForTier(tier) {
|
|
452
570
|
const files = [...SHARED_FILES];
|
package/package.json
CHANGED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NotificationDropdown.tsx
|
|
3
|
+
*
|
|
4
|
+
* Dropdown panel showing recent notifications with mark-as-read actions.
|
|
5
|
+
*
|
|
6
|
+
* @module dashboard/components/notifications/NotificationDropdown
|
|
7
|
+
* @created 2026-02-03
|
|
8
|
+
* @task task-303.2
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { NotificationItem } from './NotificationItem';
|
|
12
|
+
import type { Notification } from '../../lib/notifications/types';
|
|
13
|
+
|
|
14
|
+
export interface NotificationDropdownProps {
|
|
15
|
+
notifications: Notification[];
|
|
16
|
+
readIds: Set<string>;
|
|
17
|
+
isOpen: boolean;
|
|
18
|
+
onMarkRead: (id: string) => void;
|
|
19
|
+
onMarkAllRead: () => void;
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
loading?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function NotificationDropdown({
|
|
25
|
+
notifications,
|
|
26
|
+
readIds,
|
|
27
|
+
isOpen,
|
|
28
|
+
onMarkRead,
|
|
29
|
+
onMarkAllRead,
|
|
30
|
+
_onClose,
|
|
31
|
+
loading = false,
|
|
32
|
+
}: NotificationDropdownProps): JSX.Element | null {
|
|
33
|
+
if (!isOpen) return null;
|
|
34
|
+
|
|
35
|
+
const hasUnread = notifications.some((n) => !readIds.has(n.id));
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className="absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 z-60 overflow-hidden animate-slide-down"
|
|
40
|
+
role="menu"
|
|
41
|
+
aria-orientation="vertical"
|
|
42
|
+
aria-label="Notifications"
|
|
43
|
+
>
|
|
44
|
+
{/* Header */}
|
|
45
|
+
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-200 dark:border-gray-700">
|
|
46
|
+
<h3 className="text-sm font-semibold text-gray-900 dark:text-white">Notifications</h3>
|
|
47
|
+
{hasUnread ? (
|
|
48
|
+
<button
|
|
49
|
+
onClick={onMarkAllRead}
|
|
50
|
+
className="text-xs text-blue-600 dark:text-blue-400 hover:underline focus:outline-none"
|
|
51
|
+
>
|
|
52
|
+
Mark all as read
|
|
53
|
+
</button>
|
|
54
|
+
) : (
|
|
55
|
+
<span className="text-xs text-gray-400">All read</span>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
{/* Notification list */}
|
|
60
|
+
<div className="max-h-80 overflow-y-auto">
|
|
61
|
+
{loading ? (
|
|
62
|
+
<div className="px-4 py-8 text-center">
|
|
63
|
+
<svg
|
|
64
|
+
className="animate-spin h-6 w-6 mx-auto text-blue-500"
|
|
65
|
+
fill="none"
|
|
66
|
+
viewBox="0 0 24 24"
|
|
67
|
+
>
|
|
68
|
+
<circle
|
|
69
|
+
className="opacity-25"
|
|
70
|
+
cx="12"
|
|
71
|
+
cy="12"
|
|
72
|
+
r="10"
|
|
73
|
+
stroke="currentColor"
|
|
74
|
+
strokeWidth="4"
|
|
75
|
+
/>
|
|
76
|
+
<path
|
|
77
|
+
className="opacity-75"
|
|
78
|
+
fill="currentColor"
|
|
79
|
+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
80
|
+
/>
|
|
81
|
+
</svg>
|
|
82
|
+
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">Loading...</p>
|
|
83
|
+
</div>
|
|
84
|
+
) : notifications.length === 0 ? (
|
|
85
|
+
<div className="px-4 py-8 text-center">
|
|
86
|
+
<svg
|
|
87
|
+
className="mx-auto h-8 w-8 text-gray-400"
|
|
88
|
+
fill="none"
|
|
89
|
+
stroke="currentColor"
|
|
90
|
+
viewBox="0 0 24 24"
|
|
91
|
+
>
|
|
92
|
+
<path
|
|
93
|
+
strokeLinecap="round"
|
|
94
|
+
strokeLinejoin="round"
|
|
95
|
+
strokeWidth={2}
|
|
96
|
+
d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"
|
|
97
|
+
/>
|
|
98
|
+
</svg>
|
|
99
|
+
<p className="mt-2 text-sm text-gray-500 dark:text-gray-400">No notifications</p>
|
|
100
|
+
<p className="text-xs text-gray-400 dark:text-gray-500">You're all caught up!</p>
|
|
101
|
+
</div>
|
|
102
|
+
) : (
|
|
103
|
+
<div className="group">
|
|
104
|
+
{notifications.map((notification) => (
|
|
105
|
+
<NotificationItem
|
|
106
|
+
key={notification.id}
|
|
107
|
+
notification={notification}
|
|
108
|
+
isRead={readIds.has(notification.id)}
|
|
109
|
+
onMarkRead={onMarkRead}
|
|
110
|
+
compact
|
|
111
|
+
/>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
)}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
{/* Footer */}
|
|
118
|
+
<div className="px-4 py-3 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/50">
|
|
119
|
+
<a
|
|
120
|
+
href="/notifications"
|
|
121
|
+
className="text-sm text-blue-600 dark:text-blue-400 hover:underline"
|
|
122
|
+
>
|
|
123
|
+
View all notifications
|
|
124
|
+
</a>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default NotificationDropdown;
|