@wopr-network/platform-ui-core 1.27.7 → 1.27.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) hide show
  1. package/next.config.ts +1 -2
  2. package/package.json +17 -17
  3. package/src/__tests__/account-switcher.test.tsx +21 -20
  4. package/src/__tests__/activity-page.test.tsx +2 -6
  5. package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
  6. package/src/__tests__/admin-api.test.ts +1 -6
  7. package/src/__tests__/admin-gpu-api.test.ts +1 -3
  8. package/src/__tests__/admin-marketplace-api.test.ts +1 -4
  9. package/src/__tests__/admin-middleware.test.ts +76 -83
  10. package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
  11. package/src/__tests__/api-401-redirect.test.ts +46 -9
  12. package/src/__tests__/api-client.test.ts +3 -5
  13. package/src/__tests__/api-config.test.ts +22 -42
  14. package/src/__tests__/api-fleet-resources.test.ts +1 -2
  15. package/src/__tests__/api-fleet-trpc.test.ts +2 -8
  16. package/src/__tests__/api-null-guards.test.ts +3 -1
  17. package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
  18. package/src/__tests__/auth-password-reset.test.tsx +7 -21
  19. package/src/__tests__/auth-redirect.test.tsx +8 -2
  20. package/src/__tests__/auth.test.tsx +25 -23
  21. package/src/__tests__/auto-topup-card.test.tsx +4 -12
  22. package/src/__tests__/backups-tab.test.tsx +3 -4
  23. package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
  24. package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
  25. package/src/__tests__/billing.test.tsx +8 -39
  26. package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
  27. package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
  28. package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
  29. package/src/__tests__/bot-settings-restart.test.tsx +1 -3
  30. package/src/__tests__/bot-settings.test.tsx +2 -6
  31. package/src/__tests__/brand.test.ts +6 -26
  32. package/src/__tests__/buy-credits-panel.test.tsx +1 -3
  33. package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
  34. package/src/__tests__/capability-conflicts.test.ts +2 -8
  35. package/src/__tests__/capability-resolver.test.tsx +2 -12
  36. package/src/__tests__/channel-wizard.test.tsx +4 -17
  37. package/src/__tests__/chat/chat-panel.test.tsx +1 -4
  38. package/src/__tests__/chat-store.test.ts +5 -15
  39. package/src/__tests__/command-center.test.tsx +10 -12
  40. package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
  41. package/src/__tests__/confirmation-tracker.test.tsx +3 -18
  42. package/src/__tests__/coupon-input.test.tsx +1 -3
  43. package/src/__tests__/create-instance.test.tsx +1 -3
  44. package/src/__tests__/credit-balance.test.tsx +4 -12
  45. package/src/__tests__/credits.test.tsx +32 -85
  46. package/src/__tests__/email-verification-banner.test.tsx +2 -6
  47. package/src/__tests__/error-boundaries.test.tsx +0 -1
  48. package/src/__tests__/fetch-pricing.test.ts +2 -1
  49. package/src/__tests__/field-oauth.test.tsx +2 -6
  50. package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
  51. package/src/__tests__/fixtures/mock-manifests.ts +2 -4
  52. package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
  53. package/src/__tests__/fleet-health-update.test.tsx +1 -8
  54. package/src/__tests__/gpu-dashboard.test.tsx +2 -6
  55. package/src/__tests__/instance-detail.test.tsx +3 -9
  56. package/src/__tests__/instance-list.test.tsx +1 -5
  57. package/src/__tests__/layout-snapshots.test.tsx +64 -11
  58. package/src/__tests__/marketplace-admin.test.tsx +2 -6
  59. package/src/__tests__/marketplace.test.tsx +11 -35
  60. package/src/__tests__/merge-api-rates.test.ts +1 -6
  61. package/src/__tests__/middleware.test.ts +32 -219
  62. package/src/__tests__/next-config-headers.test.ts +1 -3
  63. package/src/__tests__/notifications.test.tsx +4 -11
  64. package/src/__tests__/oauth-buttons.test.tsx +36 -59
  65. package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
  66. package/src/__tests__/observability.test.tsx +23 -36
  67. package/src/__tests__/onboarding-page.test.tsx +4 -6
  68. package/src/__tests__/org-billing-api.test.tsx +1 -6
  69. package/src/__tests__/plugin-install-flow.test.tsx +28 -58
  70. package/src/__tests__/plugin-registry.test.tsx +3 -11
  71. package/src/__tests__/plugin-tool-sync.test.ts +1 -3
  72. package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
  73. package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
  74. package/src/__tests__/portfolio-chart.test.tsx +2 -6
  75. package/src/__tests__/promotion-form.test.tsx +2 -6
  76. package/src/__tests__/promotions-list.test.tsx +1 -3
  77. package/src/__tests__/provider-key-api.test.ts +2 -1
  78. package/src/__tests__/resend-verification-button.test.tsx +8 -24
  79. package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
  80. package/src/__tests__/settings.test.tsx +11 -21
  81. package/src/__tests__/setup-checklist.test.tsx +3 -9
  82. package/src/__tests__/setup.ts +25 -6
  83. package/src/__tests__/snapshot-api.test.ts +2 -1
  84. package/src/__tests__/step-superpowers.test.tsx +1 -3
  85. package/src/__tests__/tenant-context.test.tsx +1 -6
  86. package/src/__tests__/tenant-keys-api.test.ts +3 -4
  87. package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
  88. package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
  89. package/src/__tests__/transaction-history.test.tsx +190 -238
  90. package/src/__tests__/trpc-types.test.ts +2 -6
  91. package/src/__tests__/use-chat.test.ts +1 -3
  92. package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
  93. package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
  94. package/src/__tests__/use-webmcp.test.ts +1 -3
  95. package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
  96. package/src/__tests__/verify-page.test.tsx +4 -13
  97. package/src/__tests__/verify-redirect.test.tsx +2 -6
  98. package/src/app/(auth)/error.tsx +1 -7
  99. package/src/app/(auth)/forgot-password/page.tsx +4 -18
  100. package/src/app/(auth)/login/page.tsx +5 -22
  101. package/src/app/(auth)/reset-password/page.tsx +2 -12
  102. package/src/app/(auth)/signup/page.tsx +10 -44
  103. package/src/app/(auth)/verify/page.tsx +47 -0
  104. package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
  105. package/src/app/(dashboard)/billing/error.tsx +2 -10
  106. package/src/app/(dashboard)/billing/layout.tsx +12 -62
  107. package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
  108. package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
  109. package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
  110. package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
  111. package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
  112. package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
  113. package/src/app/(dashboard)/changesets/error.tsx +1 -7
  114. package/src/app/(dashboard)/chat/page.tsx +2 -6
  115. package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
  116. package/src/app/(dashboard)/error.tsx +1 -7
  117. package/src/app/(dashboard)/layout.tsx +15 -36
  118. package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
  119. package/src/app/(dashboard)/marketplace/error.tsx +1 -7
  120. package/src/app/(dashboard)/marketplace/page.tsx +6 -27
  121. package/src/app/(dashboard)/not-found.tsx +2 -5
  122. package/src/app/(dashboard)/onboarding/page.tsx +5 -22
  123. package/src/app/(dashboard)/settings/account/page.tsx +1 -6
  124. package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
  125. package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
  126. package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
  127. package/src/app/(dashboard)/settings/error.tsx +2 -10
  128. package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
  129. package/src/app/(dashboard)/settings/org/page.tsx +13 -56
  130. package/src/app/(dashboard)/settings/page.tsx +1 -0
  131. package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
  132. package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
  133. package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
  134. package/src/app/(dashboard)/settings/security/page.tsx +31 -111
  135. package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
  136. package/src/app/admin/error.tsx +1 -7
  137. package/src/app/admin/fleet-updates/error.tsx +1 -7
  138. package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
  139. package/src/app/admin/layout.tsx +4 -0
  140. package/src/app/admin/payment-methods/page.tsx +9 -38
  141. package/src/app/admin/products/error.tsx +2 -7
  142. package/src/app/admin/products/page.tsx +1 -4
  143. package/src/app/admin/promotions/[id]/page.tsx +9 -38
  144. package/src/app/admin/promotions/page.tsx +9 -36
  145. package/src/app/admin/rate-overrides/page.tsx +9 -45
  146. package/src/app/auth/callback/[provider]/page.tsx +1 -8
  147. package/src/app/auth/verify/page.tsx +9 -36
  148. package/src/app/channels/error.tsx +2 -10
  149. package/src/app/channels/layout.tsx +9 -0
  150. package/src/app/channels/page.tsx +8 -20
  151. package/src/app/channels/setup/[plugin]/page.tsx +3 -5
  152. package/src/app/error.tsx +1 -7
  153. package/src/app/fleet/error.tsx +1 -7
  154. package/src/app/fleet/layout.tsx +5 -0
  155. package/src/app/fleet/settings/page.tsx +1 -3
  156. package/src/app/global-error.tsx +2 -10
  157. package/src/app/globals.css +1 -4
  158. package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
  159. package/src/app/instances/error.tsx +2 -10
  160. package/src/app/instances/instance-list-client.tsx +20 -69
  161. package/src/app/instances/layout.tsx +9 -0
  162. package/src/app/instances/new/create-instance-client.tsx +10 -31
  163. package/src/app/layout.tsx +2 -10
  164. package/src/app/not-found.tsx +1 -3
  165. package/src/app/page.tsx +1 -2
  166. package/src/app/plugins/error.tsx +2 -10
  167. package/src/app/plugins/layout.tsx +5 -0
  168. package/src/app/plugins/page.tsx +16 -48
  169. package/src/app/pricing/error.tsx +1 -7
  170. package/src/app/privacy/page.tsx +93 -150
  171. package/src/app/status/error.tsx +1 -7
  172. package/src/app/terms/page.tsx +89 -144
  173. package/src/components/account-switcher.tsx +25 -52
  174. package/src/components/admin/accounting-dashboard.tsx +1 -3
  175. package/src/components/admin/admin-guard.tsx +1 -3
  176. package/src/components/admin/admin-nav.tsx +1 -3
  177. package/src/components/admin/affiliate-dashboard.tsx +25 -94
  178. package/src/components/admin/audit-log-table.tsx +13 -49
  179. package/src/components/admin/billing-health-dashboard.tsx +7 -25
  180. package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
  181. package/src/components/admin/bulk-actions-bar.tsx +1 -3
  182. package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
  183. package/src/components/admin/bulk-export-dialog.tsx +6 -32
  184. package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
  185. package/src/components/admin/bulk-grant-dialog.tsx +4 -15
  186. package/src/components/admin/bulk-preview-dialog.tsx +3 -12
  187. package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
  188. package/src/components/admin/bulk-select-all-banner.tsx +1 -6
  189. package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
  190. package/src/components/admin/bulk-undo-toast.tsx +1 -2
  191. package/src/components/admin/compliance-dashboard.tsx +31 -101
  192. package/src/components/admin/gpu-dashboard.tsx +21 -70
  193. package/src/components/admin/grant-credits-dialog.tsx +4 -17
  194. package/src/components/admin/incident-dashboard.tsx +10 -25
  195. package/src/components/admin/inference-dashboard.tsx +14 -54
  196. package/src/components/admin/marketplace-admin.tsx +18 -60
  197. package/src/components/admin/migrations-dashboard.tsx +9 -42
  198. package/src/components/admin/onboarding-dashboard.tsx +14 -64
  199. package/src/components/admin/pool-config-dashboard.tsx +4 -10
  200. package/src/components/admin/products/fleet-form.tsx +2 -11
  201. package/src/components/admin/products/nav-editor.tsx +3 -10
  202. package/src/components/admin/promotions/promotion-form.tsx +9 -42
  203. package/src/components/admin/roles-dashboard.tsx +7 -34
  204. package/src/components/admin/suspend-dialog.tsx +4 -11
  205. package/src/components/admin/tenant-notes-panel.tsx +1 -3
  206. package/src/components/admin/tenant-row-actions.tsx +4 -20
  207. package/src/components/admin/tenant-table.tsx +12 -49
  208. package/src/components/auth/auth-redirect.tsx +11 -3
  209. package/src/components/auth/email-verification-result-banner.tsx +1 -3
  210. package/src/components/auth/resend-verification-button.tsx +2 -10
  211. package/src/components/auth/wopr-wordmark.tsx +1 -3
  212. package/src/components/billing/add-payment-method-dialog.tsx +1 -2
  213. package/src/components/billing/affiliate-dashboard.tsx +4 -16
  214. package/src/components/billing/amount-selector.tsx +1 -3
  215. package/src/components/billing/auto-topup-card.tsx +2 -11
  216. package/src/components/billing/buy-credits-panel.tsx +14 -17
  217. package/src/components/billing/byok-callout.tsx +6 -8
  218. package/src/components/billing/confirmation-tracker.tsx +4 -14
  219. package/src/components/billing/credit-balance-badge.tsx +22 -0
  220. package/src/components/billing/credit-balance.tsx +3 -9
  221. package/src/components/billing/crypto-checkout.tsx +5 -24
  222. package/src/components/billing/degraded-state-banner.tsx +1 -3
  223. package/src/components/billing/deposit-view.tsx +301 -41
  224. package/src/components/billing/dividend-banner.tsx +1 -3
  225. package/src/components/billing/dividend-eligibility.tsx +3 -12
  226. package/src/components/billing/dividend-pool-stats.tsx +6 -20
  227. package/src/components/billing/first-dividend-dialog.tsx +2 -2
  228. package/src/components/billing/org-billing-page.tsx +8 -31
  229. package/src/components/billing/payment-method-picker.tsx +2 -10
  230. package/src/components/billing/suspension-banner.tsx +2 -7
  231. package/src/components/billing/transaction-history.tsx +10 -58
  232. package/src/components/billing/unified-checkout.tsx +547 -0
  233. package/src/components/bot-settings/backups-tab.tsx +9 -33
  234. package/src/components/bot-settings/bot-settings-client.tsx +32 -134
  235. package/src/components/bot-settings/resources-tab.tsx +2 -9
  236. package/src/components/bot-settings/storage-tab.tsx +19 -48
  237. package/src/components/bot-settings/vps-info-panel.tsx +3 -11
  238. package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
  239. package/src/components/brand-hydrator.tsx +13 -0
  240. package/src/components/channel-wizard/field-interactive.tsx +1 -3
  241. package/src/components/channel-wizard/field-qr.tsx +10 -39
  242. package/src/components/channel-wizard/step-renderer.tsx +5 -28
  243. package/src/components/channel-wizard/wizard.tsx +6 -31
  244. package/src/components/chat/chat-message.tsx +1 -4
  245. package/src/components/chat/chat-panel.tsx +4 -18
  246. package/src/components/chat/chat-widget.tsx +3 -14
  247. package/src/components/dashboard/command-center.tsx +15 -61
  248. package/src/components/fleet/update-settings-card.tsx +7 -23
  249. package/src/components/instance-update-banner.tsx +130 -0
  250. package/src/components/instances/friends-tab.test.tsx +2 -9
  251. package/src/components/instances/friends-tab.tsx +18 -74
  252. package/src/components/instances/update-available-badge.tsx +2 -11
  253. package/src/components/landing/hero.tsx +3 -9
  254. package/src/components/landing/landing-page.tsx +1 -3
  255. package/src/components/landing/portfolio-chart.tsx +4 -9
  256. package/src/components/landing/story-sections.tsx +1 -3
  257. package/src/components/landing/terminal-sequence.tsx +4 -17
  258. package/src/components/marketplace/empty-state.tsx +2 -6
  259. package/src/components/marketplace/first-visit-hero.tsx +1 -3
  260. package/src/components/marketplace/install-wizard.tsx +20 -77
  261. package/src/components/marketplace/marketplace-tabs.tsx +1 -4
  262. package/src/components/marketplace/plugin-card.tsx +2 -9
  263. package/src/components/marketplace/superpower-content.tsx +1 -3
  264. package/src/components/marketplace/terminal-search.tsx +2 -8
  265. package/src/components/oauth-buttons.tsx +29 -14
  266. package/src/components/observability/fleet-health.tsx +5 -18
  267. package/src/components/observability/health-overview.tsx +7 -20
  268. package/src/components/observability/logs-viewer.tsx +8 -32
  269. package/src/components/observability/metrics-dashboard.tsx +2 -15
  270. package/src/components/onboarding/fallback-setup.tsx +6 -25
  271. package/src/components/onboarding/setup-checklist.tsx +18 -51
  272. package/src/components/onboarding/step-superpowers.tsx +1 -4
  273. package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
  274. package/src/components/pricing/dividend-calculator.tsx +6 -12
  275. package/src/components/pricing/dividend-stats.tsx +5 -17
  276. package/src/components/pricing/pricing-page.tsx +17 -36
  277. package/src/components/settings/create-org-wizard.tsx +2 -5
  278. package/src/components/sidebar.tsx +7 -42
  279. package/src/components/sidecar-frame.tsx +78 -0
  280. package/src/components/status/status-page.tsx +6 -28
  281. package/src/components/ui/alert-dialog.tsx +8 -25
  282. package/src/components/ui/badge.tsx +2 -8
  283. package/src/components/ui/banner.tsx +1 -6
  284. package/src/components/ui/card.tsx +5 -24
  285. package/src/components/ui/checkbox.tsx +1 -5
  286. package/src/components/ui/collapsible.tsx +3 -8
  287. package/src/components/ui/dialog.tsx +4 -10
  288. package/src/components/ui/dropdown-menu.tsx +9 -18
  289. package/src/components/ui/form.tsx +2 -16
  290. package/src/components/ui/popover.tsx +3 -23
  291. package/src/components/ui/progress.tsx +1 -5
  292. package/src/components/ui/radio-group.tsx +3 -15
  293. package/src/components/ui/select.tsx +4 -17
  294. package/src/components/ui/sheet.tsx +5 -19
  295. package/src/components/ui/skeleton.tsx +1 -7
  296. package/src/components/ui/table.tsx +5 -22
  297. package/src/components/ui/tabs.tsx +3 -13
  298. package/src/components/ui/tooltip.tsx +1 -1
  299. package/src/components/unified-sidebar.tsx +493 -0
  300. package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
  301. package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
  302. package/src/hooks/use-credit-balance.ts +27 -0
  303. package/src/hooks/use-my-org-role.ts +1 -3
  304. package/src/hooks/use-plugin-registry.ts +8 -14
  305. package/src/hooks/use-plugin-setup-chat.ts +2 -5
  306. package/src/hooks/use-sidecar-bridge.tsx +148 -0
  307. package/src/hooks/use-webmcp.ts +1 -4
  308. package/src/lib/__tests__/admin-api.test.ts +1 -3
  309. package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
  310. package/src/lib/__tests__/api-fetch.test.ts +4 -16
  311. package/src/lib/__tests__/org-billing-api.test.ts +1 -3
  312. package/src/lib/__tests__/pricing-data.test.ts +0 -8
  313. package/src/lib/__tests__/settings-api.test.ts +1 -3
  314. package/src/lib/admin-affiliate-api.ts +2 -7
  315. package/src/lib/admin-api.ts +6 -26
  316. package/src/lib/admin-incident-api.ts +11 -19
  317. package/src/lib/admin-marketplace-api.ts +1 -5
  318. package/src/lib/api-config.test.ts +5 -50
  319. package/src/lib/api.ts +143 -122
  320. package/src/lib/auth-client.ts +1 -2
  321. package/src/lib/bot-settings-data.ts +11 -36
  322. package/src/lib/brand-config.ts +56 -115
  323. package/src/lib/brand.ts +2 -15
  324. package/src/lib/chat/use-chat.ts +2 -7
  325. package/src/lib/cost-comparison-data.test.ts +1 -3
  326. package/src/lib/cost-comparison-data.ts +1 -4
  327. package/src/lib/errors.ts +1 -4
  328. package/src/lib/fetch-utils.test.ts +26 -9
  329. package/src/lib/fetch-utils.ts +40 -11
  330. package/src/lib/logger.ts +2 -0
  331. package/src/lib/marketplace-data.ts +3 -11
  332. package/src/lib/oauth-errors.ts +2 -4
  333. package/src/lib/onboarding-data.ts +3 -11
  334. package/src/lib/org-api.ts +2 -10
  335. package/src/lib/org-billing-api.ts +5 -19
  336. package/src/lib/plugin/tool-definitions.ts +1 -2
  337. package/src/lib/require-auth.ts +57 -0
  338. package/src/lib/settings-api.ts +1 -4
  339. package/src/lib/sidecar-routes.ts +43 -0
  340. package/src/lib/trpc-server.ts +49 -0
  341. package/src/lib/trpc-types.ts +4 -6
  342. package/src/lib/trpc.tsx +12 -4
  343. package/src/lib/validate-redirect-url.ts +1 -4
  344. package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
  345. package/src/lib/webmcp/register.ts +1 -4
  346. package/src/lib/webmcp/tools.ts +2 -9
  347. package/src/proxy.ts +35 -212
  348. package/src/types/missing-deps.d.ts +2 -8
  349. package/tsconfig.json +1 -8
  350. package/biome.json +0 -52
  351. package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
  352. package/src/__tests__/billing-byok-callout.test.tsx +0 -76
  353. package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
