@wopr-network/platform-ui-core 1.27.8 → 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 +4 -14
- 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
|
@@ -38,9 +38,7 @@ function newItem(sortOrder: number): NavItem {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export function NavEditor({ initial, onSave }: NavEditorProps) {
|
|
41
|
-
const [items, setItems] = useState<NavItem[]>(
|
|
42
|
-
[...initial].sort((a, b) => a.sortOrder - b.sortOrder),
|
|
43
|
-
);
|
|
41
|
+
const [items, setItems] = useState<NavItem[]>([...initial].sort((a, b) => a.sortOrder - b.sortOrder));
|
|
44
42
|
const [saving, setSaving] = useState(false);
|
|
45
43
|
|
|
46
44
|
function update(id: string, patch: Partial<NavItem>) {
|
|
@@ -84,14 +82,9 @@ export function NavEditor({ initial, onSave }: NavEditorProps) {
|
|
|
84
82
|
<CardTitle>Navigation Items</CardTitle>
|
|
85
83
|
</CardHeader>
|
|
86
84
|
<CardContent className="space-y-3">
|
|
87
|
-
{items.length === 0 &&
|
|
88
|
-
<p className="text-sm text-muted-foreground">No navigation items. Add one below.</p>
|
|
89
|
-
)}
|
|
85
|
+
{items.length === 0 && <p className="text-sm text-muted-foreground">No navigation items. Add one below.</p>}
|
|
90
86
|
{items.map((item, index) => (
|
|
91
|
-
<div
|
|
92
|
-
key={item.id}
|
|
93
|
-
className="flex items-start gap-3 rounded-md border border-border bg-muted/30 p-3"
|
|
94
|
-
>
|
|
87
|
+
<div key={item.id} className="flex items-start gap-3 rounded-md border border-border bg-muted/30 p-3">
|
|
95
88
|
<div className="flex flex-col gap-1 pt-1">
|
|
96
89
|
<Button
|
|
97
90
|
variant="ghost"
|
|
@@ -8,13 +8,7 @@ import { Checkbox } from "@/components/ui/checkbox";
|
|
|
8
8
|
import { Input } from "@/components/ui/input";
|
|
9
9
|
import { Label } from "@/components/ui/label";
|
|
10
10
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
11
|
-
import {
|
|
12
|
-
Select,
|
|
13
|
-
SelectContent,
|
|
14
|
-
SelectItem,
|
|
15
|
-
SelectTrigger,
|
|
16
|
-
SelectValue,
|
|
17
|
-
} from "@/components/ui/select";
|
|
11
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
18
12
|
import { Textarea } from "@/components/ui/textarea";
|
|
19
13
|
import { toUserMessage } from "@/lib/errors";
|
|
20
14
|
import type { Promotion, PromotionType, UserSegment, ValueType } from "@/lib/promotions-types";
|
|
@@ -51,21 +45,15 @@ export function PromotionForm({ initialData }: PromotionFormProps) {
|
|
|
51
45
|
const [endsAt, setEndsAt] = useState(initialData?.endsAt ?? "");
|
|
52
46
|
|
|
53
47
|
// Eligibility
|
|
54
|
-
const [firstPurchaseOnly, setFirstPurchaseOnly] = useState(
|
|
55
|
-
initialData?.firstPurchaseOnly ?? false,
|
|
56
|
-
);
|
|
48
|
+
const [firstPurchaseOnly, setFirstPurchaseOnly] = useState(initialData?.firstPurchaseOnly ?? false);
|
|
57
49
|
const [minPurchaseCents, setMinPurchaseCents] = useState(initialData?.minPurchaseCents ?? 0);
|
|
58
50
|
const [userSegment, setUserSegment] = useState<UserSegment>(initialData?.userSegment ?? "all");
|
|
59
51
|
|
|
60
52
|
// Limits
|
|
61
|
-
const [unlimited, setUnlimited] = useState(
|
|
62
|
-
initialData ? initialData.totalUseLimit === null : true,
|
|
63
|
-
);
|
|
53
|
+
const [unlimited, setUnlimited] = useState(initialData ? initialData.totalUseLimit === null : true);
|
|
64
54
|
const [totalUseLimit, setTotalUseLimit] = useState(initialData?.totalUseLimit ?? 1000);
|
|
65
55
|
const [perUserLimit, setPerUserLimit] = useState(initialData?.perUserLimit ?? 1);
|
|
66
|
-
const [noBudgetCap, setNoBudgetCap] = useState(
|
|
67
|
-
initialData ? initialData.budgetCap === null : true,
|
|
68
|
-
);
|
|
56
|
+
const [noBudgetCap, setNoBudgetCap] = useState(initialData ? initialData.budgetCap === null : true);
|
|
69
57
|
const [budgetCap, setBudgetCap] = useState(initialData?.budgetCap ?? 0);
|
|
70
58
|
|
|
71
59
|
// Coupon
|
|
@@ -178,11 +166,7 @@ export function PromotionForm({ initialData }: PromotionFormProps) {
|
|
|
178
166
|
<CardTitle className="text-sm">Value</CardTitle>
|
|
179
167
|
</CardHeader>
|
|
180
168
|
<CardContent className="space-y-4">
|
|
181
|
-
<RadioGroup
|
|
182
|
-
value={valueType}
|
|
183
|
-
onValueChange={(v) => setValueType(v as ValueType)}
|
|
184
|
-
className="flex gap-4"
|
|
185
|
-
>
|
|
169
|
+
<RadioGroup value={valueType} onValueChange={(v) => setValueType(v as ValueType)} className="flex gap-4">
|
|
186
170
|
<div className="flex items-center gap-2">
|
|
187
171
|
<RadioGroupItem value="flat_credits" id="vt-flat" />
|
|
188
172
|
<Label htmlFor="vt-flat" className="text-sm cursor-pointer">
|
|
@@ -249,22 +233,13 @@ export function PromotionForm({ initialData }: PromotionFormProps) {
|
|
|
249
233
|
</div>
|
|
250
234
|
)}
|
|
251
235
|
<div className="flex items-center gap-2">
|
|
252
|
-
<Checkbox
|
|
253
|
-
id="no-expiry"
|
|
254
|
-
checked={noExpiry}
|
|
255
|
-
onCheckedChange={(v) => setNoExpiry(v === true)}
|
|
256
|
-
/>
|
|
236
|
+
<Checkbox id="no-expiry" checked={noExpiry} onCheckedChange={(v) => setNoExpiry(v === true)} />
|
|
257
237
|
<Label htmlFor="no-expiry">No expiry</Label>
|
|
258
238
|
</div>
|
|
259
239
|
{!noExpiry && (
|
|
260
240
|
<div>
|
|
261
241
|
<Label htmlFor="promo-ends">End date</Label>
|
|
262
|
-
<Input
|
|
263
|
-
id="promo-ends"
|
|
264
|
-
type="datetime-local"
|
|
265
|
-
value={endsAt}
|
|
266
|
-
onChange={(e) => setEndsAt(e.target.value)}
|
|
267
|
-
/>
|
|
242
|
+
<Input id="promo-ends" type="datetime-local" value={endsAt} onChange={(e) => setEndsAt(e.target.value)} />
|
|
268
243
|
</div>
|
|
269
244
|
)}
|
|
270
245
|
</CardContent>
|
|
@@ -328,11 +303,7 @@ export function PromotionForm({ initialData }: PromotionFormProps) {
|
|
|
328
303
|
</CardHeader>
|
|
329
304
|
<CardContent className="space-y-4">
|
|
330
305
|
<div className="flex items-center gap-2">
|
|
331
|
-
<Checkbox
|
|
332
|
-
id="unlimited"
|
|
333
|
-
checked={unlimited}
|
|
334
|
-
onCheckedChange={(v) => setUnlimited(v === true)}
|
|
335
|
-
/>
|
|
306
|
+
<Checkbox id="unlimited" checked={unlimited} onCheckedChange={(v) => setUnlimited(v === true)} />
|
|
336
307
|
<Label htmlFor="unlimited">Unlimited uses</Label>
|
|
337
308
|
</div>
|
|
338
309
|
{!unlimited && (
|
|
@@ -358,11 +329,7 @@ export function PromotionForm({ initialData }: PromotionFormProps) {
|
|
|
358
329
|
/>
|
|
359
330
|
</div>
|
|
360
331
|
<div className="flex items-center gap-2">
|
|
361
|
-
<Checkbox
|
|
362
|
-
id="no-budget-cap"
|
|
363
|
-
checked={noBudgetCap}
|
|
364
|
-
onCheckedChange={(v) => setNoBudgetCap(v === true)}
|
|
365
|
-
/>
|
|
332
|
+
<Checkbox id="no-budget-cap" checked={noBudgetCap} onCheckedChange={(v) => setNoBudgetCap(v === true)} />
|
|
366
333
|
<Label htmlFor="no-budget-cap">No budget cap</Label>
|
|
367
334
|
</div>
|
|
368
335
|
{!noBudgetCap && (
|
|
@@ -6,22 +6,9 @@ import { toast } from "sonner";
|
|
|
6
6
|
import { Badge } from "@/components/ui/badge";
|
|
7
7
|
import { Button } from "@/components/ui/button";
|
|
8
8
|
import { Input } from "@/components/ui/input";
|
|
9
|
-
import {
|
|
10
|
-
Select,
|
|
11
|
-
SelectContent,
|
|
12
|
-
SelectItem,
|
|
13
|
-
SelectTrigger,
|
|
14
|
-
SelectValue,
|
|
15
|
-
} from "@/components/ui/select";
|
|
9
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
16
10
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
17
|
-
import {
|
|
18
|
-
Table,
|
|
19
|
-
TableBody,
|
|
20
|
-
TableCell,
|
|
21
|
-
TableHead,
|
|
22
|
-
TableHeader,
|
|
23
|
-
TableRow,
|
|
24
|
-
} from "@/components/ui/table";
|
|
11
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
25
12
|
import type { AdminRole, UserRoleAssignment } from "@/lib/admin-api";
|
|
26
13
|
import { assignRole, getRolesList, revokeRole } from "@/lib/admin-api";
|
|
27
14
|
import { toUserMessage } from "@/lib/errors";
|
|
@@ -86,10 +73,7 @@ function RoleRow({ assignment, onChanged }: RoleRowProps) {
|
|
|
86
73
|
<code className="text-xs text-muted-foreground">{assignment.tenant_id}</code>
|
|
87
74
|
</TableCell>
|
|
88
75
|
<TableCell>
|
|
89
|
-
<Badge
|
|
90
|
-
variant="secondary"
|
|
91
|
-
className={`${roleBadgeClass(assignment.role)} inline-flex items-center gap-1`}
|
|
92
|
-
>
|
|
76
|
+
<Badge variant="secondary" className={`${roleBadgeClass(assignment.role)} inline-flex items-center gap-1`}>
|
|
93
77
|
<RoleIcon role={assignment.role} />
|
|
94
78
|
{assignment.role}
|
|
95
79
|
</Badge>
|
|
@@ -134,9 +118,7 @@ function RoleCatalog({ roles }: { roles: AdminRole[] }) {
|
|
|
134
118
|
</Badge>
|
|
135
119
|
{role.is_system && <span className="text-xs text-muted-foreground">(system)</span>}
|
|
136
120
|
</div>
|
|
137
|
-
{role.description &&
|
|
138
|
-
<p className="text-xs text-muted-foreground">{role.description}</p>
|
|
139
|
-
)}
|
|
121
|
+
{role.description && <p className="text-xs text-muted-foreground">{role.description}</p>}
|
|
140
122
|
</div>
|
|
141
123
|
</div>
|
|
142
124
|
))}
|
|
@@ -173,9 +155,7 @@ export function RolesDashboard() {
|
|
|
173
155
|
|
|
174
156
|
function handleRoleChanged(userId: string, tenantId: string, newRole: string) {
|
|
175
157
|
setAssignments((prev) =>
|
|
176
|
-
prev.map((a) =>
|
|
177
|
-
a.user_id === userId && a.tenant_id === tenantId ? { ...a, role: newRole } : a,
|
|
178
|
-
),
|
|
158
|
+
prev.map((a) => (a.user_id === userId && a.tenant_id === tenantId ? { ...a, role: newRole } : a)),
|
|
179
159
|
);
|
|
180
160
|
}
|
|
181
161
|
|
|
@@ -247,20 +227,13 @@ export function RolesDashboard() {
|
|
|
247
227
|
<TableBody>
|
|
248
228
|
{filtered.length === 0 ? (
|
|
249
229
|
<TableRow>
|
|
250
|
-
<TableCell
|
|
251
|
-
colSpan={5}
|
|
252
|
-
className="text-center text-muted-foreground py-8 text-sm"
|
|
253
|
-
>
|
|
230
|
+
<TableCell colSpan={5} className="text-center text-muted-foreground py-8 text-sm">
|
|
254
231
|
No users found.
|
|
255
232
|
</TableCell>
|
|
256
233
|
</TableRow>
|
|
257
234
|
) : (
|
|
258
235
|
filtered.map((a) => (
|
|
259
|
-
<RoleRow
|
|
260
|
-
key={`${a.user_id}-${a.tenant_id}`}
|
|
261
|
-
assignment={a}
|
|
262
|
-
onChanged={handleRoleChanged}
|
|
263
|
-
/>
|
|
236
|
+
<RoleRow key={`${a.user_id}-${a.tenant_id}`} assignment={a} onChanged={handleRoleChanged} />
|
|
264
237
|
))
|
|
265
238
|
)}
|
|
266
239
|
</TableBody>
|
|
@@ -52,13 +52,12 @@ export function SuspendDialog({ open, onOpenChange, user, onComplete }: SuspendD
|
|
|
52
52
|
<DialogDescription>
|
|
53
53
|
{isSuspended ? (
|
|
54
54
|
<>
|
|
55
|
-
Reactivate <span className="font-mono text-terminal">{user.email}</span>? Their bots
|
|
56
|
-
will resume.
|
|
55
|
+
Reactivate <span className="font-mono text-terminal">{user.email}</span>? Their bots will resume.
|
|
57
56
|
</>
|
|
58
57
|
) : (
|
|
59
58
|
<>
|
|
60
|
-
Suspend <span className="font-mono text-terminal">{user.email}</span>? Their running
|
|
61
|
-
|
|
59
|
+
Suspend <span className="font-mono text-terminal">{user.email}</span>? Their running bots will be paused
|
|
60
|
+
immediately.
|
|
62
61
|
</>
|
|
63
62
|
)}
|
|
64
63
|
</DialogDescription>
|
|
@@ -83,13 +82,7 @@ export function SuspendDialog({ open, onOpenChange, user, onComplete }: SuspendD
|
|
|
83
82
|
disabled={(!isSuspended && !reason.trim()) || submitting}
|
|
84
83
|
onClick={handleConfirm}
|
|
85
84
|
>
|
|
86
|
-
{submitting
|
|
87
|
-
? isSuspended
|
|
88
|
-
? "Reactivating..."
|
|
89
|
-
: "Suspending..."
|
|
90
|
-
: isSuspended
|
|
91
|
-
? "Reactivate"
|
|
92
|
-
: "Suspend"}
|
|
85
|
+
{submitting ? (isSuspended ? "Reactivating..." : "Suspending...") : isSuspended ? "Reactivate" : "Suspend"}
|
|
93
86
|
</Button>
|
|
94
87
|
</DialogFooter>
|
|
95
88
|
</DialogContent>
|
|
@@ -120,9 +120,7 @@ export function TenantNotesPanel({ tenantId }: TenantNotesPanelProps) {
|
|
|
120
120
|
<div key={note.id} className="px-4 py-3 space-y-1">
|
|
121
121
|
<div className="flex items-center justify-between">
|
|
122
122
|
<span className="text-xs font-medium text-muted-foreground">{note.admin_user}</span>
|
|
123
|
-
<span className="text-xs text-muted-foreground">
|
|
124
|
-
{relativeTime(note.created_at)}
|
|
125
|
-
</span>
|
|
123
|
+
<span className="text-xs text-muted-foreground">{relativeTime(note.created_at)}</span>
|
|
126
124
|
</div>
|
|
127
125
|
<p className="text-sm whitespace-pre-wrap">{note.content}</p>
|
|
128
126
|
</div>
|
|
@@ -42,18 +42,12 @@ export function TenantRowActions({ user, onAction }: TenantRowActionsProps) {
|
|
|
42
42
|
</DropdownMenuItem>
|
|
43
43
|
<DropdownMenuSeparator />
|
|
44
44
|
{user.status === "active" ? (
|
|
45
|
-
<DropdownMenuItem
|
|
46
|
-
onClick={() => setSuspendOpen(true)}
|
|
47
|
-
className="text-amber-500 focus:text-amber-500"
|
|
48
|
-
>
|
|
45
|
+
<DropdownMenuItem onClick={() => setSuspendOpen(true)} className="text-amber-500 focus:text-amber-500">
|
|
49
46
|
<ShieldBan className="size-4" />
|
|
50
47
|
Suspend
|
|
51
48
|
</DropdownMenuItem>
|
|
52
49
|
) : user.status === "suspended" ? (
|
|
53
|
-
<DropdownMenuItem
|
|
54
|
-
onClick={() => setSuspendOpen(true)}
|
|
55
|
-
className="text-terminal focus:text-terminal"
|
|
56
|
-
>
|
|
50
|
+
<DropdownMenuItem onClick={() => setSuspendOpen(true)} className="text-terminal focus:text-terminal">
|
|
57
51
|
<ShieldCheck className="size-4" />
|
|
58
52
|
Reactivate
|
|
59
53
|
</DropdownMenuItem>
|
|
@@ -61,18 +55,8 @@ export function TenantRowActions({ user, onAction }: TenantRowActionsProps) {
|
|
|
61
55
|
</DropdownMenuContent>
|
|
62
56
|
</DropdownMenu>
|
|
63
57
|
|
|
64
|
-
<SuspendDialog
|
|
65
|
-
|
|
66
|
-
onOpenChange={setSuspendOpen}
|
|
67
|
-
user={user}
|
|
68
|
-
onComplete={onAction}
|
|
69
|
-
/>
|
|
70
|
-
<GrantCreditsDialog
|
|
71
|
-
open={grantOpen}
|
|
72
|
-
onOpenChange={setGrantOpen}
|
|
73
|
-
user={user}
|
|
74
|
-
onComplete={onAction}
|
|
75
|
-
/>
|
|
58
|
+
<SuspendDialog open={suspendOpen} onOpenChange={setSuspendOpen} user={user} onComplete={onAction} />
|
|
59
|
+
<GrantCreditsDialog open={grantOpen} onOpenChange={setGrantOpen} user={user} onComplete={onAction} />
|
|
76
60
|
</>
|
|
77
61
|
);
|
|
78
62
|
}
|
|
@@ -7,14 +7,7 @@ import { Button } from "@/components/ui/button";
|
|
|
7
7
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
8
8
|
import { Input } from "@/components/ui/input";
|
|
9
9
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
10
|
-
import {
|
|
11
|
-
Table,
|
|
12
|
-
TableBody,
|
|
13
|
-
TableCell,
|
|
14
|
-
TableHead,
|
|
15
|
-
TableHeader,
|
|
16
|
-
TableRow,
|
|
17
|
-
} from "@/components/ui/table";
|
|
10
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
18
11
|
import {
|
|
19
12
|
type AdminUserSummary,
|
|
20
13
|
bulkGrantCredits,
|
|
@@ -119,9 +112,7 @@ export function TenantTable() {
|
|
|
119
112
|
setSelected(new Set());
|
|
120
113
|
reload();
|
|
121
114
|
} catch (err) {
|
|
122
|
-
toast.error(
|
|
123
|
-
`Failed to suspend tenants: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
124
|
-
);
|
|
115
|
+
toast.error(`Failed to suspend tenants: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
125
116
|
}
|
|
126
117
|
}
|
|
127
118
|
|
|
@@ -131,9 +122,7 @@ export function TenantTable() {
|
|
|
131
122
|
setSelected(new Set());
|
|
132
123
|
reload();
|
|
133
124
|
} catch (err) {
|
|
134
|
-
toast.error(
|
|
135
|
-
`Failed to reactivate tenants: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
136
|
-
);
|
|
125
|
+
toast.error(`Failed to reactivate tenants: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
137
126
|
}
|
|
138
127
|
}
|
|
139
128
|
|
|
@@ -149,9 +138,7 @@ export function TenantTable() {
|
|
|
149
138
|
setSelected(new Set());
|
|
150
139
|
reload();
|
|
151
140
|
} catch (err) {
|
|
152
|
-
toast.error(
|
|
153
|
-
`Failed to grant credits: ${err instanceof Error ? err.message : "Unknown error"}`,
|
|
154
|
-
);
|
|
141
|
+
toast.error(`Failed to grant credits: ${err instanceof Error ? err.message : "Unknown error"}`);
|
|
155
142
|
}
|
|
156
143
|
}
|
|
157
144
|
|
|
@@ -193,30 +180,18 @@ export function TenantTable() {
|
|
|
193
180
|
<TableHeader>
|
|
194
181
|
<TableRow className="bg-secondary crt-scanlines">
|
|
195
182
|
<TableHead className="w-10">
|
|
196
|
-
<Checkbox
|
|
197
|
-
checked={total > 0 && selected.size === total}
|
|
198
|
-
onCheckedChange={toggleAll}
|
|
199
|
-
/>
|
|
183
|
+
<Checkbox checked={total > 0 && selected.size === total} onCheckedChange={toggleAll} />
|
|
200
184
|
</TableHead>
|
|
201
185
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Email</TableHead>
|
|
202
186
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Name</TableHead>
|
|
203
187
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Status</TableHead>
|
|
204
188
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Plan</TableHead>
|
|
205
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider text-right">
|
|
206
|
-
|
|
207
|
-
</TableHead>
|
|
208
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider text-right">
|
|
209
|
-
Agents
|
|
210
|
-
</TableHead>
|
|
189
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider text-right">Credits</TableHead>
|
|
190
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider text-right">Agents</TableHead>
|
|
211
191
|
<TableHead className="w-10" />
|
|
212
192
|
</TableRow>
|
|
213
193
|
</TableHeader>
|
|
214
|
-
<TableBody
|
|
215
|
-
className={cn(
|
|
216
|
-
"transition-opacity duration-150",
|
|
217
|
-
loading && users.length > 0 && "opacity-60",
|
|
218
|
-
)}
|
|
219
|
-
>
|
|
194
|
+
<TableBody className={cn("transition-opacity duration-150", loading && users.length > 0 && "opacity-60")}>
|
|
220
195
|
{loading && users.length === 0 ? (
|
|
221
196
|
Array.from({ length: 8 }).map((_, i) => (
|
|
222
197
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows have no stable ID
|
|
@@ -242,19 +217,14 @@ export function TenantTable() {
|
|
|
242
217
|
> No users found
|
|
243
218
|
<span className="animate-ellipsis" />
|
|
244
219
|
</p>
|
|
245
|
-
<p className="text-xs text-muted-foreground mt-1">
|
|
246
|
-
Try adjusting your search query
|
|
247
|
-
</p>
|
|
220
|
+
<p className="text-xs text-muted-foreground mt-1">Try adjusting your search query</p>
|
|
248
221
|
</TableCell>
|
|
249
222
|
</TableRow>
|
|
250
223
|
) : (
|
|
251
224
|
users.map((user) => (
|
|
252
225
|
<TableRow
|
|
253
226
|
key={user.id}
|
|
254
|
-
className={cn(
|
|
255
|
-
"h-10 hover:bg-secondary/50",
|
|
256
|
-
selected.has(user.tenant_id) && "bg-terminal/5",
|
|
257
|
-
)}
|
|
227
|
+
className={cn("h-10 hover:bg-secondary/50", selected.has(user.tenant_id) && "bg-terminal/5")}
|
|
258
228
|
>
|
|
259
229
|
<TableCell>
|
|
260
230
|
<Checkbox
|
|
@@ -275,17 +245,10 @@ export function TenantTable() {
|
|
|
275
245
|
</span>
|
|
276
246
|
</TableCell>
|
|
277
247
|
<TableCell className="text-xs text-muted-foreground">{user.role}</TableCell>
|
|
278
|
-
<TableCell
|
|
279
|
-
className={cn(
|
|
280
|
-
"text-right font-mono text-xs",
|
|
281
|
-
creditColor(user.credit_balance_cents),
|
|
282
|
-
)}
|
|
283
|
-
>
|
|
248
|
+
<TableCell className={cn("text-right font-mono text-xs", creditColor(user.credit_balance_cents))}>
|
|
284
249
|
{formatCreditStandard(user.credit_balance_cents / 100)}
|
|
285
250
|
</TableCell>
|
|
286
|
-
<TableCell className="text-right text-xs text-muted-foreground">
|
|
287
|
-
{user.agent_count}
|
|
288
|
-
</TableCell>
|
|
251
|
+
<TableCell className="text-right text-xs text-muted-foreground">{user.agent_count}</TableCell>
|
|
289
252
|
<TableCell>
|
|
290
253
|
<TenantRowActions user={user} onAction={reload} />
|
|
291
254
|
</TableCell>
|
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useRouter } from "next/navigation";
|
|
3
|
+
import { useRouter, useSearchParams } from "next/navigation";
|
|
4
4
|
import { useEffect } from "react";
|
|
5
5
|
import { useSession } from "@/lib/auth-client";
|
|
6
6
|
import { getBrandConfig } from "@/lib/brand-config";
|
|
7
|
+
import { sanitizeRedirectUrl } from "@/lib/utils";
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Redirects authenticated users away from auth pages.
|
|
11
|
+
* Checks callbackUrl param first, falls back to brand homePath.
|
|
12
|
+
*/
|
|
8
13
|
export function AuthRedirect() {
|
|
9
14
|
const { data: session, isPending } = useSession();
|
|
10
15
|
const router = useRouter();
|
|
16
|
+
const searchParams = useSearchParams();
|
|
11
17
|
|
|
12
18
|
useEffect(() => {
|
|
13
19
|
if (!isPending && session) {
|
|
14
|
-
|
|
20
|
+
const callback = searchParams.get("callbackUrl");
|
|
21
|
+
const homePath = getBrandConfig().homePath ?? "/dashboard";
|
|
22
|
+
router.replace(callback ? sanitizeRedirectUrl(callback) : homePath);
|
|
15
23
|
}
|
|
16
|
-
}, [isPending, session, router]);
|
|
24
|
+
}, [isPending, session, router, searchParams]);
|
|
17
25
|
|
|
18
26
|
return null;
|
|
19
27
|
}
|
|
@@ -53,9 +53,7 @@ export function EmailVerificationResultBanner() {
|
|
|
53
53
|
return (
|
|
54
54
|
<Banner variant="destructive" role="alert">
|
|
55
55
|
<XCircle className="size-4 shrink-0" />
|
|
56
|
-
<span className="flex-1">
|
|
57
|
-
Email verification failed. Please try again or contact support.
|
|
58
|
-
</span>
|
|
56
|
+
<span className="flex-1">Email verification failed. Please try again or contact support.</span>
|
|
59
57
|
<Button
|
|
60
58
|
variant="ghost"
|
|
61
59
|
size="xs"
|
|
@@ -11,11 +11,7 @@ interface ResendVerificationButtonProps {
|
|
|
11
11
|
className?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function ResendVerificationButton({
|
|
15
|
-
email,
|
|
16
|
-
variant = "terminal",
|
|
17
|
-
className,
|
|
18
|
-
}: ResendVerificationButtonProps) {
|
|
14
|
+
export function ResendVerificationButton({ email, variant = "terminal", className }: ResendVerificationButtonProps) {
|
|
19
15
|
const [cooldown, setCooldown] = useState(0);
|
|
20
16
|
const [sending, setSending] = useState(false);
|
|
21
17
|
const [sent, setSent] = useState(false);
|
|
@@ -61,11 +57,7 @@ export function ResendVerificationButton({
|
|
|
61
57
|
}
|
|
62
58
|
}, [email, cooldown, sending]);
|
|
63
59
|
|
|
64
|
-
const buttonText = sending
|
|
65
|
-
? "Sending..."
|
|
66
|
-
: cooldown > 0
|
|
67
|
-
? `Resend in ${cooldown}s`
|
|
68
|
-
: "Resend verification email";
|
|
60
|
+
const buttonText = sending ? "Sending..." : cooldown > 0 ? `Resend in ${cooldown}s` : "Resend verification email";
|
|
69
61
|
|
|
70
62
|
return (
|
|
71
63
|
<div className="flex flex-col gap-1">
|
|
@@ -6,9 +6,7 @@ export function BrandWordmark() {
|
|
|
6
6
|
const tagline = getBrandConfig().tagline;
|
|
7
7
|
return (
|
|
8
8
|
<div className="mb-6 flex flex-col items-center gap-1">
|
|
9
|
-
<span className="text-2xl font-bold uppercase tracking-[0.3em] text-terminal">
|
|
10
|
-
{brandName()}
|
|
11
|
-
</span>
|
|
9
|
+
<span className="text-2xl font-bold uppercase tracking-[0.3em] text-terminal">{brandName()}</span>
|
|
12
10
|
<span className="text-xs uppercase tracking-[0.2em] text-muted-foreground">{tagline}</span>
|
|
13
11
|
</div>
|
|
14
12
|
);
|
|
@@ -74,8 +74,7 @@ export function AddPaymentMethodDialog({
|
|
|
74
74
|
<DialogHeader>
|
|
75
75
|
<DialogTitle>{orgId ? "Add org payment method" : "Add payment method"}</DialogTitle>
|
|
76
76
|
<DialogDescription>
|
|
77
|
-
Your card details are handled securely by Stripe. We never see or store your card
|
|
78
|
-
number.
|
|
77
|
+
Your card details are handled securely by Stripe. We never see or store your card number.
|
|
79
78
|
</DialogDescription>
|
|
80
79
|
</DialogHeader>
|
|
81
80
|
|
|
@@ -30,10 +30,7 @@ export function AffiliateDashboard() {
|
|
|
30
30
|
setLoading(true);
|
|
31
31
|
setError(null);
|
|
32
32
|
try {
|
|
33
|
-
const [statsData, referralsData] = await Promise.all([
|
|
34
|
-
getAffiliateStats(),
|
|
35
|
-
getAffiliateReferrals(),
|
|
36
|
-
]);
|
|
33
|
+
const [statsData, referralsData] = await Promise.all([getAffiliateStats(), getAffiliateReferrals()]);
|
|
37
34
|
setStats(statsData);
|
|
38
35
|
setReferrals(referralsData.referrals);
|
|
39
36
|
setTotal(referralsData.total);
|
|
@@ -140,9 +137,7 @@ export function AffiliateDashboard() {
|
|
|
140
137
|
<div className="max-w-3xl space-y-6">
|
|
141
138
|
<div>
|
|
142
139
|
<h1 className="text-2xl font-bold tracking-tight">Refer & Earn</h1>
|
|
143
|
-
<p className="text-sm text-muted-foreground">
|
|
144
|
-
Share your link and earn credits when friends join
|
|
145
|
-
</p>
|
|
140
|
+
<p className="text-sm text-muted-foreground">Share your link and earn credits when friends join</p>
|
|
146
141
|
</div>
|
|
147
142
|
|
|
148
143
|
{/* Referral link card */}
|
|
@@ -205,9 +200,7 @@ export function AffiliateDashboard() {
|
|
|
205
200
|
</Card>
|
|
206
201
|
<Card>
|
|
207
202
|
<CardContent className="pt-6 text-center">
|
|
208
|
-
<div className="text-3xl font-bold text-emerald-500">
|
|
209
|
-
{formatCents(stats.totalEarnedCents)}
|
|
210
|
-
</div>
|
|
203
|
+
<div className="text-3xl font-bold text-emerald-500">{formatCents(stats.totalEarnedCents)}</div>
|
|
211
204
|
<p className="text-xs text-muted-foreground mt-1">earned</p>
|
|
212
205
|
</CardContent>
|
|
213
206
|
</Card>
|
|
@@ -280,12 +273,7 @@ export function AffiliateDashboard() {
|
|
|
280
273
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
281
274
|
{offset < total && (
|
|
282
275
|
<div className="pt-2">
|
|
283
|
-
<Button
|
|
284
|
-
variant="outline"
|
|
285
|
-
size="sm"
|
|
286
|
-
onClick={handleLoadMore}
|
|
287
|
-
disabled={loadingMore}
|
|
288
|
-
>
|
|
276
|
+
<Button variant="outline" size="sm" onClick={handleLoadMore} disabled={loadingMore}>
|
|
289
277
|
{loadingMore ? "Loading more..." : "Load more"}
|
|
290
278
|
</Button>
|
|
291
279
|
</div>
|
|
@@ -31,9 +31,7 @@ export function AmountSelector({ onSelect }: AmountSelectorProps) {
|
|
|
31
31
|
}}
|
|
32
32
|
className={cn(
|
|
33
33
|
"rounded-md border p-3 text-lg font-bold transition-colors hover:bg-accent",
|
|
34
|
-
selected === amt && !custom
|
|
35
|
-
? "border-primary bg-primary/5 ring-1 ring-primary"
|
|
36
|
-
: "border-border",
|
|
34
|
+
selected === amt && !custom ? "border-primary bg-primary/5 ring-1 ring-primary" : "border-border",
|
|
37
35
|
)}
|
|
38
36
|
>
|
|
39
37
|
${amt}
|
|
@@ -6,13 +6,7 @@ import { useCallback, useEffect, useState } from "react";
|
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
8
8
|
import { Label } from "@/components/ui/label";
|
|
9
|
-
import {
|
|
10
|
-
Select,
|
|
11
|
-
SelectContent,
|
|
12
|
-
SelectItem,
|
|
13
|
-
SelectTrigger,
|
|
14
|
-
SelectValue,
|
|
15
|
-
} from "@/components/ui/select";
|
|
9
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
16
10
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
17
11
|
import { Switch } from "@/components/ui/switch";
|
|
18
12
|
import type { AutoTopupInterval, AutoTopupSettings } from "@/lib/api";
|
|
@@ -73,10 +67,7 @@ export function AutoTopupCard() {
|
|
|
73
67
|
load();
|
|
74
68
|
}, [load]);
|
|
75
69
|
|
|
76
|
-
async function save(
|
|
77
|
-
update: Parameters<typeof updateAutoTopupSettings>[0],
|
|
78
|
-
prevSettings: AutoTopupSettings,
|
|
79
|
-
) {
|
|
70
|
+
async function save(update: Parameters<typeof updateAutoTopupSettings>[0], prevSettings: AutoTopupSettings) {
|
|
80
71
|
setSaving(true);
|
|
81
72
|
setError(null);
|
|
82
73
|
try {
|
|
@@ -134,9 +134,7 @@ export function BuyCreditsPanel() {
|
|
|
134
134
|
</p>
|
|
135
135
|
</CardHeader>
|
|
136
136
|
<CardContent>
|
|
137
|
-
<p className="text-sm text-muted-foreground">
|
|
138
|
-
Credit purchases are not available at this time.
|
|
139
|
-
</p>
|
|
137
|
+
<p className="text-sm text-muted-foreground">Credit purchases are not available at this time.</p>
|
|
140
138
|
</CardContent>
|
|
141
139
|
</Card>
|
|
142
140
|
);
|
|
@@ -146,9 +144,7 @@ export function BuyCreditsPanel() {
|
|
|
146
144
|
<Card>
|
|
147
145
|
<CardHeader>
|
|
148
146
|
<CardTitle>Buy Credits</CardTitle>
|
|
149
|
-
<p className="text-xs text-muted-foreground">
|
|
150
|
-
Every purchase resets your 7-day dividend window
|
|
151
|
-
</p>
|
|
147
|
+
<p className="text-xs text-muted-foreground">Every purchase resets your 7-day dividend window</p>
|
|
152
148
|
</CardHeader>
|
|
153
149
|
<CardContent className="space-y-4">
|
|
154
150
|
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3 md:grid-cols-5">
|
|
@@ -169,19 +165,13 @@ export function BuyCreditsPanel() {
|
|
|
169
165
|
>
|
|
170
166
|
<span className="text-lg font-bold">{tier.label}</span>
|
|
171
167
|
{tier.bonusPercent > 0 && (
|
|
172
|
-
<Badge className="bg-primary/15 text-primary border-primary/25 text-xs">
|
|
173
|
-
+{tier.bonusPercent}%
|
|
174
|
-
</Badge>
|
|
168
|
+
<Badge className="bg-primary/15 text-primary border-primary/25 text-xs">+{tier.bonusPercent}%</Badge>
|
|
175
169
|
)}
|
|
176
170
|
</motion.button>
|
|
177
171
|
))}
|
|
178
172
|
</div>
|
|
179
173
|
{error && <p className="text-sm text-destructive">{error}</p>}
|
|
180
|
-
<Button
|
|
181
|
-
onClick={handleCheckout}
|
|
182
|
-
disabled={selected === null || loading}
|
|
183
|
-
className="w-full sm:w-auto"
|
|
184
|
-
>
|
|
174
|
+
<Button onClick={handleCheckout} disabled={selected === null || loading} className="w-full sm:w-auto">
|
|
185
175
|
{loading ? "Redirecting..." : "Buy credits"}
|
|
186
176
|
</Button>
|
|
187
177
|
</CardContent>
|