@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
|
@@ -8,14 +8,7 @@ import { toast } from "sonner";
|
|
|
8
8
|
import { Badge } from "@/components/ui/badge";
|
|
9
9
|
import { Button } from "@/components/ui/button";
|
|
10
10
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
11
|
-
import {
|
|
12
|
-
Table,
|
|
13
|
-
TableBody,
|
|
14
|
-
TableCell,
|
|
15
|
-
TableHead,
|
|
16
|
-
TableHeader,
|
|
17
|
-
TableRow,
|
|
18
|
-
} from "@/components/ui/table";
|
|
11
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
19
12
|
import {
|
|
20
13
|
blockAffiliateFingerprint,
|
|
21
14
|
type FingerprintCluster,
|
|
@@ -89,25 +82,14 @@ function SuppressionFeed() {
|
|
|
89
82
|
<Table>
|
|
90
83
|
<TableHeader>
|
|
91
84
|
<TableRow className="bg-secondary crt-scanlines">
|
|
92
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
93
|
-
|
|
94
|
-
</TableHead>
|
|
95
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
96
|
-
Referred
|
|
97
|
-
</TableHead>
|
|
98
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
99
|
-
Signals
|
|
100
|
-
</TableHead>
|
|
85
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Referrer</TableHead>
|
|
86
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Referred</TableHead>
|
|
87
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Signals</TableHead>
|
|
101
88
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Phase</TableHead>
|
|
102
89
|
<TableHead className="text-xs font-medium uppercase tracking-wider">When</TableHead>
|
|
103
90
|
</TableRow>
|
|
104
91
|
</TableHeader>
|
|
105
|
-
<TableBody
|
|
106
|
-
className={cn(
|
|
107
|
-
"transition-opacity duration-150",
|
|
108
|
-
loading && events.length > 0 && "opacity-60",
|
|
109
|
-
)}
|
|
110
|
-
>
|
|
92
|
+
<TableBody className={cn("transition-opacity duration-150", loading && events.length > 0 && "opacity-60")}>
|
|
111
93
|
{loading && events.length === 0 ? (
|
|
112
94
|
Array.from({ length: 5 }).map((_, i) => (
|
|
113
95
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows
|
|
@@ -142,9 +124,7 @@ function SuppressionFeed() {
|
|
|
142
124
|
events.map((e) => (
|
|
143
125
|
<TableRow key={e.id} className="h-10 hover:bg-secondary/50">
|
|
144
126
|
<TableCell className="font-mono text-sm cursor-pointer hover:text-terminal hover:underline">
|
|
145
|
-
<Link href={`/admin/tenants?search=${e.referrerTenantId}`}>
|
|
146
|
-
{e.referrerTenantId}
|
|
147
|
-
</Link>
|
|
127
|
+
<Link href={`/admin/tenants?search=${e.referrerTenantId}`}>{e.referrerTenantId}</Link>
|
|
148
128
|
</TableCell>
|
|
149
129
|
<TableCell className="font-mono text-sm">{e.referredTenantId}</TableCell>
|
|
150
130
|
<TableCell>
|
|
@@ -227,24 +207,13 @@ function VelocityPanel() {
|
|
|
227
207
|
<Table>
|
|
228
208
|
<TableHeader>
|
|
229
209
|
<TableRow className="bg-secondary crt-scanlines">
|
|
230
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
231
|
-
|
|
232
|
-
</TableHead>
|
|
233
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
234
|
-
30d Payouts
|
|
235
|
-
</TableHead>
|
|
236
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
237
|
-
30d Total
|
|
238
|
-
</TableHead>
|
|
210
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Referrer</TableHead>
|
|
211
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">30d Payouts</TableHead>
|
|
212
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">30d Total</TableHead>
|
|
239
213
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Status</TableHead>
|
|
240
214
|
</TableRow>
|
|
241
215
|
</TableHeader>
|
|
242
|
-
<TableBody
|
|
243
|
-
className={cn(
|
|
244
|
-
"transition-opacity duration-150",
|
|
245
|
-
loading && referrers.length > 0 && "opacity-60",
|
|
246
|
-
)}
|
|
247
|
-
>
|
|
216
|
+
<TableBody className={cn("transition-opacity duration-150", loading && referrers.length > 0 && "opacity-60")}>
|
|
248
217
|
{loading && referrers.length === 0 ? (
|
|
249
218
|
Array.from({ length: 3 }).map((_, i) => (
|
|
250
219
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows
|
|
@@ -279,22 +248,13 @@ function VelocityPanel() {
|
|
|
279
248
|
const atCap = payoutRatio >= 1 || creditRatio >= 1;
|
|
280
249
|
const nearCap = !atCap && (payoutRatio >= 0.8 || creditRatio >= 0.8);
|
|
281
250
|
|
|
282
|
-
const countColor = atCap
|
|
283
|
-
? "text-red-400"
|
|
284
|
-
: nearCap
|
|
285
|
-
? "text-amber-400"
|
|
286
|
-
: "text-terminal";
|
|
251
|
+
const countColor = atCap ? "text-red-400" : nearCap ? "text-amber-400" : "text-terminal";
|
|
287
252
|
const rowBg = atCap ? "bg-red-500/5" : nearCap ? "bg-amber-500/5" : "";
|
|
288
253
|
|
|
289
254
|
return (
|
|
290
|
-
<TableRow
|
|
291
|
-
key={r.referrerTenantId}
|
|
292
|
-
className={cn("h-10 hover:bg-secondary/50", rowBg)}
|
|
293
|
-
>
|
|
255
|
+
<TableRow key={r.referrerTenantId} className={cn("h-10 hover:bg-secondary/50", rowBg)}>
|
|
294
256
|
<TableCell className="font-mono text-sm cursor-pointer hover:text-terminal hover:underline">
|
|
295
|
-
<Link href={`/admin/tenants?search=${r.referrerTenantId}`}>
|
|
296
|
-
{r.referrerTenantId}
|
|
297
|
-
</Link>
|
|
257
|
+
<Link href={`/admin/tenants?search=${r.referrerTenantId}`}>{r.referrerTenantId}</Link>
|
|
298
258
|
</TableCell>
|
|
299
259
|
<TableCell className={cn("font-mono text-sm", countColor)}>
|
|
300
260
|
{r.payoutCount30d} / {CAP_REFERRALS}
|
|
@@ -304,9 +264,7 @@ function VelocityPanel() {
|
|
|
304
264
|
</TableCell>
|
|
305
265
|
<TableCell>
|
|
306
266
|
{atCap && (
|
|
307
|
-
<Badge className="bg-red-500/15 text-red-400 border border-red-500/20 text-xs">
|
|
308
|
-
AT CAP
|
|
309
|
-
</Badge>
|
|
267
|
+
<Badge className="bg-red-500/15 text-red-400 border border-red-500/20 text-xs">AT CAP</Badge>
|
|
310
268
|
)}
|
|
311
269
|
{nearCap && (
|
|
312
270
|
<Badge className="bg-amber-500/15 text-amber-400 border border-amber-500/20 text-xs">
|
|
@@ -314,9 +272,7 @@ function VelocityPanel() {
|
|
|
314
272
|
</Badge>
|
|
315
273
|
)}
|
|
316
274
|
{!atCap && !nearCap && (
|
|
317
|
-
<Badge className="bg-terminal/15 text-terminal border border-terminal/20 text-xs">
|
|
318
|
-
NORMAL
|
|
319
|
-
</Badge>
|
|
275
|
+
<Badge className="bg-terminal/15 text-terminal border border-terminal/20 text-xs">NORMAL</Badge>
|
|
320
276
|
)}
|
|
321
277
|
</TableCell>
|
|
322
278
|
</TableRow>
|
|
@@ -373,31 +329,18 @@ function FingerprintPanel() {
|
|
|
373
329
|
<div className="space-y-3">
|
|
374
330
|
<div>
|
|
375
331
|
<h2 className="text-lg font-semibold text-foreground">Same-Card Clusters</h2>
|
|
376
|
-
<p className="text-xs text-muted-foreground">
|
|
377
|
-
Accounts sharing the same Stripe card fingerprint
|
|
378
|
-
</p>
|
|
332
|
+
<p className="text-xs text-muted-foreground">Accounts sharing the same Stripe card fingerprint</p>
|
|
379
333
|
</div>
|
|
380
334
|
<div className="rounded-sm border border-terminal/10">
|
|
381
335
|
<Table>
|
|
382
336
|
<TableHeader>
|
|
383
337
|
<TableRow className="bg-secondary crt-scanlines">
|
|
384
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
385
|
-
|
|
386
|
-
</TableHead>
|
|
387
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
388
|
-
Accounts
|
|
389
|
-
</TableHead>
|
|
390
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
391
|
-
Actions
|
|
392
|
-
</TableHead>
|
|
338
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Fingerprint</TableHead>
|
|
339
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Accounts</TableHead>
|
|
340
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Actions</TableHead>
|
|
393
341
|
</TableRow>
|
|
394
342
|
</TableHeader>
|
|
395
|
-
<TableBody
|
|
396
|
-
className={cn(
|
|
397
|
-
"transition-opacity duration-150",
|
|
398
|
-
loading && clusters.length > 0 && "opacity-60",
|
|
399
|
-
)}
|
|
400
|
-
>
|
|
343
|
+
<TableBody className={cn("transition-opacity duration-150", loading && clusters.length > 0 && "opacity-60")}>
|
|
401
344
|
{loading && clusters.length === 0 ? (
|
|
402
345
|
Array.from({ length: 3 }).map((_, i) => (
|
|
403
346
|
// biome-ignore lint/suspicious/noArrayIndexKey: skeleton rows
|
|
@@ -417,18 +360,13 @@ function FingerprintPanel() {
|
|
|
417
360
|
> No same-card clusters detected
|
|
418
361
|
<span className="animate-ellipsis" />
|
|
419
362
|
</p>
|
|
420
|
-
<p className="text-xs text-muted-foreground mt-1">
|
|
421
|
-
All payment fingerprints map to unique accounts
|
|
422
|
-
</p>
|
|
363
|
+
<p className="text-xs text-muted-foreground mt-1">All payment fingerprints map to unique accounts</p>
|
|
423
364
|
</TableCell>
|
|
424
365
|
</TableRow>
|
|
425
366
|
) : (
|
|
426
367
|
clusters.map((c) => (
|
|
427
368
|
<TableRow key={c.stripeFingerprint} className="h-10 hover:bg-secondary/50">
|
|
428
|
-
<TableCell
|
|
429
|
-
className="font-mono text-sm text-muted-foreground"
|
|
430
|
-
title={c.stripeFingerprint}
|
|
431
|
-
>
|
|
369
|
+
<TableCell className="font-mono text-sm text-muted-foreground" title={c.stripeFingerprint}>
|
|
432
370
|
{c.stripeFingerprint.substring(0, 10)}...
|
|
433
371
|
</TableCell>
|
|
434
372
|
<TableCell>
|
|
@@ -465,15 +403,9 @@ function FingerprintPanel() {
|
|
|
465
403
|
disabled={blockingFingerprint === c.stripeFingerprint}
|
|
466
404
|
onClick={() => handleBlock(c.stripeFingerprint)}
|
|
467
405
|
>
|
|
468
|
-
{blockingFingerprint === c.stripeFingerprint
|
|
469
|
-
? "Blocking..."
|
|
470
|
-
: "Confirm Block?"}
|
|
406
|
+
{blockingFingerprint === c.stripeFingerprint ? "Blocking..." : "Confirm Block?"}
|
|
471
407
|
</Button>
|
|
472
|
-
<Button
|
|
473
|
-
variant="ghost"
|
|
474
|
-
size="sm"
|
|
475
|
-
onClick={() => setConfirmingFingerprint(null)}
|
|
476
|
-
>
|
|
408
|
+
<Button variant="ghost" size="sm" onClick={() => setConfirmingFingerprint(null)}>
|
|
477
409
|
Cancel
|
|
478
410
|
</Button>
|
|
479
411
|
</>
|
|
@@ -543,8 +475,7 @@ export function AffiliateDashboard() {
|
|
|
543
475
|
AFFILIATE OPS
|
|
544
476
|
</h1>
|
|
545
477
|
<p className="text-sm text-muted-foreground font-mono mt-1">
|
|
546
|
-
{summary.suppressions} suppressions | {summary.nearCap} referrers near cap |{
|
|
547
|
-
{summary.clusters} card clusters
|
|
478
|
+
{summary.suppressions} suppressions | {summary.nearCap} referrers near cap | {summary.clusters} card clusters
|
|
548
479
|
</p>
|
|
549
480
|
</div>
|
|
550
481
|
|
|
@@ -5,22 +5,9 @@ import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
|
5
5
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { Input } from "@/components/ui/input";
|
|
8
|
-
import {
|
|
9
|
-
Select,
|
|
10
|
-
SelectContent,
|
|
11
|
-
SelectItem,
|
|
12
|
-
SelectTrigger,
|
|
13
|
-
SelectValue,
|
|
14
|
-
} from "@/components/ui/select";
|
|
8
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
15
9
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
16
|
-
import {
|
|
17
|
-
Table,
|
|
18
|
-
TableBody,
|
|
19
|
-
TableCell,
|
|
20
|
-
TableHead,
|
|
21
|
-
TableHeader,
|
|
22
|
-
TableRow,
|
|
23
|
-
} from "@/components/ui/table";
|
|
10
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
24
11
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
25
12
|
import type { AuditLogResponse } from "@/lib/api";
|
|
26
13
|
import { fetchAuditLog } from "@/lib/api";
|
|
@@ -65,11 +52,9 @@ function relativeTime(iso: string): string {
|
|
|
65
52
|
function actionBadgeClasses(action: string): string {
|
|
66
53
|
if (action.startsWith("admin.suspend") || action.startsWith("admin.ban"))
|
|
67
54
|
return "bg-destructive/15 text-red-400 border border-destructive/20";
|
|
68
|
-
if (action.startsWith("admin.reactivate"))
|
|
69
|
-
return "bg-terminal/15 text-terminal border border-terminal/20";
|
|
55
|
+
if (action.startsWith("admin.reactivate")) return "bg-terminal/15 text-terminal border border-terminal/20";
|
|
70
56
|
if (action.startsWith("billing")) return "bg-chart-3/15 text-amber-400 border border-chart-3/20";
|
|
71
|
-
if (action.startsWith("security"))
|
|
72
|
-
return "bg-destructive/15 text-red-400 border border-destructive/20";
|
|
57
|
+
if (action.startsWith("security")) return "bg-destructive/15 text-red-400 border border-destructive/20";
|
|
73
58
|
return "bg-secondary text-muted-foreground border border-border";
|
|
74
59
|
}
|
|
75
60
|
|
|
@@ -96,9 +81,7 @@ export function AuditLogTable() {
|
|
|
96
81
|
setLoadError(false);
|
|
97
82
|
try {
|
|
98
83
|
const since =
|
|
99
|
-
dateRange === "all"
|
|
100
|
-
? undefined
|
|
101
|
-
: new Date(Date.now() - Number(dateRange) * 86400000).toISOString();
|
|
84
|
+
dateRange === "all" ? undefined : new Date(Date.now() - Number(dateRange) * 86400000).toISOString();
|
|
102
85
|
const result = await fetchAuditLog({
|
|
103
86
|
limit: PAGE_SIZE,
|
|
104
87
|
offset: newOffset,
|
|
@@ -171,9 +154,7 @@ export function AuditLogTable() {
|
|
|
171
154
|
<h1 className="text-xl font-bold uppercase tracking-wider text-terminal [text-shadow:0_0_10px_rgba(0,255,65,0.25)]">
|
|
172
155
|
AUDIT LOG
|
|
173
156
|
</h1>
|
|
174
|
-
<span className="text-sm text-muted-foreground font-mono">
|
|
175
|
-
{data ? `${data.total} events` : "Loading..."}
|
|
176
|
-
</span>
|
|
157
|
+
<span className="text-sm text-muted-foreground font-mono">{data ? `${data.total} events` : "Loading..."}</span>
|
|
177
158
|
</div>
|
|
178
159
|
|
|
179
160
|
{/* Filters */}
|
|
@@ -218,21 +199,13 @@ export function AuditLogTable() {
|
|
|
218
199
|
<Table>
|
|
219
200
|
<TableHeader>
|
|
220
201
|
<TableRow className="bg-secondary crt-scanlines">
|
|
221
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider w-[100px]">
|
|
222
|
-
Time
|
|
223
|
-
</TableHead>
|
|
202
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider w-[100px]">Time</TableHead>
|
|
224
203
|
<TableHead className="text-xs font-medium uppercase tracking-wider">Action</TableHead>
|
|
225
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
226
|
-
|
|
227
|
-
</TableHead>
|
|
228
|
-
<TableHead className="text-xs font-medium uppercase tracking-wider">
|
|
229
|
-
Details
|
|
230
|
-
</TableHead>
|
|
204
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Resource</TableHead>
|
|
205
|
+
<TableHead className="text-xs font-medium uppercase tracking-wider">Details</TableHead>
|
|
231
206
|
</TableRow>
|
|
232
207
|
</TableHeader>
|
|
233
|
-
<TableBody
|
|
234
|
-
className={cn("transition-opacity duration-150", loading && data && "opacity-60")}
|
|
235
|
-
>
|
|
208
|
+
<TableBody className={cn("transition-opacity duration-150", loading && data && "opacity-60")}>
|
|
236
209
|
{loading && !data ? (
|
|
237
210
|
["s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8"].map((rowKey, i) => (
|
|
238
211
|
<TableRow key={rowKey} className="h-10">
|
|
@@ -274,12 +247,8 @@ export function AuditLogTable() {
|
|
|
274
247
|
</span>
|
|
275
248
|
</TableCell>
|
|
276
249
|
<TableCell className="text-sm">
|
|
277
|
-
<span className="text-xs uppercase tracking-wide text-muted-foreground">
|
|
278
|
-
|
|
279
|
-
</span>{" "}
|
|
280
|
-
<span className="font-mono text-xs">
|
|
281
|
-
{event.resourceName ?? event.resourceId}
|
|
282
|
-
</span>
|
|
250
|
+
<span className="text-xs uppercase tracking-wide text-muted-foreground">{event.resourceType}</span>{" "}
|
|
251
|
+
<span className="font-mono text-xs">{event.resourceName ?? event.resourceId}</span>
|
|
283
252
|
</TableCell>
|
|
284
253
|
<TableCell className="text-xs text-muted-foreground max-w-[300px] truncate">
|
|
285
254
|
{event.details ?? "\u2014"}
|
|
@@ -309,12 +278,7 @@ export function AuditLogTable() {
|
|
|
309
278
|
>
|
|
310
279
|
Previous
|
|
311
280
|
</Button>
|
|
312
|
-
<Button
|
|
313
|
-
variant="ghost"
|
|
314
|
-
size="xs"
|
|
315
|
-
disabled={!data.hasMore}
|
|
316
|
-
onClick={() => load(offset + PAGE_SIZE)}
|
|
317
|
-
>
|
|
281
|
+
<Button variant="ghost" size="xs" disabled={!data.hasMore} onClick={() => load(offset + PAGE_SIZE)}>
|
|
318
282
|
Next
|
|
319
283
|
</Button>
|
|
320
284
|
<Button
|
|
@@ -79,9 +79,7 @@ function CheckRow({ ok, label, detail }: { ok: boolean; label: string; detail?:
|
|
|
79
79
|
return (
|
|
80
80
|
<div className="flex items-center justify-between py-2 border-b border-border/50 last:border-0">
|
|
81
81
|
<div className="flex items-center gap-2">
|
|
82
|
-
<div
|
|
83
|
-
className={cn("w-2 h-2 rounded-full flex-shrink-0", ok ? "bg-green-500" : "bg-red-500")}
|
|
84
|
-
/>
|
|
82
|
+
<div className={cn("w-2 h-2 rounded-full flex-shrink-0", ok ? "bg-green-500" : "bg-red-500")} />
|
|
85
83
|
<span className="text-sm">{label}</span>
|
|
86
84
|
</div>
|
|
87
85
|
{detail && <span className="text-xs text-muted-foreground">{detail}</span>}
|
|
@@ -131,9 +129,7 @@ export function BillingHealthDashboard() {
|
|
|
131
129
|
|
|
132
130
|
async function poll() {
|
|
133
131
|
try {
|
|
134
|
-
const result = (await trpcVanilla.admin.billingHealth.query(
|
|
135
|
-
undefined,
|
|
136
|
-
)) as BillingHealthData;
|
|
132
|
+
const result = (await trpcVanilla.admin.billingHealth.query(undefined)) as BillingHealthData;
|
|
137
133
|
if (active) {
|
|
138
134
|
setData(result);
|
|
139
135
|
setError(null);
|
|
@@ -184,9 +180,7 @@ export function BillingHealthDashboard() {
|
|
|
184
180
|
<div className="flex items-center justify-between">
|
|
185
181
|
<div className="flex items-center gap-3">
|
|
186
182
|
<StatusBadge status={data.overall} />
|
|
187
|
-
{data.severity &&
|
|
188
|
-
<span className="text-sm text-muted-foreground font-medium">{data.severity}</span>
|
|
189
|
-
)}
|
|
183
|
+
{data.severity && <span className="text-sm text-muted-foreground font-medium">{data.severity}</span>}
|
|
190
184
|
</div>
|
|
191
185
|
{lastUpdated && (
|
|
192
186
|
<span className="text-xs text-muted-foreground">
|
|
@@ -230,9 +224,7 @@ export function BillingHealthDashboard() {
|
|
|
230
224
|
label="Revenue (24h)"
|
|
231
225
|
value={formatCents(data.business.revenueToday)}
|
|
232
226
|
sub={
|
|
233
|
-
data.business.activeTenantCount !== null
|
|
234
|
-
? `${data.business.activeTenantCount} active tenants`
|
|
235
|
-
: undefined
|
|
227
|
+
data.business.activeTenantCount !== null ? `${data.business.activeTenantCount} active tenants` : undefined
|
|
236
228
|
}
|
|
237
229
|
/>
|
|
238
230
|
</div>
|
|
@@ -298,12 +290,7 @@ export function BillingHealthDashboard() {
|
|
|
298
290
|
</CardHeader>
|
|
299
291
|
<CardContent>
|
|
300
292
|
{data.alerts.map((alert) => (
|
|
301
|
-
<CheckRow
|
|
302
|
-
key={alert.name}
|
|
303
|
-
ok={!alert.firing}
|
|
304
|
-
label={alert.name}
|
|
305
|
-
detail={alert.message}
|
|
306
|
-
/>
|
|
293
|
+
<CheckRow key={alert.name} ok={!alert.firing} label={alert.name} detail={alert.message} />
|
|
307
294
|
))}
|
|
308
295
|
</CardContent>
|
|
309
296
|
</Card>
|
|
@@ -319,11 +306,7 @@ export function BillingHealthDashboard() {
|
|
|
319
306
|
<div>
|
|
320
307
|
<div className="text-xs text-muted-foreground mb-1">CPU</div>
|
|
321
308
|
<div className="text-lg font-medium">
|
|
322
|
-
{(data.system.cpuCount > 0
|
|
323
|
-
? (data.system.cpuLoad1m / data.system.cpuCount) * 100
|
|
324
|
-
: 0
|
|
325
|
-
).toFixed(0)}
|
|
326
|
-
%
|
|
309
|
+
{(data.system.cpuCount > 0 ? (data.system.cpuLoad1m / data.system.cpuCount) * 100 : 0).toFixed(0)}%
|
|
327
310
|
</div>
|
|
328
311
|
<div className="text-xs text-muted-foreground">
|
|
329
312
|
Load {data.system.cpuLoad1m.toFixed(2)} / {data.system.cpuCount} cores
|
|
@@ -339,8 +322,7 @@ export function BillingHealthDashboard() {
|
|
|
339
322
|
%
|
|
340
323
|
</div>
|
|
341
324
|
<div className="text-xs text-muted-foreground">
|
|
342
|
-
{formatBytes(data.system.memoryUsedBytes)} /{
|
|
343
|
-
{formatBytes(data.system.memoryTotalBytes)}
|
|
325
|
+
{formatBytes(data.system.memoryUsedBytes)} / {formatBytes(data.system.memoryTotalBytes)}
|
|
344
326
|
</div>
|
|
345
327
|
</div>
|
|
346
328
|
<div>
|
|
@@ -79,13 +79,7 @@ describe("BulkActionsBar", () => {
|
|
|
79
79
|
it("fires onReactivate when Reactivate clicked", async () => {
|
|
80
80
|
const user = userEvent.setup();
|
|
81
81
|
const onReactivate = vi.fn();
|
|
82
|
-
render(
|
|
83
|
-
<BulkActionsBar
|
|
84
|
-
{...defaultProps}
|
|
85
|
-
hasSuspendedInSelection={true}
|
|
86
|
-
onReactivate={onReactivate}
|
|
87
|
-
/>,
|
|
88
|
-
);
|
|
82
|
+
render(<BulkActionsBar {...defaultProps} hasSuspendedInSelection={true} onReactivate={onReactivate} />);
|
|
89
83
|
await user.click(screen.getByText("Reactivate"));
|
|
90
84
|
expect(onReactivate).toHaveBeenCalledOnce();
|
|
91
85
|
});
|
|
@@ -37,9 +37,7 @@ function BulkActionsBar({
|
|
|
37
37
|
>
|
|
38
38
|
<div className="flex items-center gap-2">
|
|
39
39
|
<span className="text-lg font-semibold text-terminal">{selectedCount} selected</span>
|
|
40
|
-
{allMatchingSelected && (
|
|
41
|
-
<span className="text-sm text-muted-foreground">(all matching filters)</span>
|
|
42
|
-
)}
|
|
40
|
+
{allMatchingSelected && <span className="text-sm text-muted-foreground">(all matching filters)</span>}
|
|
43
41
|
</div>
|
|
44
42
|
|
|
45
43
|
<div className="flex items-center gap-2">
|
|
@@ -47,13 +47,7 @@ describe("BulkExportDialog", () => {
|
|
|
47
47
|
it("disables Generate export when all fields unchecked", async () => {
|
|
48
48
|
const user = userEvent.setup();
|
|
49
49
|
render(<BulkExportDialog {...defaultProps} />);
|
|
50
|
-
for (const label of [
|
|
51
|
-
"Account info",
|
|
52
|
-
"Credit balance",
|
|
53
|
-
"Monthly products",
|
|
54
|
-
"Lifetime spend",
|
|
55
|
-
"Last seen",
|
|
56
|
-
]) {
|
|
50
|
+
for (const label of ["Account info", "Credit balance", "Monthly products", "Lifetime spend", "Last seen"]) {
|
|
57
51
|
await user.click(screen.getByLabelText(label));
|
|
58
52
|
}
|
|
59
53
|
expect(screen.getByText("Generate export").closest("button")).toBeDisabled();
|
|
@@ -14,13 +14,7 @@ import {
|
|
|
14
14
|
DialogTitle,
|
|
15
15
|
} from "@/components/ui/dialog";
|
|
16
16
|
import { Label } from "@/components/ui/label";
|
|
17
|
-
import {
|
|
18
|
-
Select,
|
|
19
|
-
SelectContent,
|
|
20
|
-
SelectItem,
|
|
21
|
-
SelectTrigger,
|
|
22
|
-
SelectValue,
|
|
23
|
-
} from "@/components/ui/select";
|
|
17
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
24
18
|
import { Separator } from "@/components/ui/separator";
|
|
25
19
|
|
|
26
20
|
type ExportFieldKey =
|
|
@@ -55,13 +49,7 @@ interface BulkExportDialogProps {
|
|
|
55
49
|
isLoading?: boolean;
|
|
56
50
|
}
|
|
57
51
|
|
|
58
|
-
function BulkExportDialog({
|
|
59
|
-
open,
|
|
60
|
-
onOpenChange,
|
|
61
|
-
selectedCount,
|
|
62
|
-
onConfirm,
|
|
63
|
-
isLoading,
|
|
64
|
-
}: BulkExportDialogProps) {
|
|
52
|
+
function BulkExportDialog({ open, onOpenChange, selectedCount, onConfirm, isLoading }: BulkExportDialogProps) {
|
|
65
53
|
const [enabledFields, setEnabledFields] = useState<Set<ExportFieldKey>>(
|
|
66
54
|
new Set(["account_info", "credit_balance", "monthly_products", "lifetime_spend", "last_seen"]),
|
|
67
55
|
);
|
|
@@ -85,15 +73,7 @@ function BulkExportDialog({
|
|
|
85
73
|
|
|
86
74
|
const handleOpenChange = (next: boolean) => {
|
|
87
75
|
if (!next) {
|
|
88
|
-
setEnabledFields(
|
|
89
|
-
new Set([
|
|
90
|
-
"account_info",
|
|
91
|
-
"credit_balance",
|
|
92
|
-
"monthly_products",
|
|
93
|
-
"lifetime_spend",
|
|
94
|
-
"last_seen",
|
|
95
|
-
]),
|
|
96
|
-
);
|
|
76
|
+
setEnabledFields(new Set(["account_info", "credit_balance", "monthly_products", "lifetime_spend", "last_seen"]));
|
|
97
77
|
}
|
|
98
78
|
onOpenChange(next);
|
|
99
79
|
};
|
|
@@ -125,9 +105,7 @@ function BulkExportDialog({
|
|
|
125
105
|
<Label htmlFor={`export-${field.key}`} className="cursor-pointer">
|
|
126
106
|
{field.label}
|
|
127
107
|
</Label>
|
|
128
|
-
{field.description &&
|
|
129
|
-
<span className="text-xs text-muted-foreground">{field.description}</span>
|
|
130
|
-
)}
|
|
108
|
+
{field.description && <span className="text-xs text-muted-foreground">{field.description}</span>}
|
|
131
109
|
</div>
|
|
132
110
|
</div>
|
|
133
111
|
))}
|
|
@@ -170,11 +148,7 @@ function BulkExportDialog({
|
|
|
170
148
|
<Button variant="outline" onClick={() => handleOpenChange(false)} disabled={isLoading}>
|
|
171
149
|
Cancel
|
|
172
150
|
</Button>
|
|
173
|
-
<Button
|
|
174
|
-
variant="terminal"
|
|
175
|
-
onClick={handleConfirm}
|
|
176
|
-
disabled={enabledFields.size === 0 || isLoading}
|
|
177
|
-
>
|
|
151
|
+
<Button variant="terminal" onClick={handleConfirm} disabled={enabledFields.size === 0 || isLoading}>
|
|
178
152
|
<Download className="size-4" />
|
|
179
153
|
Generate export
|
|
180
154
|
<ArrowRight className="size-4" />
|
|
@@ -185,5 +159,5 @@ function BulkExportDialog({
|
|
|
185
159
|
);
|
|
186
160
|
}
|
|
187
161
|
|
|
188
|
-
export { BulkExportDialog };
|
|
189
162
|
export type { ExportFieldKey };
|
|
163
|
+
export { BulkExportDialog };
|
|
@@ -21,9 +21,7 @@ describe("BulkGrantDialog", () => {
|
|
|
21
21
|
it("confirm button is disabled when amount is 0", () => {
|
|
22
22
|
render(<BulkGrantDialog {...defaultProps} />);
|
|
23
23
|
const buttons = screen.getAllByRole("button");
|
|
24
|
-
const confirmBtn = buttons.find(
|
|
25
|
-
(b) => b.textContent?.includes("Grant") && b.textContent?.includes("="),
|
|
26
|
-
);
|
|
24
|
+
const confirmBtn = buttons.find((b) => b.textContent?.includes("Grant") && b.textContent?.includes("="));
|
|
27
25
|
expect(confirmBtn).toBeDisabled();
|
|
28
26
|
});
|
|
29
27
|
|
|
@@ -32,9 +30,7 @@ describe("BulkGrantDialog", () => {
|
|
|
32
30
|
render(<BulkGrantDialog {...defaultProps} />);
|
|
33
31
|
await user.type(screen.getByLabelText("Amount per tenant"), "5");
|
|
34
32
|
const buttons = screen.getAllByRole("button");
|
|
35
|
-
const confirmBtn = buttons.find(
|
|
36
|
-
(b) => b.textContent?.includes("Grant") && b.textContent?.includes("="),
|
|
37
|
-
);
|
|
33
|
+
const confirmBtn = buttons.find((b) => b.textContent?.includes("Grant") && b.textContent?.includes("="));
|
|
38
34
|
expect(confirmBtn).toBeDisabled();
|
|
39
35
|
});
|
|
40
36
|
|
|
@@ -25,13 +25,7 @@ interface BulkGrantDialogProps {
|
|
|
25
25
|
isLoading?: boolean;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function BulkGrantDialog({
|
|
29
|
-
open,
|
|
30
|
-
onOpenChange,
|
|
31
|
-
selectedCount,
|
|
32
|
-
onConfirm,
|
|
33
|
-
isLoading,
|
|
34
|
-
}: BulkGrantDialogProps) {
|
|
28
|
+
function BulkGrantDialog({ open, onOpenChange, selectedCount, onConfirm, isLoading }: BulkGrantDialogProps) {
|
|
35
29
|
const [amount, setAmount] = useState("");
|
|
36
30
|
const [reason, setReason] = useState("");
|
|
37
31
|
const [notifyByEmail, setNotifyByEmail] = useState(false);
|
|
@@ -66,21 +60,16 @@ function BulkGrantDialog({
|
|
|
66
60
|
<DialogHeader>
|
|
67
61
|
<DialogTitle className="flex items-center gap-2">
|
|
68
62
|
<Gift className="size-5 text-terminal" />
|
|
69
|
-
Grant credits to <span className="text-terminal font-semibold">{selectedCount}</span>
|
|
70
|
-
tenants
|
|
63
|
+
Grant credits to <span className="text-terminal font-semibold">{selectedCount}</span> tenants
|
|
71
64
|
</DialogTitle>
|
|
72
|
-
<DialogDescription>
|
|
73
|
-
Grant a fixed amount to each selected tenant account.
|
|
74
|
-
</DialogDescription>
|
|
65
|
+
<DialogDescription>Grant a fixed amount to each selected tenant account.</DialogDescription>
|
|
75
66
|
</DialogHeader>
|
|
76
67
|
|
|
77
68
|
<div className="flex flex-col gap-4">
|
|
78
69
|
<div className="space-y-2">
|
|
79
70
|
<Label htmlFor="grant-amount">Amount per tenant</Label>
|
|
80
71
|
<div className="relative">
|
|
81
|
-
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">
|
|
82
|
-
$
|
|
83
|
-
</span>
|
|
72
|
+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">$</span>
|
|
84
73
|
<Input
|
|
85
74
|
id="grant-amount"
|
|
86
75
|
type="number"
|
|
@@ -9,14 +9,7 @@ import {
|
|
|
9
9
|
DialogHeader,
|
|
10
10
|
DialogTitle,
|
|
11
11
|
} from "@/components/ui/dialog";
|
|
12
|
-
import {
|
|
13
|
-
Table,
|
|
14
|
-
TableBody,
|
|
15
|
-
TableCell,
|
|
16
|
-
TableHead,
|
|
17
|
-
TableHeader,
|
|
18
|
-
TableRow,
|
|
19
|
-
} from "@/components/ui/table";
|
|
12
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
20
13
|
|
|
21
14
|
interface PreviewTenant {
|
|
22
15
|
tenantId: string;
|
|
@@ -51,9 +44,7 @@ function BulkPreviewDialog({
|
|
|
51
44
|
<DialogContent className="sm:max-w-2xl">
|
|
52
45
|
<DialogHeader>
|
|
53
46
|
<DialogTitle>Preview: {tenants.length} tenants will be affected</DialogTitle>
|
|
54
|
-
<DialogDescription>
|
|
55
|
-
Review the list of tenants before executing the operation.
|
|
56
|
-
</DialogDescription>
|
|
47
|
+
<DialogDescription>Review the list of tenants before executing the operation.</DialogDescription>
|
|
57
48
|
</DialogHeader>
|
|
58
49
|
|
|
59
50
|
<div className="max-h-80 overflow-y-auto rounded-sm border border-border">
|
|
@@ -102,5 +93,5 @@ function BulkPreviewDialog({
|
|
|
102
93
|
);
|
|
103
94
|
}
|
|
104
95
|
|
|
105
|
-
export { BulkPreviewDialog };
|
|
106
96
|
export type { PreviewTenant };
|
|
97
|
+
export { BulkPreviewDialog };
|
|
@@ -19,13 +19,7 @@ interface BulkReactivateDialogProps {
|
|
|
19
19
|
isLoading?: boolean;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function BulkReactivateDialog({
|
|
23
|
-
open,
|
|
24
|
-
onOpenChange,
|
|
25
|
-
selectedCount,
|
|
26
|
-
onConfirm,
|
|
27
|
-
isLoading,
|
|
28
|
-
}: BulkReactivateDialogProps) {
|
|
22
|
+
function BulkReactivateDialog({ open, onOpenChange, selectedCount, onConfirm, isLoading }: BulkReactivateDialogProps) {
|
|
29
23
|
return (
|
|
30
24
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
31
25
|
<DialogContent className="sm:max-w-sm">
|
|
@@ -10,12 +10,7 @@ interface BulkSelectAllBannerProps {
|
|
|
10
10
|
onSelectAllMatching: () => void;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function BulkSelectAllBanner({
|
|
14
|
-
visible,
|
|
15
|
-
pageCount,
|
|
16
|
-
totalMatching,
|
|
17
|
-
onSelectAllMatching,
|
|
18
|
-
}: BulkSelectAllBannerProps) {
|
|
13
|
+
function BulkSelectAllBanner({ visible, pageCount, totalMatching, onSelectAllMatching }: BulkSelectAllBannerProps) {
|
|
19
14
|
return (
|
|
20
15
|
<AnimatePresence>
|
|
21
16
|
{visible && (
|