@@ -61,17 +61,14 @@ function BulkSuspendDialog({
61
61
  <DialogHeader>
62
62
  <DialogTitle className="flex items-center gap-2">
63
63
  <ShieldBan className="size-5 text-amber-500" />
64
- <span className="text-amber-500">Suspend</span>{" "}
65
- <span className="font-semibold">{selectedCount}</span> accounts
64
+ <span className="text-amber-500">Suspend</span> <span className="font-semibold">{selectedCount}</span>{" "}
65
+ accounts
66
66
  </DialogTitle>
67
- <DialogDescription>
68
- Suspended accounts will immediately lose platform access.
69
- </DialogDescription>
67
+ <DialogDescription>Suspended accounts will immediately lose platform access.</DialogDescription>
70
68
  </DialogHeader>
71
69
 
72
70
  <Banner variant="warning">
73
- This will immediately prevent these accounts from using the platform. Users will be locked
74
- out.
71
+ This will immediately prevent these accounts from using the platform. Users will be locked out.
75
72
  </Banner>
76
73
 
77
74
  <div className="flex flex-col gap-4">
@@ -113,11 +110,7 @@ function BulkSuspendDialog({
113
110
  <Button variant="outline" onClick={() => handleOpenChange(false)} disabled={isLoading}>
114
111
  Cancel
115
112
  </Button>
116
- <Button
117
- variant="destructive"
118
- onClick={handleConfirm}
119
- disabled={!reason.trim() || isLoading}
120
- >
113
+ <Button variant="destructive" onClick={handleConfirm} disabled={!reason.trim() || isLoading}>
121
114
  Suspend {selectedCount} accounts
122
115
  </Button>
123
116
  </DialogFooter>
@@ -59,8 +59,7 @@ function BulkUndoToast({
59
59
 
60
60
  const progressPercent = (remainingMs / windowMs) * 100;
61
61
 
62
- const barColor =
63
- remainingMs > 180_000 ? "bg-terminal" : remainingMs > 60_000 ? "bg-amber-500" : "bg-red-500";
62
+ const barColor = remainingMs > 180_000 ? "bg-terminal" : remainingMs > 60_000 ? "bg-amber-500" : "bg-red-500";
64
63
 
65
64
  return (
66
65
  <AnimatePresence>
@@ -14,23 +14,10 @@ import {
14
14
  } from "@/components/ui/dialog";
15
15
  import { Input } from "@/components/ui/input";
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 { Skeleton } from "@/components/ui/skeleton";
25
19
  import { Switch } from "@/components/ui/switch";
26
- import {
27
- Table,
28
- TableBody,
29
- TableCell,
30
- TableHead,
31
- TableHeader,
32
- TableRow,
33
- } from "@/components/ui/table";
20
+ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
34
21
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
35
22
  import { Textarea } from "@/components/ui/textarea";
36
23
  import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
@@ -106,8 +93,7 @@ function complianceActionBadgeClasses(action: string): string {
106
93
  return "bg-destructive/15 text-red-400 border border-destructive/20";
107
94
  if (action.includes("trigger_export") || action.includes("complete_export"))
108
95
  return "bg-terminal/15 text-terminal border border-terminal/20";
109
- if (action.includes("policy_update"))
110
- return "bg-amber-500/15 text-amber-400 border border-amber-500/20";
96
+ if (action.includes("policy_update")) return "bg-amber-500/15 text-amber-400 border border-amber-500/20";
111
97
  return "bg-secondary text-muted-foreground border border-border";
112
98
  }
113
99
 
@@ -202,9 +188,7 @@ function TriggerDialog({
202
188
  <Dialog open={open} onOpenChange={handleOpenChange}>
203
189
  <DialogContent>
204
190
  <DialogHeader>
205
- <DialogTitle className="text-base font-semibold uppercase tracking-wider">
206
- {title}
207
- </DialogTitle>
191
+ <DialogTitle className="text-base font-semibold uppercase tracking-wider">{title}</DialogTitle>
208
192
  <DialogDescription>{description}</DialogDescription>
209
193
  </DialogHeader>
210
194
  <div className="space-y-3">
@@ -270,12 +254,7 @@ function Pagination({
270
254
  >
271
255
  Previous
272
256
  </Button>
273
- <Button
274
- variant="ghost"
275
- size="sm"
276
- disabled={!hasMore}
277
- onClick={() => onNavigate(offset + PAGE_SIZE)}
278
- >
257
+ <Button variant="ghost" size="sm" disabled={!hasMore} onClick={() => onNavigate(offset + PAGE_SIZE)}>
279
258
  Next
280
259
  </Button>
281
260
  <Button
@@ -417,8 +396,8 @@ function DeletionRequestsTab() {
417
396
  <span className="animate-ellipsis" />
418
397
  </p>
419
398
  <p className="mt-2 text-xs text-muted-foreground">
420
- The admin.complianceDeletionRequests procedure is not yet available. This section will
421
- activate automatically once deployed.
399
+ The admin.complianceDeletionRequests procedure is not yet available. This section will activate
400
+ automatically once deployed.
422
401
  </p>
423
402
  </div>
424
403
  <TriggerDialog
@@ -485,27 +464,15 @@ function DeletionRequestsTab() {
485
464
  <Table>
486
465
  <TableHeader>
487
466
  <TableRow className="bg-secondary crt-scanlines">
488
- <TableHead className="text-xs font-medium uppercase tracking-wider w-[140px]">
489
- Tenant ID
490
- </TableHead>
491
- <TableHead className="text-xs font-medium uppercase tracking-wider">
492
- Requested By
493
- </TableHead>
467
+ <TableHead className="text-xs font-medium uppercase tracking-wider w-[140px]">Tenant ID</TableHead>
468
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Requested By</TableHead>
494
469
  <TableHead className="text-xs font-medium uppercase tracking-wider">Status</TableHead>
495
- <TableHead className="text-xs font-medium uppercase tracking-wider">
496
- Delete After
497
- </TableHead>
498
- <TableHead className="text-xs font-medium uppercase tracking-wider">
499
- Created
500
- </TableHead>
501
- <TableHead className="text-xs font-medium uppercase tracking-wider w-[80px]">
502
- Actions
503
- </TableHead>
470
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Delete After</TableHead>
471
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Created</TableHead>
472
+ <TableHead className="text-xs font-medium uppercase tracking-wider w-[80px]">Actions</TableHead>
504
473
  </TableRow>
505
474
  </TableHeader>
506
- <TableBody
507
- className={cn("transition-opacity duration-150", loading && data && "opacity-60")}
508
- >
475
+ <TableBody className={cn("transition-opacity duration-150", loading && data && "opacity-60")}>
509
476
  {loading && !data ? (
510
477
  <SkeletonRows cols={6} />
511
478
  ) : filteredRequests.length === 0 && !loading ? (
@@ -697,8 +664,8 @@ function DataExportsTab() {
697
664
  <span className="animate-ellipsis" />
698
665
  </p>
699
666
  <p className="mt-2 text-xs text-muted-foreground">
700
- The admin.complianceExportRequests procedure is not yet available. This section will
701
- activate automatically once deployed.
667
+ The admin.complianceExportRequests procedure is not yet available. This section will activate automatically
668
+ once deployed.
702
669
  </p>
703
670
  </div>
704
671
  <TriggerDialog
@@ -765,25 +732,15 @@ function DataExportsTab() {
765
732
  <Table>
766
733
  <TableHeader>
767
734
  <TableRow className="bg-secondary crt-scanlines">
768
- <TableHead className="text-xs font-medium uppercase tracking-wider w-[140px]">
769
- Tenant ID
770
- </TableHead>
771
- <TableHead className="text-xs font-medium uppercase tracking-wider">
772
- Requested By
773
- </TableHead>
735
+ <TableHead className="text-xs font-medium uppercase tracking-wider w-[140px]">Tenant ID</TableHead>
736
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Requested By</TableHead>
774
737
  <TableHead className="text-xs font-medium uppercase tracking-wider">Status</TableHead>
775
738
  <TableHead className="text-xs font-medium uppercase tracking-wider">Format</TableHead>
776
- <TableHead className="text-xs font-medium uppercase tracking-wider">
777
- Created
778
- </TableHead>
779
- <TableHead className="text-xs font-medium uppercase tracking-wider w-[80px]">
780
- Actions
781
- </TableHead>
739
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Created</TableHead>
740
+ <TableHead className="text-xs font-medium uppercase tracking-wider w-[80px]">Actions</TableHead>
782
741
  </TableRow>
783
742
  </TableHeader>
784
- <TableBody
785
- className={cn("transition-opacity duration-150", loading && data && "opacity-60")}
786
- >
743
+ <TableBody className={cn("transition-opacity duration-150", loading && data && "opacity-60")}>
787
744
  {loading && !data ? (
788
745
  <SkeletonRows cols={6} />
789
746
  ) : filteredRequests.length === 0 && !loading ? (
@@ -882,9 +839,7 @@ function ComplianceAuditTab() {
882
839
  setLoadError(false);
883
840
  try {
884
841
  const since =
885
- dateRange === "all"
886
- ? undefined
887
- : new Date(Date.now() - Number(dateRange) * 86400000).toISOString();
842
+ dateRange === "all" ? undefined : new Date(Date.now() - Number(dateRange) * 86400000).toISOString();
888
843
  const result = await fetchAuditLog({
889
844
  limit: PAGE_SIZE,
890
845
  offset: newOffset,
@@ -976,21 +931,13 @@ function ComplianceAuditTab() {
976
931
  <Table>
977
932
  <TableHeader>
978
933
  <TableRow className="bg-secondary crt-scanlines">
979
- <TableHead className="text-xs font-medium uppercase tracking-wider w-[100px]">
980
- Time
981
- </TableHead>
934
+ <TableHead className="text-xs font-medium uppercase tracking-wider w-[100px]">Time</TableHead>
982
935
  <TableHead className="text-xs font-medium uppercase tracking-wider">Action</TableHead>
983
- <TableHead className="text-xs font-medium uppercase tracking-wider">
984
- Resource
985
- </TableHead>
986
- <TableHead className="text-xs font-medium uppercase tracking-wider">
987
- Details
988
- </TableHead>
936
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Resource</TableHead>
937
+ <TableHead className="text-xs font-medium uppercase tracking-wider">Details</TableHead>
989
938
  </TableRow>
990
939
  </TableHeader>
991
- <TableBody
992
- className={cn("transition-opacity duration-150", loading && data && "opacity-60")}
993
- >
940
+ <TableBody className={cn("transition-opacity duration-150", loading && data && "opacity-60")}>
994
941
  {loading && !data ? (
995
942
  <SkeletonRows cols={4} rows={8} />
996
943
  ) : filteredEvents.length === 0 && !loading ? (
@@ -1017,12 +964,8 @@ function ComplianceAuditTab() {
1017
964
  </span>
1018
965
  </TableCell>
1019
966
  <TableCell className="text-sm">
1020
- <span className="text-xs uppercase tracking-wide text-muted-foreground">
1021
- {event.resourceType}
1022
- </span>{" "}
1023
- <span className="font-mono text-xs">
1024
- {event.resourceName ?? event.resourceId}
1025
- </span>
967
+ <span className="text-xs uppercase tracking-wide text-muted-foreground">{event.resourceType}</span>{" "}
968
+ <span className="font-mono text-xs">{event.resourceName ?? event.resourceId}</span>
1026
969
  </TableCell>
1027
970
  <TableCell className="text-xs text-muted-foreground max-w-[300px] truncate">
1028
971
  {event.details ?? "\u2014"}
@@ -1034,14 +977,7 @@ function ComplianceAuditTab() {
1034
977
  </Table>
1035
978
  </div>
1036
979
 
1037
- {data && (
1038
- <Pagination
1039
- offset={offset}
1040
- total={data.total}
1041
- hasMore={data.hasMore}
1042
- onNavigate={(o) => load(o)}
1043
- />
1044
- )}
980
+ {data && <Pagination offset={offset} total={data.total} hasMore={data.hasMore} onNavigate={(o) => load(o)} />}
1045
981
  </div>
1046
982
  );
1047
983
  }
@@ -1098,9 +1034,7 @@ function EditRetentionPolicyDialog({
1098
1034
  <Dialog open={open} onOpenChange={onOpenChange}>
1099
1035
  <DialogContent>
1100
1036
  <DialogHeader>
1101
- <DialogTitle className="text-base font-semibold uppercase tracking-wider">
1102
- Edit Retention Policy
1103
- </DialogTitle>
1037
+ <DialogTitle className="text-base font-semibold uppercase tracking-wider">Edit Retention Policy</DialogTitle>
1104
1038
  <DialogDescription>Update retention settings for {policy.dataType}</DialogDescription>
1105
1039
  </DialogHeader>
1106
1040
  <div className="space-y-4 py-2">
@@ -1215,9 +1149,7 @@ function RetentionPoliciesTab() {
1215
1149
  key={policy.dataType}
1216
1150
  className="rounded-sm border border-terminal/10 bg-card p-4 transition-colors duration-150 hover:border-terminal/20"
1217
1151
  >
1218
- <h3 className="text-sm font-semibold uppercase tracking-wider text-foreground">
1219
- {policy.dataType}
1220
- </h3>
1152
+ <h3 className="text-sm font-semibold uppercase tracking-wider text-foreground">{policy.dataType}</h3>
1221
1153
  <dl className="mt-3 space-y-1.5 text-xs font-mono text-muted-foreground">
1222
1154
  <div className="flex justify-between">
1223
1155
  <dt>Retention period</dt>
@@ -1271,9 +1203,7 @@ function RetentionPoliciesTab() {
1271
1203
  if (!open) setEditingPolicy(null);
1272
1204
  }}
1273
1205
  onSaved={(updated) => {
1274
- setPolicies((prev) =>
1275
- prev ? prev.map((p) => (p.dataType === updated.dataType ? updated : p)) : prev,
1276
- );
1206
+ setPolicies((prev) => (prev ? prev.map((p) => (p.dataType === updated.dataType ? updated : p)) : prev));
1277
1207
  setEditingPolicy(null);
1278
1208
  }}
1279
1209
  />
@@ -73,9 +73,7 @@ function KpiCard({ label, value, subtext, valueClassName, loading, index }: KpiC
73
73
  {loading ? (
74
74
  <div className="h-8 w-24 bg-muted animate-pulse rounded-sm" />
75
75
  ) : (
76
- <div className={cn("text-2xl font-bold tabular-nums", valueClassName ?? "text-foreground")}>
77
- {value}
78
- </div>
76
+ <div className={cn("text-2xl font-bold tabular-nums", valueClassName ?? "text-foreground")}>{value}</div>
79
77
  )}
80
78
  {subtext && <div className="text-xs text-muted-foreground mt-1">{subtext}</div>}
81
79
  </motion.div>
@@ -122,9 +120,7 @@ function ProvisionForm({ regions, sizes, onProvision, onCancel }: ProvisionFormP
122
120
  transition={{ duration: 0.2 }}
123
121
  className="mx-6 mb-4 bg-card border border-terminal/20 rounded-sm p-4"
124
122
  >
125
- <div className="text-xs uppercase tracking-widest text-muted-foreground mb-3">
126
- Provision New GPU Node
127
- </div>
123
+ <div className="text-xs uppercase tracking-widest text-muted-foreground mb-3">Provision New GPU Node</div>
128
124
  <form onSubmit={handleSubmit} className="grid grid-cols-4 gap-3 items-end">
129
125
  <div>
130
126
  <label htmlFor="gpu-name" className="text-xs text-muted-foreground block mb-1">
@@ -143,12 +139,7 @@ function ProvisionForm({ regions, sizes, onProvision, onCancel }: ProvisionFormP
143
139
  <label htmlFor="gpu-region" className="text-xs text-muted-foreground block mb-1">
144
140
  Region
145
141
  </label>
146
- <select
147
- id="gpu-region"
148
- className={inputCls}
149
- value={region}
150
- onChange={(e) => setRegion(e.target.value)}
151
- >
142
+ <select id="gpu-region" className={inputCls} value={region} onChange={(e) => setRegion(e.target.value)}>
152
143
  {regions.map((r) => (
153
144
  <option key={r.slug} value={r.slug} disabled={!r.available}>
154
145
  {r.name}
@@ -160,12 +151,7 @@ function ProvisionForm({ regions, sizes, onProvision, onCancel }: ProvisionFormP
160
151
  <label htmlFor="gpu-size" className="text-xs text-muted-foreground block mb-1">
161
152
  Size
162
153
  </label>
163
- <select
164
- id="gpu-size"
165
- className={inputCls}
166
- value={size}
167
- onChange={(e) => setSize(e.target.value)}
168
- >
154
+ <select id="gpu-size" className={inputCls} value={size} onChange={(e) => setSize(e.target.value)}>
169
155
  {sizes.map((s) => (
170
156
  <option key={s.slug} value={s.slug}>
171
157
  {s.name} — ${s.priceMonthly}/mo
@@ -202,11 +188,7 @@ export function GpuDashboard() {
202
188
  setLoading(true);
203
189
  setError(null);
204
190
  try {
205
- const [nodeList, regionList, sizeList] = await Promise.all([
206
- listGpuNodes(),
207
- listGpuRegions(),
208
- listGpuSizes(),
209
- ]);
191
+ const [nodeList, regionList, sizeList] = await Promise.all([listGpuNodes(), listGpuRegions(), listGpuSizes()]);
210
192
  setNodes(nodeList);
211
193
  setRegions(regionList);
212
194
  setSizes(sizeList);
@@ -269,18 +251,13 @@ export function GpuDashboard() {
269
251
  // KPI computations
270
252
  const runningNodes = nodes.filter((n) => n.status === "running");
271
253
  const errorNodes = nodes.filter((n) => n.status === "error");
272
- const utilizationValues = runningNodes
273
- .filter((n) => n.utilization != null)
274
- .map((n) => n.utilization as number);
254
+ const utilizationValues = runningNodes.filter((n) => n.utilization != null).map((n) => n.utilization as number);
275
255
  const avgUtilization =
276
- utilizationValues.length > 0
277
- ? utilizationValues.reduce((a, b) => a + b, 0) / utilizationValues.length
278
- : null;
256
+ utilizationValues.length > 0 ? utilizationValues.reduce((a, b) => a + b, 0) / utilizationValues.length : null;
279
257
  const tempValues = runningNodes
280
258
  .filter((n) => n.temperatureCelsius != null)
281
259
  .map((n) => n.temperatureCelsius as number);
282
- const avgTemp =
283
- tempValues.length > 0 ? tempValues.reduce((a, b) => a + b, 0) / tempValues.length : null;
260
+ const avgTemp = tempValues.length > 0 ? tempValues.reduce((a, b) => a + b, 0) / tempValues.length : null;
284
261
 
285
262
  return (
286
263
  <div className="space-y-4 max-w-7xl mx-auto">
@@ -301,12 +278,7 @@ export function GpuDashboard() {
301
278
  <RefreshCw size={12} className={loading ? "animate-spin" : ""} />
302
279
  Refresh
303
280
  </Button>
304
- <Button
305
- type="button"
306
- size="xs"
307
- onClick={() => setShowProvision((v) => !v)}
308
- className="font-mono"
309
- >
281
+ <Button type="button" size="xs" onClick={() => setShowProvision((v) => !v)} className="font-mono">
310
282
  <Zap size={12} />
311
283
  Provision Node
312
284
  </Button>
@@ -323,12 +295,7 @@ export function GpuDashboard() {
323
295
 
324
296
  {/* KPI Cards */}
325
297
  <div className="grid grid-cols-2 xl:grid-cols-4 gap-3 px-6">
326
- <KpiCard
327
- index={0}
328
- label="Total Nodes"
329
- value={loading ? "" : String(nodes.length)}
330
- loading={loading}
331
- />
298
+ <KpiCard index={0} label="Total Nodes" value={loading ? "" : String(nodes.length)} loading={loading} />
332
299
  <KpiCard
333
300
  index={1}
334
301
  label="Running"
@@ -340,9 +307,7 @@ export function GpuDashboard() {
340
307
  index={2}
341
308
  label="Avg Utilization"
342
309
  value={loading ? "" : avgUtilization != null ? `${avgUtilization.toFixed(1)}%` : "—"}
343
- valueClassName={
344
- avgUtilization != null && avgUtilization > 85 ? "text-amber-400" : "text-foreground"
345
- }
310
+ valueClassName={avgUtilization != null && avgUtilization > 85 ? "text-amber-400" : "text-foreground"}
346
311
  subtext="running nodes"
347
312
  loading={loading}
348
313
  />
@@ -380,9 +345,7 @@ export function GpuDashboard() {
380
345
  className="mx-6 bg-card border border-border rounded-sm"
381
346
  >
382
347
  <div className="px-4 py-3 border-b border-border">
383
- <div className="text-xs uppercase tracking-widest text-muted-foreground">
384
- GPU Inventory
385
- </div>
348
+ <div className="text-xs uppercase tracking-widest text-muted-foreground">GPU Inventory</div>
386
349
  </div>
387
350
 
388
351
  {loading ? (
@@ -399,9 +362,7 @@ export function GpuDashboard() {
399
362
  ))}
400
363
  </div>
401
364
  ) : nodes.length === 0 ? (
402
- <div className="text-center text-muted-foreground text-xs py-12">
403
- No GPU nodes provisioned
404
- </div>
365
+ <div className="text-center text-muted-foreground text-xs py-12">No GPU nodes provisioned</div>
405
366
  ) : (
406
367
  <>
407
368
  {/* Table header */}
@@ -439,9 +400,7 @@ export function GpuDashboard() {
439
400
  <div
440
401
  className={cn(
441
402
  "w-24 text-right tabular-nums text-sm",
442
- node.utilization != null && node.utilization > 85
443
- ? "text-amber-400"
444
- : "text-foreground",
403
+ node.utilization != null && node.utilization > 85 ? "text-amber-400" : "text-foreground",
445
404
  )}
446
405
  >
447
406
  {node.utilization != null ? `${node.utilization}%` : "—"}
@@ -459,9 +418,7 @@ export function GpuDashboard() {
459
418
  {node.temperatureCelsius != null ? `${node.temperatureCelsius}°C` : "—"}
460
419
  </div>
461
420
  <div className="flex-1 text-right tabular-nums text-sm text-muted-foreground">
462
- {vramPct != null
463
- ? `${vramPct} (${node.memoryUsedMib} / ${node.memoryTotalMib} MiB)`
464
- : "—"}
421
+ {vramPct != null ? `${vramPct} (${node.memoryUsedMib} / ${node.memoryTotalMib} MiB)` : "—"}
465
422
  </div>
466
423
  <div className="w-28 flex justify-end gap-1">
467
424
  <Button
@@ -495,25 +452,19 @@ export function GpuDashboard() {
495
452
 
496
453
  {/* Allocation — stubbed */}
497
454
  <div className="mx-6 bg-card border border-border rounded-sm p-4">
498
- <div className="text-xs uppercase tracking-widest text-muted-foreground mb-2">
499
- Allocation / Tenant Mapping
500
- </div>
455
+ <div className="text-xs uppercase tracking-widest text-muted-foreground mb-2">Allocation / Tenant Mapping</div>
501
456
  <p className="text-xs text-muted-foreground">
502
- Tenant-to-GPU allocation endpoints are not yet available. This section will display which
503
- bots and tenants are assigned to each GPU node once the backend implements the allocation
504
- API.
457
+ Tenant-to-GPU allocation endpoints are not yet available. This section will display which bots and tenants are
458
+ assigned to each GPU node once the backend implements the allocation API.
505
459
  </p>
506
460
  </div>
507
461
 
508
462
  {/* Configuration — stubbed */}
509
463
  <div className="mx-6 mb-6 bg-card border border-border rounded-sm p-4">
510
- <div className="text-xs uppercase tracking-widest text-muted-foreground mb-2">
511
- GPU Configuration
512
- </div>
464
+ <div className="text-xs uppercase tracking-widest text-muted-foreground mb-2">GPU Configuration</div>
513
465
  <p className="text-xs text-muted-foreground">
514
- GPU capability enable/disable and allocation limit configuration endpoints are not yet
515
- available. This section will provide controls once the backend exposes the configuration
516
- API.
466
+ GPU capability enable/disable and allocation limit configuration endpoints are not yet available. This section
467
+ will provide controls once the backend exposes the configuration API.
517
468
  </p>
518
469
  </div>
519
470
 
@@ -29,12 +29,7 @@ interface GrantCreditsDialogProps {
29
29
  onComplete: () => void;
30
30
  }
31
31
 
32
- export function GrantCreditsDialog({
33
- open,
34
- onOpenChange,
35
- user,
36
- onComplete,
37
- }: GrantCreditsDialogProps) {
32
+ export function GrantCreditsDialog({ open, onOpenChange, user, onComplete }: GrantCreditsDialogProps) {
38
33
  const [amount, setAmount] = useState("");
39
34
  const [reason, setReason] = useState("");
40
35
  const [submitting, setSubmitting] = useState(false);
@@ -70,9 +65,7 @@ export function GrantCreditsDialog({
70
65
  <div className="space-y-2">
71
66
  <Label htmlFor="grant-amount">Amount</Label>
72
67
  <div className="relative">
73
- <span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">
74
- $
75
- </span>
68
+ <span className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">$</span>
76
69
  <Input
77
70
  id="grant-amount"
78
71
  type="number"
@@ -105,14 +98,8 @@ export function GrantCreditsDialog({
105
98
  <Button variant="outline" onClick={() => onOpenChange(false)}>
106
99
  Cancel
107
100
  </Button>
108
- <Button
109
- variant="terminal"
110
- disabled={!isValidAmount || !reason.trim() || submitting}
111
- onClick={handleGrant}
112
- >
113
- {submitting
114
- ? "Granting..."
115
- : `Grant ${isValidAmount ? formatCreditStandard(parsedAmount) : "$0.00"}`}
101
+ <Button variant="terminal" disabled={!isValidAmount || !reason.trim() || submitting} onClick={handleGrant}>
102
+ {submitting ? "Granting..." : `Grant ${isValidAmount ? formatCreditStandard(parsedAmount) : "$0.00"}`}
116
103
  </Button>
117
104
  </DialogFooter>
118
105
  </DialogContent>
@@ -37,10 +37,7 @@ function SeverityBadge({ severity }: { severity: IncidentSeverity }) {
37
37
  SEV3: "bg-yellow-500/15 text-yellow-400 border-yellow-500/20",
38
38
  };
39
39
  return (
40
- <Badge
41
- variant="secondary"
42
- className={cn("text-xs px-2 py-0.5 border font-mono", colors[severity])}
43
- >
40
+ <Badge variant="secondary" className={cn("text-xs px-2 py-0.5 border font-mono", colors[severity])}>
44
41
  {severity}
45
42
  </Badge>
46
43
  );
@@ -134,9 +131,7 @@ function SeverityClassifierPanel() {
134
131
  type="number"
135
132
  min={0}
136
133
  value={signals.dlqDepth}
137
- onChange={(e) =>
138
- setSignals((s) => ({ ...s, dlqDepth: Number.parseInt(e.target.value, 10) || 0 }))
139
- }
134
+ onChange={(e) => setSignals((s) => ({ ...s, dlqDepth: Number.parseInt(e.target.value, 10) || 0 }))}
140
135
  className="h-7 text-xs"
141
136
  />
142
137
  </div>
@@ -272,14 +267,13 @@ function EscalationPanel() {
272
267
  <div className="space-y-2">
273
268
  {result.contacts.map((contact: EscalationContact, i: number) => (
274
269
  <div
270
+ // biome-ignore lint/suspicious/noArrayIndexKey: static list, index key is safe
275
271
  key={`${contact.role}-${i}`}
276
272
  className="flex items-center justify-between py-2 border-b border-border/50 last:border-0 text-sm"
277
273
  >
278
274
  <div>
279
275
  <span className="font-medium">{contact.role}</span>
280
- {contact.name && (
281
- <span className="text-muted-foreground ml-2">— {contact.name}</span>
282
- )}
276
+ {contact.name && <span className="text-muted-foreground ml-2">— {contact.name}</span>}
283
277
  </div>
284
278
  <div className="text-xs text-muted-foreground">
285
279
  {contact.method} · within {contact.within}
@@ -346,6 +340,7 @@ function ResponseProcedurePanel() {
346
340
  {procedure && (
347
341
  <ol className="space-y-2 list-decimal list-inside">
348
342
  {procedure.steps.map((step: string, index: number) => (
343
+ // biome-ignore lint/suspicious/noArrayIndexKey: static list, index key is safe
349
344
  <li key={`${index}-${step.slice(0, 20)}`} className="text-sm text-muted-foreground">
350
345
  {step}
351
346
  </li>
@@ -442,9 +437,7 @@ function CommunicationPanel() {
442
437
  />
443
438
  </div>
444
439
  <div className="space-y-1">
445
- <Label className="text-xs text-muted-foreground">
446
- Affected Systems (comma-separated)
447
- </Label>
440
+ <Label className="text-xs text-muted-foreground">Affected Systems (comma-separated)</Label>
448
441
  <Input
449
442
  value={affectedSystemsText}
450
443
  onChange={(e) => setAffectedSystemsText(e.target.value)}
@@ -477,17 +470,13 @@ function CommunicationPanel() {
477
470
  {templates && (
478
471
  <div className="space-y-3">
479
472
  <div>
480
- <div className="text-xs font-medium text-muted-foreground mb-1">
481
- Customer Template
482
- </div>
473
+ <div className="text-xs font-medium text-muted-foreground mb-1">Customer Template</div>
483
474
  <pre className="text-xs bg-muted/30 rounded p-3 whitespace-pre-wrap font-mono border border-border/50">
484
475
  {templates.customer}
485
476
  </pre>
486
477
  </div>
487
478
  <div>
488
- <div className="text-xs font-medium text-muted-foreground mb-1">
489
- Internal Template
490
- </div>
479
+ <div className="text-xs font-medium text-muted-foreground mb-1">Internal Template</div>
491
480
  <pre className="text-xs bg-muted/30 rounded p-3 whitespace-pre-wrap font-mono border border-border/50">
492
481
  {templates.internal}
493
482
  </pre>
@@ -628,9 +617,7 @@ function PostmortemPanel() {
628
617
  />
629
618
  </div>
630
619
  <div className="space-y-1">
631
- <Label className="text-xs text-muted-foreground">
632
- Affected Systems (comma-separated)
633
- </Label>
620
+ <Label className="text-xs text-muted-foreground">Affected Systems (comma-separated)</Label>
634
621
  <Input
635
622
  value={affectedSystemsText}
636
623
  onChange={(e) => setAffectedSystemsText(e.target.value)}
@@ -696,9 +683,7 @@ export function IncidentDashboard() {
696
683
  onClick={() => setTab(t.id)}
697
684
  className={cn(
698
685
  "text-sm px-3 py-1.5 rounded-sm transition-colors",
699
- tab === t.id
700
- ? "bg-terminal/10 text-terminal font-medium"
701
- : "text-muted-foreground hover:text-foreground",
686
+ tab === t.id ? "bg-terminal/10 text-terminal font-medium" : "text-muted-foreground hover:text-foreground",
702
687
  )}
703
688
  >
704
689
  {t.label}