@wopr-network/platform-ui-core 1.27.7 → 1.27.9
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/next.config.ts +1 -2
- package/package.json +17 -17
- package/src/__tests__/account-switcher.test.tsx +21 -20
- package/src/__tests__/activity-page.test.tsx +2 -6
- package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
- package/src/__tests__/admin-api.test.ts +1 -6
- package/src/__tests__/admin-gpu-api.test.ts +1 -3
- package/src/__tests__/admin-marketplace-api.test.ts +1 -4
- package/src/__tests__/admin-middleware.test.ts +76 -83
- package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
- package/src/__tests__/api-401-redirect.test.ts +46 -9
- package/src/__tests__/api-client.test.ts +3 -5
- package/src/__tests__/api-config.test.ts +22 -42
- package/src/__tests__/api-fleet-resources.test.ts +1 -2
- package/src/__tests__/api-fleet-trpc.test.ts +2 -8
- package/src/__tests__/api-null-guards.test.ts +3 -1
- package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
- package/src/__tests__/auth-password-reset.test.tsx +7 -21
- package/src/__tests__/auth-redirect.test.tsx +8 -2
- package/src/__tests__/auth.test.tsx +25 -23
- package/src/__tests__/auto-topup-card.test.tsx +4 -12
- package/src/__tests__/backups-tab.test.tsx +3 -4
- package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
- package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
- package/src/__tests__/billing.test.tsx +8 -39
- package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
- package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
- package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
- package/src/__tests__/bot-settings-restart.test.tsx +1 -3
- package/src/__tests__/bot-settings.test.tsx +2 -6
- package/src/__tests__/brand.test.ts +6 -26
- package/src/__tests__/buy-credits-panel.test.tsx +1 -3
- package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
- package/src/__tests__/capability-conflicts.test.ts +2 -8
- package/src/__tests__/capability-resolver.test.tsx +2 -12
- package/src/__tests__/channel-wizard.test.tsx +4 -17
- package/src/__tests__/chat/chat-panel.test.tsx +1 -4
- package/src/__tests__/chat-store.test.ts +5 -15
- package/src/__tests__/command-center.test.tsx +10 -12
- package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
- package/src/__tests__/confirmation-tracker.test.tsx +3 -18
- package/src/__tests__/coupon-input.test.tsx +1 -3
- package/src/__tests__/create-instance.test.tsx +1 -3
- package/src/__tests__/credit-balance.test.tsx +4 -12
- package/src/__tests__/credits.test.tsx +32 -85
- package/src/__tests__/email-verification-banner.test.tsx +2 -6
- package/src/__tests__/error-boundaries.test.tsx +0 -1
- package/src/__tests__/fetch-pricing.test.ts +2 -1
- package/src/__tests__/field-oauth.test.tsx +2 -6
- package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
- package/src/__tests__/fixtures/mock-manifests.ts +2 -4
- package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
- package/src/__tests__/fleet-health-update.test.tsx +1 -8
- package/src/__tests__/gpu-dashboard.test.tsx +2 -6
- package/src/__tests__/instance-detail.test.tsx +3 -9
- package/src/__tests__/instance-list.test.tsx +1 -5
- package/src/__tests__/layout-snapshots.test.tsx +64 -11
- package/src/__tests__/marketplace-admin.test.tsx +2 -6
- package/src/__tests__/marketplace.test.tsx +11 -35
- package/src/__tests__/merge-api-rates.test.ts +1 -6
- package/src/__tests__/middleware.test.ts +32 -219
- package/src/__tests__/next-config-headers.test.ts +1 -3
- package/src/__tests__/notifications.test.tsx +4 -11
- package/src/__tests__/oauth-buttons.test.tsx +36 -59
- package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
- package/src/__tests__/observability.test.tsx +23 -36
- package/src/__tests__/onboarding-page.test.tsx +4 -6
- package/src/__tests__/org-billing-api.test.tsx +1 -6
- package/src/__tests__/plugin-install-flow.test.tsx +28 -58
- package/src/__tests__/plugin-registry.test.tsx +3 -11
- package/src/__tests__/plugin-tool-sync.test.ts +1 -3
- package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
- package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
- package/src/__tests__/portfolio-chart.test.tsx +2 -6
- package/src/__tests__/promotion-form.test.tsx +2 -6
- package/src/__tests__/promotions-list.test.tsx +1 -3
- package/src/__tests__/provider-key-api.test.ts +2 -1
- package/src/__tests__/resend-verification-button.test.tsx +8 -24
- package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
- package/src/__tests__/settings.test.tsx +11 -21
- package/src/__tests__/setup-checklist.test.tsx +3 -9
- package/src/__tests__/setup.ts +25 -6
- package/src/__tests__/snapshot-api.test.ts +2 -1
- package/src/__tests__/step-superpowers.test.tsx +1 -3
- package/src/__tests__/tenant-context.test.tsx +1 -6
- package/src/__tests__/tenant-keys-api.test.ts +3 -4
- package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
- package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
- package/src/__tests__/transaction-history.test.tsx +190 -238
- package/src/__tests__/trpc-types.test.ts +2 -6
- package/src/__tests__/use-chat.test.ts +1 -3
- package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
- package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
- package/src/__tests__/use-webmcp.test.ts +1 -3
- package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
- package/src/__tests__/verify-page.test.tsx +4 -13
- package/src/__tests__/verify-redirect.test.tsx +2 -6
- package/src/app/(auth)/error.tsx +1 -7
- package/src/app/(auth)/forgot-password/page.tsx +4 -18
- package/src/app/(auth)/login/page.tsx +5 -22
- package/src/app/(auth)/reset-password/page.tsx +2 -12
- package/src/app/(auth)/signup/page.tsx +10 -44
- package/src/app/(auth)/verify/page.tsx +47 -0
- package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
- package/src/app/(dashboard)/billing/error.tsx +2 -10
- package/src/app/(dashboard)/billing/layout.tsx +12 -62
- package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
- package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
- package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
- package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
- package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
- package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
- package/src/app/(dashboard)/changesets/error.tsx +1 -7
- package/src/app/(dashboard)/chat/page.tsx +2 -6
- package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
- package/src/app/(dashboard)/error.tsx +1 -7
- package/src/app/(dashboard)/layout.tsx +15 -36
- package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
- package/src/app/(dashboard)/marketplace/error.tsx +1 -7
- package/src/app/(dashboard)/marketplace/page.tsx +6 -27
- package/src/app/(dashboard)/not-found.tsx +2 -5
- package/src/app/(dashboard)/onboarding/page.tsx +5 -22
- package/src/app/(dashboard)/settings/account/page.tsx +1 -6
- package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
- package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
- package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
- package/src/app/(dashboard)/settings/error.tsx +2 -10
- package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
- package/src/app/(dashboard)/settings/org/page.tsx +13 -56
- package/src/app/(dashboard)/settings/page.tsx +1 -0
- package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
- package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
- package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
- package/src/app/(dashboard)/settings/security/page.tsx +31 -111
- package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
- package/src/app/admin/error.tsx +1 -7
- package/src/app/admin/fleet-updates/error.tsx +1 -7
- package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
- package/src/app/admin/layout.tsx +4 -0
- package/src/app/admin/payment-methods/page.tsx +9 -38
- package/src/app/admin/products/error.tsx +2 -7
- package/src/app/admin/products/page.tsx +1 -4
- package/src/app/admin/promotions/[id]/page.tsx +9 -38
- package/src/app/admin/promotions/page.tsx +9 -36
- package/src/app/admin/rate-overrides/page.tsx +9 -45
- package/src/app/auth/callback/[provider]/page.tsx +1 -8
- package/src/app/auth/verify/page.tsx +9 -36
- package/src/app/channels/error.tsx +2 -10
- package/src/app/channels/layout.tsx +9 -0
- package/src/app/channels/page.tsx +8 -20
- package/src/app/channels/setup/[plugin]/page.tsx +3 -5
- package/src/app/error.tsx +1 -7
- package/src/app/fleet/error.tsx +1 -7
- package/src/app/fleet/layout.tsx +5 -0
- package/src/app/fleet/settings/page.tsx +1 -3
- package/src/app/global-error.tsx +2 -10
- package/src/app/globals.css +1 -4
- package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
- package/src/app/instances/error.tsx +2 -10
- package/src/app/instances/instance-list-client.tsx +20 -69
- package/src/app/instances/layout.tsx +9 -0
- package/src/app/instances/new/create-instance-client.tsx +10 -31
- package/src/app/layout.tsx +2 -10
- package/src/app/not-found.tsx +1 -3
- package/src/app/page.tsx +1 -2
- package/src/app/plugins/error.tsx +2 -10
- package/src/app/plugins/layout.tsx +5 -0
- package/src/app/plugins/page.tsx +16 -48
- package/src/app/pricing/error.tsx +1 -7
- package/src/app/privacy/page.tsx +93 -150
- package/src/app/status/error.tsx +1 -7
- package/src/app/terms/page.tsx +89 -144
- package/src/components/account-switcher.tsx +25 -52
- package/src/components/admin/accounting-dashboard.tsx +1 -3
- package/src/components/admin/admin-guard.tsx +1 -3
- package/src/components/admin/admin-nav.tsx +1 -3
- package/src/components/admin/affiliate-dashboard.tsx +25 -94
- package/src/components/admin/audit-log-table.tsx +13 -49
- package/src/components/admin/billing-health-dashboard.tsx +7 -25
- package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
- package/src/components/admin/bulk-actions-bar.tsx +1 -3
- package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
- package/src/components/admin/bulk-export-dialog.tsx +6 -32
- package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
- package/src/components/admin/bulk-grant-dialog.tsx +4 -15
- package/src/components/admin/bulk-preview-dialog.tsx +3 -12
- package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
- package/src/components/admin/bulk-select-all-banner.tsx +1 -6
- package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
- package/src/components/admin/bulk-undo-toast.tsx +1 -2
- package/src/components/admin/compliance-dashboard.tsx +31 -101
- package/src/components/admin/gpu-dashboard.tsx +21 -70
- package/src/components/admin/grant-credits-dialog.tsx +4 -17
- package/src/components/admin/incident-dashboard.tsx +10 -25
- package/src/components/admin/inference-dashboard.tsx +14 -54
- package/src/components/admin/marketplace-admin.tsx +18 -60
- package/src/components/admin/migrations-dashboard.tsx +9 -42
- package/src/components/admin/onboarding-dashboard.tsx +14 -64
- package/src/components/admin/pool-config-dashboard.tsx +4 -10
- package/src/components/admin/products/fleet-form.tsx +2 -11
- package/src/components/admin/products/nav-editor.tsx +3 -10
- package/src/components/admin/promotions/promotion-form.tsx +9 -42
- package/src/components/admin/roles-dashboard.tsx +7 -34
- package/src/components/admin/suspend-dialog.tsx +4 -11
- package/src/components/admin/tenant-notes-panel.tsx +1 -3
- package/src/components/admin/tenant-row-actions.tsx +4 -20
- package/src/components/admin/tenant-table.tsx +12 -49
- package/src/components/auth/auth-redirect.tsx +11 -3
- package/src/components/auth/email-verification-result-banner.tsx +1 -3
- package/src/components/auth/resend-verification-button.tsx +2 -10
- package/src/components/auth/wopr-wordmark.tsx +1 -3
- package/src/components/billing/add-payment-method-dialog.tsx +1 -2
- package/src/components/billing/affiliate-dashboard.tsx +4 -16
- package/src/components/billing/amount-selector.tsx +1 -3
- package/src/components/billing/auto-topup-card.tsx +2 -11
- package/src/components/billing/buy-credits-panel.tsx +14 -17
- package/src/components/billing/byok-callout.tsx +6 -8
- package/src/components/billing/confirmation-tracker.tsx +4 -14
- package/src/components/billing/credit-balance-badge.tsx +22 -0
- package/src/components/billing/credit-balance.tsx +3 -9
- package/src/components/billing/crypto-checkout.tsx +5 -24
- package/src/components/billing/degraded-state-banner.tsx +1 -3
- package/src/components/billing/deposit-view.tsx +301 -41
- package/src/components/billing/dividend-banner.tsx +1 -3
- package/src/components/billing/dividend-eligibility.tsx +3 -12
- package/src/components/billing/dividend-pool-stats.tsx +6 -20
- package/src/components/billing/first-dividend-dialog.tsx +2 -2
- package/src/components/billing/org-billing-page.tsx +8 -31
- package/src/components/billing/payment-method-picker.tsx +2 -10
- package/src/components/billing/suspension-banner.tsx +2 -7
- package/src/components/billing/transaction-history.tsx +10 -58
- package/src/components/billing/unified-checkout.tsx +547 -0
- package/src/components/bot-settings/backups-tab.tsx +9 -33
- package/src/components/bot-settings/bot-settings-client.tsx +32 -134
- package/src/components/bot-settings/resources-tab.tsx +2 -9
- package/src/components/bot-settings/storage-tab.tsx +19 -48
- package/src/components/bot-settings/vps-info-panel.tsx +3 -11
- package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
- package/src/components/brand-hydrator.tsx +13 -0
- package/src/components/channel-wizard/field-interactive.tsx +1 -3
- package/src/components/channel-wizard/field-qr.tsx +10 -39
- package/src/components/channel-wizard/step-renderer.tsx +5 -28
- package/src/components/channel-wizard/wizard.tsx +6 -31
- package/src/components/chat/chat-message.tsx +1 -4
- package/src/components/chat/chat-panel.tsx +4 -18
- package/src/components/chat/chat-widget.tsx +3 -14
- package/src/components/dashboard/command-center.tsx +15 -61
- package/src/components/fleet/update-settings-card.tsx +7 -23
- package/src/components/instance-update-banner.tsx +130 -0
- package/src/components/instances/friends-tab.test.tsx +2 -9
- package/src/components/instances/friends-tab.tsx +18 -74
- package/src/components/instances/update-available-badge.tsx +2 -11
- package/src/components/landing/hero.tsx +3 -9
- package/src/components/landing/landing-page.tsx +1 -3
- package/src/components/landing/portfolio-chart.tsx +4 -9
- package/src/components/landing/story-sections.tsx +1 -3
- package/src/components/landing/terminal-sequence.tsx +4 -17
- package/src/components/marketplace/empty-state.tsx +2 -6
- package/src/components/marketplace/first-visit-hero.tsx +1 -3
- package/src/components/marketplace/install-wizard.tsx +20 -77
- package/src/components/marketplace/marketplace-tabs.tsx +1 -4
- package/src/components/marketplace/plugin-card.tsx +2 -9
- package/src/components/marketplace/superpower-content.tsx +1 -3
- package/src/components/marketplace/terminal-search.tsx +2 -8
- package/src/components/oauth-buttons.tsx +29 -14
- package/src/components/observability/fleet-health.tsx +5 -18
- package/src/components/observability/health-overview.tsx +7 -20
- package/src/components/observability/logs-viewer.tsx +8 -32
- package/src/components/observability/metrics-dashboard.tsx +2 -15
- package/src/components/onboarding/fallback-setup.tsx +6 -25
- package/src/components/onboarding/setup-checklist.tsx +18 -51
- package/src/components/onboarding/step-superpowers.tsx +1 -4
- package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
- package/src/components/pricing/dividend-calculator.tsx +6 -12
- package/src/components/pricing/dividend-stats.tsx +5 -17
- package/src/components/pricing/pricing-page.tsx +17 -36
- package/src/components/settings/create-org-wizard.tsx +2 -5
- package/src/components/sidebar.tsx +7 -42
- package/src/components/sidecar-frame.tsx +78 -0
- package/src/components/status/status-page.tsx +6 -28
- package/src/components/ui/alert-dialog.tsx +8 -25
- package/src/components/ui/badge.tsx +2 -8
- package/src/components/ui/banner.tsx +1 -6
- package/src/components/ui/card.tsx +5 -24
- package/src/components/ui/checkbox.tsx +1 -5
- package/src/components/ui/collapsible.tsx +3 -8
- package/src/components/ui/dialog.tsx +4 -10
- package/src/components/ui/dropdown-menu.tsx +9 -18
- package/src/components/ui/form.tsx +2 -16
- package/src/components/ui/popover.tsx +3 -23
- package/src/components/ui/progress.tsx +1 -5
- package/src/components/ui/radio-group.tsx +3 -15
- package/src/components/ui/select.tsx +4 -17
- package/src/components/ui/sheet.tsx +5 -19
- package/src/components/ui/skeleton.tsx +1 -7
- package/src/components/ui/table.tsx +5 -22
- package/src/components/ui/tabs.tsx +3 -13
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/components/unified-sidebar.tsx +493 -0
- package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
- package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
- package/src/hooks/use-credit-balance.ts +27 -0
- package/src/hooks/use-my-org-role.ts +1 -3
- package/src/hooks/use-plugin-registry.ts +8 -14
- package/src/hooks/use-plugin-setup-chat.ts +2 -5
- package/src/hooks/use-sidecar-bridge.tsx +148 -0
- package/src/hooks/use-webmcp.ts +1 -4
- package/src/lib/__tests__/admin-api.test.ts +1 -3
- package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
- package/src/lib/__tests__/api-fetch.test.ts +4 -16
- package/src/lib/__tests__/org-billing-api.test.ts +1 -3
- package/src/lib/__tests__/pricing-data.test.ts +0 -8
- package/src/lib/__tests__/settings-api.test.ts +1 -3
- package/src/lib/admin-affiliate-api.ts +2 -7
- package/src/lib/admin-api.ts +6 -26
- package/src/lib/admin-incident-api.ts +11 -19
- package/src/lib/admin-marketplace-api.ts +1 -5
- package/src/lib/api-config.test.ts +5 -50
- package/src/lib/api.ts +143 -122
- package/src/lib/auth-client.ts +1 -2
- package/src/lib/bot-settings-data.ts +11 -36
- package/src/lib/brand-config.ts +56 -115
- package/src/lib/brand.ts +2 -15
- package/src/lib/chat/use-chat.ts +2 -7
- package/src/lib/cost-comparison-data.test.ts +1 -3
- package/src/lib/cost-comparison-data.ts +1 -4
- package/src/lib/errors.ts +1 -4
- package/src/lib/fetch-utils.test.ts +26 -9
- package/src/lib/fetch-utils.ts +40 -11
- package/src/lib/logger.ts +2 -0
- package/src/lib/marketplace-data.ts +3 -11
- package/src/lib/oauth-errors.ts +2 -4
- package/src/lib/onboarding-data.ts +3 -11
- package/src/lib/org-api.ts +2 -10
- package/src/lib/org-billing-api.ts +5 -19
- package/src/lib/plugin/tool-definitions.ts +1 -2
- package/src/lib/require-auth.ts +57 -0
- package/src/lib/settings-api.ts +1 -4
- package/src/lib/sidecar-routes.ts +43 -0
- package/src/lib/trpc-server.ts +49 -0
- package/src/lib/trpc-types.ts +4 -6
- package/src/lib/trpc.tsx +12 -4
- package/src/lib/validate-redirect-url.ts +1 -4
- package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
- package/src/lib/webmcp/register.ts +1 -4
- package/src/lib/webmcp/tools.ts +2 -9
- package/src/proxy.ts +35 -212
- package/src/types/missing-deps.d.ts +2 -8
- package/tsconfig.json +1 -8
- package/biome.json +0 -52
- package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
- package/src/__tests__/billing-byok-callout.test.tsx +0 -76
- package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
|
@@ -13,12 +13,11 @@ export function DividendCalculator() {
|
|
|
13
13
|
<div className="space-y-2 text-center">
|
|
14
14
|
<p className="text-lg text-foreground">
|
|
15
15
|
<span className="font-semibold text-terminal">100K active users</span> ·{" "}
|
|
16
|
-
<span className="font-semibold text-terminal">$20/month average spend</span> ·
|
|
17
|
-
equal daily share
|
|
16
|
+
<span className="font-semibold text-terminal">$20/month average spend</span> · equal daily share
|
|
18
17
|
</p>
|
|
19
18
|
<p className="text-muted-foreground">
|
|
20
|
-
Projected dividend: <span className="font-semibold text-terminal">~$0.67/day</span>.
|
|
21
|
-
|
|
19
|
+
Projected dividend: <span className="font-semibold text-terminal">~$0.67/day</span>. Average daily spend:{" "}
|
|
20
|
+
<span className="font-semibold text-terminal">~$0.67</span>.
|
|
22
21
|
</p>
|
|
23
22
|
<p className="text-xl font-bold text-terminal" data-testid="net-cost">
|
|
24
23
|
Net cost of credits at scale: $0.
|
|
@@ -29,17 +28,12 @@ export function DividendCalculator() {
|
|
|
29
28
|
|
|
30
29
|
<Card className="border-amber-500/20 bg-amber-500/5">
|
|
31
30
|
<CardContent className="space-y-2 py-6 text-center">
|
|
32
|
-
<p className="text-sm uppercase tracking-widest text-amber-500">
|
|
33
|
-
Early adopter advantage
|
|
34
|
-
</p>
|
|
31
|
+
<p className="text-sm uppercase tracking-widest text-amber-500">Early adopter advantage</p>
|
|
35
32
|
<p className="text-foreground">
|
|
36
33
|
The earlier you join, the more you accumulate. Day 1 users in a 100K community collect{" "}
|
|
37
|
-
<span className="font-semibold text-terminal">~$132</span> in their first 60 days on a
|
|
38
|
-
$5 minimum spend.
|
|
39
|
-
</p>
|
|
40
|
-
<p className="text-sm text-muted-foreground">
|
|
41
|
-
The math doesn't lie — being early pays.
|
|
34
|
+
<span className="font-semibold text-terminal">~$132</span> in their first 60 days on a $5 minimum spend.
|
|
42
35
|
</p>
|
|
36
|
+
<p className="text-sm text-muted-foreground">The math doesn't lie — being early pays.</p>
|
|
43
37
|
</CardContent>
|
|
44
38
|
</Card>
|
|
45
39
|
</div>
|
|
@@ -14,10 +14,7 @@ function useCountUp(target: number, duration = 1200) {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
// Respect prefers-reduced-motion: skip animation, set immediately
|
|
17
|
-
if (
|
|
18
|
-
typeof window !== "undefined" &&
|
|
19
|
-
window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
20
|
-
) {
|
|
17
|
+
if (typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
21
18
|
setValue(target);
|
|
22
19
|
return;
|
|
23
20
|
}
|
|
@@ -79,9 +76,7 @@ export function DividendStats() {
|
|
|
79
76
|
{error && <p className="col-span-full text-center text-sm text-red-500">{error}</p>}
|
|
80
77
|
<Card className="border-terminal/30">
|
|
81
78
|
<CardContent className="flex flex-col items-center gap-1 py-6 text-center">
|
|
82
|
-
<p className="text-xs uppercase tracking-widest text-muted-foreground">
|
|
83
|
-
Today's community pool
|
|
84
|
-
</p>
|
|
79
|
+
<p className="text-xs uppercase tracking-widest text-muted-foreground">Today's community pool</p>
|
|
85
80
|
<p className="text-3xl font-bold text-terminal sm:text-4xl" data-testid="pool-amount">
|
|
86
81
|
{loaded && pool > 0 ? formatDollars(animatedPool) : "--"}
|
|
87
82
|
</p>
|
|
@@ -90,9 +85,7 @@ export function DividendStats() {
|
|
|
90
85
|
|
|
91
86
|
<Card className="border-terminal/30">
|
|
92
87
|
<CardContent className="flex flex-col items-center gap-1 py-6 text-center">
|
|
93
|
-
<p className="text-xs uppercase tracking-widest text-muted-foreground">
|
|
94
|
-
Active users in pool
|
|
95
|
-
</p>
|
|
88
|
+
<p className="text-xs uppercase tracking-widest text-muted-foreground">Active users in pool</p>
|
|
96
89
|
<p className="text-3xl font-bold text-terminal sm:text-4xl" data-testid="active-users">
|
|
97
90
|
{loaded && users > 0 ? Math.round(animatedUsers).toLocaleString() : "--"}
|
|
98
91
|
</p>
|
|
@@ -101,13 +94,8 @@ export function DividendStats() {
|
|
|
101
94
|
|
|
102
95
|
<Card className="border-terminal/30">
|
|
103
96
|
<CardContent className="flex flex-col items-center gap-1 py-6 text-center">
|
|
104
|
-
<p className="text-xs uppercase tracking-widest text-muted-foreground">
|
|
105
|
-
|
|
106
|
-
</p>
|
|
107
|
-
<p
|
|
108
|
-
className="text-3xl font-bold text-terminal sm:text-4xl"
|
|
109
|
-
data-testid="projected-dividend"
|
|
110
|
-
>
|
|
97
|
+
<p className="text-xs uppercase tracking-widest text-muted-foreground">Your projected daily dividend</p>
|
|
98
|
+
<p className="text-3xl font-bold text-terminal sm:text-4xl" data-testid="projected-dividend">
|
|
111
99
|
{loaded && dividend > 0 ? `~${formatCreditStandard(animatedDividend)}` : "--"}
|
|
112
100
|
</p>
|
|
113
101
|
</CardContent>
|
|
@@ -47,13 +47,13 @@ export async function PricingPage() {
|
|
|
47
47
|
</h1>
|
|
48
48
|
|
|
49
49
|
<p className="mt-6 max-w-xl text-lg text-muted-foreground">
|
|
50
|
-
Every day, the platform distributes credits back to active users from its own margin. The
|
|
51
|
-
|
|
50
|
+
Every day, the platform distributes credits back to active users from its own margin. The bigger the community
|
|
51
|
+
grows, the more you receive. Early users get the most.
|
|
52
52
|
</p>
|
|
53
53
|
|
|
54
54
|
<p className="mt-4 max-w-lg text-sm text-muted-foreground">
|
|
55
|
-
At scale, the daily dividend covers your entire credit spend. You're not paying to
|
|
56
|
-
|
|
55
|
+
At scale, the daily dividend covers your entire credit spend. You're not paying to run your bots.{" "}
|
|
56
|
+
{brandName()} is.
|
|
57
57
|
</p>
|
|
58
58
|
</section>
|
|
59
59
|
|
|
@@ -75,22 +75,17 @@ export async function PricingPage() {
|
|
|
75
75
|
<span className="font-semibold text-terminal">
|
|
76
76
|
${pricingData.bot_price.amount}/{pricingData.bot_price.period}
|
|
77
77
|
</span>
|
|
78
|
-
. That's the minimum to be eligible for the daily dividend. Usage is billed at cost
|
|
79
|
-
from credits.
|
|
78
|
+
. That's the minimum to be eligible for the daily dividend. Usage is billed at cost from credits.
|
|
80
79
|
</p>
|
|
81
80
|
|
|
82
81
|
<Card className="w-full max-w-md border-terminal">
|
|
83
82
|
<CardHeader className="items-center text-center">
|
|
84
|
-
<CardTitle className="text-sm uppercase tracking-widest text-muted-foreground">
|
|
85
|
-
Pool eligibility
|
|
86
|
-
</CardTitle>
|
|
83
|
+
<CardTitle className="text-sm uppercase tracking-widest text-muted-foreground">Pool eligibility</CardTitle>
|
|
87
84
|
</CardHeader>
|
|
88
85
|
<CardContent className="flex flex-col items-center gap-4">
|
|
89
86
|
<p className="text-5xl font-bold text-terminal sm:text-6xl" data-testid="bot-price">
|
|
90
87
|
${pricingData.bot_price.amount}
|
|
91
|
-
<span className="text-xl font-normal text-muted-foreground">
|
|
92
|
-
/{pricingData.bot_price.period}
|
|
93
|
-
</span>
|
|
88
|
+
<span className="text-xl font-normal text-muted-foreground">/{pricingData.bot_price.period}</span>
|
|
94
89
|
</p>
|
|
95
90
|
<p className="text-muted-foreground">Minimum spend to stay in the dividend pool.</p>
|
|
96
91
|
</CardContent>
|
|
@@ -121,9 +116,7 @@ export async function PricingPage() {
|
|
|
121
116
|
<p className="font-medium">{model.name}</p>
|
|
122
117
|
<p className="text-sm text-muted-foreground">per {model.unit}</p>
|
|
123
118
|
</div>
|
|
124
|
-
<p className="text-lg font-bold text-terminal">
|
|
125
|
-
{formatPrice(model.price)}
|
|
126
|
-
</p>
|
|
119
|
+
<p className="text-lg font-bold text-terminal">{formatPrice(model.price)}</p>
|
|
127
120
|
</CardContent>
|
|
128
121
|
</Card>
|
|
129
122
|
))}
|
|
@@ -136,19 +129,15 @@ export async function PricingPage() {
|
|
|
136
129
|
|
|
137
130
|
{/* --- VPS Tier --- */}
|
|
138
131
|
<section className="mx-auto max-w-4xl px-6 pb-24">
|
|
139
|
-
<h2 className="mb-4 text-center text-2xl font-bold tracking-tight sm:text-3xl">
|
|
140
|
-
Need a dedicated machine?
|
|
141
|
-
</h2>
|
|
132
|
+
<h2 className="mb-4 text-center text-2xl font-bold tracking-tight sm:text-3xl">Need a dedicated machine?</h2>
|
|
142
133
|
<p className="mb-8 text-center text-muted-foreground">
|
|
143
|
-
The VPS tier gives your bot a persistent container with fixed monthly pricing — no
|
|
144
|
-
|
|
134
|
+
The VPS tier gives your bot a persistent container with fixed monthly pricing — no per-credit billing for
|
|
135
|
+
compute.
|
|
145
136
|
</p>
|
|
146
137
|
<div className="mx-auto max-w-sm">
|
|
147
138
|
<Card className="border-terminal/50">
|
|
148
139
|
<CardHeader className="items-center text-center">
|
|
149
|
-
<CardTitle className="text-sm uppercase tracking-widest text-muted-foreground">
|
|
150
|
-
VPS tier
|
|
151
|
-
</CardTitle>
|
|
140
|
+
<CardTitle className="text-sm uppercase tracking-widest text-muted-foreground">VPS tier</CardTitle>
|
|
152
141
|
</CardHeader>
|
|
153
142
|
<CardContent className="flex flex-col items-center gap-4">
|
|
154
143
|
<p className="text-5xl font-bold text-terminal sm:text-6xl">
|
|
@@ -160,8 +149,7 @@ export async function PricingPage() {
|
|
|
160
149
|
<span className="text-terminal">✓</span> 2 GB RAM / 2 vCPU / 20 GB SSD
|
|
161
150
|
</li>
|
|
162
151
|
<li className="flex items-center gap-2">
|
|
163
|
-
<span className="text-terminal">✓</span> Persistent container — data survives
|
|
164
|
-
restarts
|
|
152
|
+
<span className="text-terminal">✓</span> Persistent container — data survives restarts
|
|
165
153
|
</li>
|
|
166
154
|
<li className="flex items-center gap-2">
|
|
167
155
|
<span className="text-terminal">✓</span> Dedicated hostname
|
|
@@ -173,12 +161,7 @@ export async function PricingPage() {
|
|
|
173
161
|
<span className="text-terminal">✓</span> Flat monthly price — no metered compute
|
|
174
162
|
</li>
|
|
175
163
|
</ul>
|
|
176
|
-
<Button
|
|
177
|
-
data-onboarding-id="pricing.subscribe.vps"
|
|
178
|
-
variant="terminal"
|
|
179
|
-
className="w-full"
|
|
180
|
-
asChild
|
|
181
|
-
>
|
|
164
|
+
<Button data-onboarding-id="pricing.subscribe.vps" variant="terminal" className="w-full" asChild>
|
|
182
165
|
<Link href="/signup">Get started</Link>
|
|
183
166
|
</Button>
|
|
184
167
|
</CardContent>
|
|
@@ -189,8 +172,8 @@ export async function PricingPage() {
|
|
|
189
172
|
{/* --- Credits Explainer --- */}
|
|
190
173
|
<section className="flex flex-col items-center justify-center gap-6 px-6 pb-24 text-center">
|
|
191
174
|
<p className="max-w-lg text-lg text-muted-foreground">
|
|
192
|
-
Your bot is ${pricingData.bot_price.amount}/mo. Usage is billed from credits. Free tier
|
|
193
|
-
|
|
175
|
+
Your bot is ${pricingData.bot_price.amount}/mo. Usage is billed from credits. Free tier includes $
|
|
176
|
+
{pricingData.signup_credit} signup credit.
|
|
194
177
|
</p>
|
|
195
178
|
<p className="text-sm text-muted-foreground">
|
|
196
179
|
Credits in, dividend back. The community grows, your costs shrink.
|
|
@@ -207,9 +190,7 @@ export async function PricingPage() {
|
|
|
207
190
|
<Link href="/signup">Get Started</Link>
|
|
208
191
|
</Button>
|
|
209
192
|
|
|
210
|
-
<span className="mt-4 text-sm text-muted-foreground opacity-60">
|
|
211
|
-
{getBrandConfig().domain}
|
|
212
|
-
</span>
|
|
193
|
+
<span className="mt-4 text-sm text-muted-foreground opacity-60">{getBrandConfig().domain}</span>
|
|
213
194
|
</section>
|
|
214
195
|
|
|
215
196
|
{/* --- Footer --- */}
|
|
@@ -155,9 +155,7 @@ export default function CreateOrgWizard() {
|
|
|
155
155
|
>
|
|
156
156
|
<DialogHeader>
|
|
157
157
|
<DialogTitle>Confirm</DialogTitle>
|
|
158
|
-
<DialogDescription>
|
|
159
|
-
You{"'"}ll be the admin. You can invite members after setup.
|
|
160
|
-
</DialogDescription>
|
|
158
|
+
<DialogDescription>You{"'"}ll be the admin. You can invite members after setup.</DialogDescription>
|
|
161
159
|
</DialogHeader>
|
|
162
160
|
<div className="mt-4 space-y-3">
|
|
163
161
|
<div className="rounded-md border px-4 py-3 text-sm">
|
|
@@ -165,8 +163,7 @@ export default function CreateOrgWizard() {
|
|
|
165
163
|
<span className="text-muted-foreground">Name:</span> <strong>{orgName}</strong>
|
|
166
164
|
</p>
|
|
167
165
|
<p>
|
|
168
|
-
<span className="text-muted-foreground">Slug:</span>
|
|
169
|
-
<code className="text-xs">{slug}</code>
|
|
166
|
+
<span className="text-muted-foreground">Slug:</span> <code className="text-xs">{slug}</code>
|
|
170
167
|
</p>
|
|
171
168
|
</div>
|
|
172
169
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
import Image from "next/image";
|
|
21
21
|
import Link from "next/link";
|
|
22
22
|
import { usePathname, useRouter } from "next/navigation";
|
|
23
|
-
import { useCallback, useEffect, useState } from "react";
|
|
24
23
|
import { AccountSwitcher } from "@/components/account-switcher";
|
|
24
|
+
import { CreditBalanceBadge } from "@/components/billing/credit-balance-badge";
|
|
25
25
|
import {
|
|
26
26
|
DropdownMenu,
|
|
27
27
|
DropdownMenuContent,
|
|
@@ -31,10 +31,8 @@ import {
|
|
|
31
31
|
DropdownMenuTrigger,
|
|
32
32
|
} from "@/components/ui/dropdown-menu";
|
|
33
33
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
34
|
-
import { getCreditBalance } from "@/lib/api";
|
|
35
34
|
import { signOut, useSession } from "@/lib/auth-client";
|
|
36
35
|
import { getBrandConfig, productName } from "@/lib/brand-config";
|
|
37
|
-
import { formatCreditStandard } from "@/lib/format-credit";
|
|
38
36
|
import { cn } from "@/lib/utils";
|
|
39
37
|
|
|
40
38
|
function getNavItems() {
|
|
@@ -44,21 +42,13 @@ function getNavItems() {
|
|
|
44
42
|
function isNavActive(href: string, pathname: string): boolean {
|
|
45
43
|
if (href === "/dashboard") return pathname === "/dashboard";
|
|
46
44
|
if (href === "/marketplace") return pathname === "/marketplace";
|
|
47
|
-
if (href === "/settings
|
|
48
|
-
if (href === "/billing/plans")
|
|
49
|
-
return pathname.startsWith("/billing") && !pathname.startsWith("/billing/credits");
|
|
45
|
+
if (href === "/settings") return pathname.startsWith("/settings");
|
|
46
|
+
if (href === "/billing/plans") return pathname.startsWith("/billing") && !pathname.startsWith("/billing/credits");
|
|
50
47
|
if (href === "/billing/credits") return pathname.startsWith("/billing/credits");
|
|
51
48
|
if (href === "/admin/tenants" || href === "/admin") return pathname.startsWith("/admin");
|
|
52
49
|
return pathname.startsWith(href);
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
function balanceColorClass(balance: number): string {
|
|
56
|
-
if (balance === 0) return "text-red-500";
|
|
57
|
-
if (balance < 1) return "text-red-500";
|
|
58
|
-
if (balance <= 2) return "text-amber-500";
|
|
59
|
-
return "text-terminal";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
52
|
function getInitials(name: string): string {
|
|
63
53
|
return name
|
|
64
54
|
.split(" ")
|
|
@@ -90,23 +80,9 @@ export function SidebarContent({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
90
80
|
const pathname = usePathname();
|
|
91
81
|
const router = useRouter();
|
|
92
82
|
const { data: session, isPending } = useSession();
|
|
93
|
-
const [creditBalance, setCreditBalance] = useState<number | null>(null);
|
|
94
83
|
|
|
95
84
|
const user = session?.user;
|
|
96
85
|
|
|
97
|
-
const loadBalance = useCallback(async () => {
|
|
98
|
-
try {
|
|
99
|
-
const data = await getCreditBalance();
|
|
100
|
-
setCreditBalance(data.balance);
|
|
101
|
-
} catch {
|
|
102
|
-
// Silently fail — balance is non-critical UI decoration
|
|
103
|
-
}
|
|
104
|
-
}, []);
|
|
105
|
-
|
|
106
|
-
useEffect(() => {
|
|
107
|
-
if (user) loadBalance();
|
|
108
|
-
}, [user, loadBalance]);
|
|
109
|
-
|
|
110
86
|
async function handleSignOut() {
|
|
111
87
|
try {
|
|
112
88
|
await signOut();
|
|
@@ -131,8 +107,7 @@ export function SidebarContent({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
131
107
|
{getNavItems()
|
|
132
108
|
.filter(
|
|
133
109
|
(item) =>
|
|
134
|
-
!item.href.startsWith("/admin") ||
|
|
135
|
-
(user as { role?: string } | undefined)?.role === "platform_admin",
|
|
110
|
+
!item.href.startsWith("/admin") || (user as { role?: string } | undefined)?.role === "platform_admin",
|
|
136
111
|
)
|
|
137
112
|
.map((item) => {
|
|
138
113
|
const NavIcon = getNavIcon(item.href);
|
|
@@ -153,11 +128,7 @@ export function SidebarContent({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
153
128
|
{NavIcon && <NavIcon className="size-4 shrink-0 opacity-70" />}
|
|
154
129
|
{item.label}
|
|
155
130
|
</span>
|
|
156
|
-
{item.label === "Credits" &&
|
|
157
|
-
<span className={cn("text-xs font-mono", balanceColorClass(creditBalance))}>
|
|
158
|
-
{formatCreditStandard(creditBalance)}
|
|
159
|
-
</span>
|
|
160
|
-
)}
|
|
131
|
+
{item.label === "Credits" && <CreditBalanceBadge />}
|
|
161
132
|
</Link>
|
|
162
133
|
);
|
|
163
134
|
})}
|
|
@@ -190,17 +161,11 @@ export function SidebarContent({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
190
161
|
<DropdownMenuLabel className="font-normal">
|
|
191
162
|
<div className="flex flex-col gap-1">
|
|
192
163
|
{user.name && <span className="text-sm font-medium">{user.name}</span>}
|
|
193
|
-
{user.email &&
|
|
194
|
-
<span className="text-xs text-muted-foreground">{user.email}</span>
|
|
195
|
-
)}
|
|
164
|
+
{user.email && <span className="text-xs text-muted-foreground">{user.email}</span>}
|
|
196
165
|
</div>
|
|
197
166
|
</DropdownMenuLabel>
|
|
198
167
|
<DropdownMenuSeparator />
|
|
199
|
-
<DropdownMenuItem onClick={() => router.push("/settings
|
|
200
|
-
<UserIcon />
|
|
201
|
-
Profile
|
|
202
|
-
</DropdownMenuItem>
|
|
203
|
-
<DropdownMenuItem onClick={() => router.push("/settings/providers")}>
|
|
168
|
+
<DropdownMenuItem onClick={() => router.push("/settings")}>
|
|
204
169
|
<SettingsIcon />
|
|
205
170
|
Settings
|
|
206
171
|
</DropdownMenuItem>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePathname } from "next/navigation";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
6
|
+
import { useSidecarBridge } from "@/hooks/use-sidecar-bridge";
|
|
7
|
+
import { getRouteType } from "@/lib/sidecar-routes";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build the iframe src. For deep-link paths (non-/dashboard iframe routes),
|
|
11
|
+
* pass the path as an `initial-path` query so the sidecar's root redirect
|
|
12
|
+
* can land on the right page directly instead of racing with the shell's
|
|
13
|
+
* postMessage forwarding. See CompanyRootRedirect on the sidecar side.
|
|
14
|
+
*/
|
|
15
|
+
function buildIframeSrc(): string {
|
|
16
|
+
if (typeof window === "undefined") return "/_sidecar/";
|
|
17
|
+
const pathname = window.location.pathname;
|
|
18
|
+
if (getRouteType(pathname) !== "iframe" || pathname === "/dashboard") return "/_sidecar/";
|
|
19
|
+
const initialPath = pathname + window.location.search + window.location.hash;
|
|
20
|
+
return `/_sidecar/?initial-path=${encodeURIComponent(initialPath)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function SidecarFrame() {
|
|
24
|
+
// iframe loads /_sidecar/ — core API's tenant-proxy resolves the user's instance
|
|
25
|
+
const { setIframeRef, navigate } = useSidecarBridge();
|
|
26
|
+
const pathname = usePathname();
|
|
27
|
+
const iframeElRef = useRef<HTMLIFrameElement>(null);
|
|
28
|
+
const [iframeLoaded, setIframeLoaded] = useState(false);
|
|
29
|
+
// Stable across re-renders: computed once from the shell's initial URL so
|
|
30
|
+
// the iframe doesn't reload when the shell URL changes post-routeChanged.
|
|
31
|
+
const [iframeSrc] = useState(buildIframeSrc);
|
|
32
|
+
|
|
33
|
+
const routeType = getRouteType(pathname);
|
|
34
|
+
const isVisible = routeType === "iframe";
|
|
35
|
+
|
|
36
|
+
// Register iframe ref with bridge
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
setIframeRef(iframeElRef.current);
|
|
39
|
+
return () => setIframeRef(null);
|
|
40
|
+
}, [setIframeRef]);
|
|
41
|
+
|
|
42
|
+
// Handle browser back/forward
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
function onPopState() {
|
|
45
|
+
const newPath = window.location.pathname;
|
|
46
|
+
if (getRouteType(newPath) === "iframe") {
|
|
47
|
+
navigate(newPath);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
window.addEventListener("popstate", onPopState);
|
|
51
|
+
return () => window.removeEventListener("popstate", onPopState);
|
|
52
|
+
}, [navigate]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="relative flex-1 min-h-0" style={{ display: isVisible ? "flex" : "none" }}>
|
|
56
|
+
{/* Loading skeleton until sidecar posts "ready" */}
|
|
57
|
+
{isVisible && !iframeLoaded && (
|
|
58
|
+
<div className="absolute inset-0 flex flex-col gap-4 p-6">
|
|
59
|
+
<Skeleton className="h-8 w-64" />
|
|
60
|
+
<Skeleton className="h-4 w-96" />
|
|
61
|
+
<div className="flex gap-4 mt-4">
|
|
62
|
+
<Skeleton className="h-32 w-64" />
|
|
63
|
+
<Skeleton className="h-32 w-64" />
|
|
64
|
+
<Skeleton className="h-32 w-64" />
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
<iframe
|
|
69
|
+
ref={iframeElRef}
|
|
70
|
+
src={iframeSrc}
|
|
71
|
+
title="Paperclip"
|
|
72
|
+
className="h-full w-full border-0"
|
|
73
|
+
onLoad={() => setIframeLoaded(true)}
|
|
74
|
+
allow="clipboard-write"
|
|
75
|
+
/>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -17,10 +17,7 @@ const STATUS_LABELS: Record<HealthStatus, string> = {
|
|
|
17
17
|
unhealthy: "Down",
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
const BANNER_CONFIG: Record<
|
|
21
|
-
HealthStatus,
|
|
22
|
-
{ text: string; border: string; bg: string; textColor: string }
|
|
23
|
-
> = {
|
|
20
|
+
const BANNER_CONFIG: Record<HealthStatus, { text: string; border: string; bg: string; textColor: string }> = {
|
|
24
21
|
healthy: {
|
|
25
22
|
text: "> ALL SYSTEMS OPERATIONAL",
|
|
26
23
|
border: "border-terminal/20",
|
|
@@ -96,9 +93,7 @@ export function StatusPage() {
|
|
|
96
93
|
<div className="mx-auto max-w-2xl px-6 py-16">
|
|
97
94
|
<div className="space-y-1">
|
|
98
95
|
<h1 className="text-2xl font-bold tracking-tight">Platform Status</h1>
|
|
99
|
-
<p className="text-sm text-muted-foreground">
|
|
100
|
-
Real-time health of the {brandName()} platform.
|
|
101
|
-
</p>
|
|
96
|
+
<p className="text-sm text-muted-foreground">Real-time health of the {brandName()} platform.</p>
|
|
102
97
|
</div>
|
|
103
98
|
|
|
104
99
|
<div className="mt-8 space-y-6">
|
|
@@ -116,9 +111,7 @@ export function StatusPage() {
|
|
|
116
111
|
<div className="flex items-center gap-3">
|
|
117
112
|
<XCircle className="size-5 shrink-0 text-red-500" />
|
|
118
113
|
<div className="flex-1">
|
|
119
|
-
<p className="text-sm font-medium text-red-500">
|
|
120
|
-
Unable to reach the {brandName()} platform
|
|
121
|
-
</p>
|
|
114
|
+
<p className="text-sm font-medium text-red-500">Unable to reach the {brandName()} platform</p>
|
|
122
115
|
<p className="mt-1 text-xs text-muted-foreground">
|
|
123
116
|
The health endpoint is not responding. The platform may be experiencing an outage.
|
|
124
117
|
</p>
|
|
@@ -136,14 +129,7 @@ export function StatusPage() {
|
|
|
136
129
|
{(() => {
|
|
137
130
|
const cfg = BANNER_CONFIG[health.status];
|
|
138
131
|
return (
|
|
139
|
-
<div
|
|
140
|
-
className={cn(
|
|
141
|
-
"rounded-sm border px-4 py-2 text-sm font-mono",
|
|
142
|
-
cfg.border,
|
|
143
|
-
cfg.bg,
|
|
144
|
-
cfg.textColor,
|
|
145
|
-
)}
|
|
146
|
-
>
|
|
132
|
+
<div className={cn("rounded-sm border px-4 py-2 text-sm font-mono", cfg.border, cfg.bg, cfg.textColor)}>
|
|
147
133
|
{cfg.text}
|
|
148
134
|
</div>
|
|
149
135
|
);
|
|
@@ -160,9 +146,7 @@ export function StatusPage() {
|
|
|
160
146
|
</div>
|
|
161
147
|
<div className="flex items-center gap-3">
|
|
162
148
|
{svc.latencyMs !== null && (
|
|
163
|
-
<span className="text-xs font-mono text-muted-foreground">
|
|
164
|
-
{svc.latencyMs}ms
|
|
165
|
-
</span>
|
|
149
|
+
<span className="text-xs font-mono text-muted-foreground">{svc.latencyMs}ms</span>
|
|
166
150
|
)}
|
|
167
151
|
<div className="flex items-center gap-1.5">
|
|
168
152
|
<StatusIcon status={svc.status} />
|
|
@@ -190,13 +174,7 @@ export function StatusPage() {
|
|
|
190
174
|
</div>
|
|
191
175
|
<div className="flex items-center gap-2">
|
|
192
176
|
{lastChecked && <span>checked {lastChecked.toLocaleTimeString()}</span>}
|
|
193
|
-
<Button
|
|
194
|
-
variant="ghost"
|
|
195
|
-
size="sm"
|
|
196
|
-
className="h-6 px-2"
|
|
197
|
-
onClick={load}
|
|
198
|
-
disabled={refreshing}
|
|
199
|
-
>
|
|
177
|
+
<Button variant="ghost" size="sm" className="h-6 px-2" onClick={load} disabled={refreshing}>
|
|
200
178
|
<RefreshCw className={cn("size-3", refreshing && "animate-spin")} />
|
|
201
179
|
</Button>
|
|
202
180
|
</div>
|
|
@@ -9,9 +9,7 @@ function AlertDialog({ ...props }: React.ComponentProps<typeof AlertDialogPrimit
|
|
|
9
9
|
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function AlertDialogTrigger({
|
|
13
|
-
...props
|
|
14
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
12
|
+
function AlertDialogTrigger({ ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
|
|
15
13
|
return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -19,15 +17,12 @@ function AlertDialogPortal({ ...props }: React.ComponentProps<typeof AlertDialog
|
|
|
19
17
|
return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
function AlertDialogOverlay({
|
|
23
|
-
className,
|
|
24
|
-
...props
|
|
25
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
20
|
+
function AlertDialogOverlay({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
|
|
26
21
|
return (
|
|
27
22
|
<AlertDialogPrimitive.Overlay
|
|
28
23
|
data-slot="alert-dialog-overlay"
|
|
29
24
|
className={cn(
|
|
30
|
-
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/
|
|
25
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80 backdrop-blur-sm",
|
|
31
26
|
className,
|
|
32
27
|
)}
|
|
33
28
|
{...props}
|
|
@@ -35,17 +30,14 @@ function AlertDialogOverlay({
|
|
|
35
30
|
);
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
function AlertDialogContent({
|
|
39
|
-
className,
|
|
40
|
-
...props
|
|
41
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
|
33
|
+
function AlertDialogContent({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
|
|
42
34
|
return (
|
|
43
35
|
<AlertDialogPortal>
|
|
44
36
|
<AlertDialogOverlay />
|
|
45
37
|
<AlertDialogPrimitive.Content
|
|
46
38
|
data-slot="alert-dialog-content"
|
|
47
39
|
className={cn(
|
|
48
|
-
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-
|
|
40
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-1/2 left-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 rounded-lg border p-6 shadow-lg duration-200",
|
|
49
41
|
className,
|
|
50
42
|
)}
|
|
51
43
|
{...props}
|
|
@@ -74,10 +66,7 @@ function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">)
|
|
|
74
66
|
);
|
|
75
67
|
}
|
|
76
68
|
|
|
77
|
-
function AlertDialogTitle({
|
|
78
|
-
className,
|
|
79
|
-
...props
|
|
80
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
69
|
+
function AlertDialogTitle({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
|
|
81
70
|
return (
|
|
82
71
|
<AlertDialogPrimitive.Title
|
|
83
72
|
data-slot="alert-dialog-title"
|
|
@@ -100,10 +89,7 @@ function AlertDialogDescription({
|
|
|
100
89
|
);
|
|
101
90
|
}
|
|
102
91
|
|
|
103
|
-
function AlertDialogAction({
|
|
104
|
-
className,
|
|
105
|
-
...props
|
|
106
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
92
|
+
function AlertDialogAction({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
|
|
107
93
|
return (
|
|
108
94
|
<AlertDialogPrimitive.Action
|
|
109
95
|
data-slot="alert-dialog-action"
|
|
@@ -113,10 +99,7 @@ function AlertDialogAction({
|
|
|
113
99
|
);
|
|
114
100
|
}
|
|
115
101
|
|
|
116
|
-
function AlertDialogCancel({
|
|
117
|
-
className,
|
|
118
|
-
...props
|
|
119
|
-
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
102
|
+
function AlertDialogCancel({ className, ...props }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
|
|
120
103
|
return (
|
|
121
104
|
<AlertDialogPrimitive.Cancel
|
|
122
105
|
data-slot="alert-dialog-cancel"
|
|
@@ -13,8 +13,7 @@ const badgeVariants = cva(
|
|
|
13
13
|
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
14
14
|
destructive:
|
|
15
15
|
"bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
16
|
-
outline:
|
|
17
|
-
"border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
16
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
18
17
|
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
19
18
|
link: "text-primary underline-offset-4 [a&]:hover:underline",
|
|
20
19
|
terminal: "border-terminal text-terminal bg-terminal/10 [a&]:hover:bg-terminal/20",
|
|
@@ -35,12 +34,7 @@ function Badge({
|
|
|
35
34
|
const Comp = asChild ? Slot.Root : "span";
|
|
36
35
|
|
|
37
36
|
return (
|
|
38
|
-
<Comp
|
|
39
|
-
data-slot="badge"
|
|
40
|
-
data-variant={variant}
|
|
41
|
-
className={cn(badgeVariants({ variant }), className)}
|
|
42
|
-
{...props}
|
|
43
|
-
/>
|
|
37
|
+
<Comp data-slot="badge" data-variant={variant} className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
44
38
|
);
|
|
45
39
|
}
|
|
46
40
|
|
|
@@ -24,12 +24,7 @@ function Banner({
|
|
|
24
24
|
...props
|
|
25
25
|
}: React.ComponentProps<"div"> & VariantProps<typeof bannerVariants>) {
|
|
26
26
|
return (
|
|
27
|
-
<div
|
|
28
|
-
data-slot="banner"
|
|
29
|
-
data-variant={variant}
|
|
30
|
-
className={cn(bannerVariants({ variant, className }))}
|
|
31
|
-
{...props}
|
|
32
|
-
/>
|
|
27
|
+
<div data-slot="banner" data-variant={variant} className={cn(bannerVariants({ variant, className }))} {...props} />
|
|
33
28
|
);
|
|
34
29
|
}
|
|
35
30
|
|