@aglyn/plugins-ai 1.0.0-beta.230 → 1.0.0-beta.232
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/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +8 -4
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-assist-provider-on-host.component.d.ts +66 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js +78 -0
- package/src/lib/components/ai-assist-provider-on-host.component.js.map +1 -0
- package/src/lib/components/ai-assist-provider.component.js +9 -10
- package/src/lib/components/ai-assist-provider.component.js.map +1 -1
- package/src/lib/components/ai-job-follow.component.js +1 -0
- package/src/lib/components/ai-job-follow.component.js.map +1 -1
- package/src/lib/components/ai-job-links.js +13 -0
- package/src/lib/components/ai-job-links.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.js +3 -1
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +1 -0
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-media-create.component.d.ts +20 -7
- package/src/lib/components/ai-media-create.component.js +107 -55
- package/src/lib/components/ai-media-create.component.js.map +1 -1
- package/src/lib/components/ai-permissions-on-host.component.d.ts +3 -46
- package/src/lib/components/ai-permissions-on-host.component.js +5 -53
- package/src/lib/components/ai-permissions-on-host.component.js.map +1 -1
- package/src/lib/components/ai-seo-fields-card.component.js +1 -0
- package/src/lib/components/ai-seo-fields-card.component.js.map +1 -1
- package/src/lib/components/ai-site-build-page.component.js +20 -8
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-memory-card.component.d.ts +32 -0
- package/src/lib/components/ai-site-memory-card.component.js +187 -0
- package/src/lib/components/ai-site-memory-card.component.js.map +1 -0
- package/src/lib/components/ai-site-start-card.component.js +80 -77
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-theme-proposal-card.component.js +4 -0
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/components/ai-usage-strip.component.js +1 -0
- package/src/lib/components/ai-usage-strip.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.d.ts +10 -0
- package/src/lib/components/assist-panel.component.js +119 -9
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/assist-signals-page.component.js +2 -0
- package/src/lib/components/assist-signals-page.component.js.map +1 -1
- package/src/lib/components/billing-ai-allotments.component.js +3 -0
- package/src/lib/components/billing-ai-allotments.component.js.map +1 -1
- package/src/lib/components/billing-ai-top-users.component.js +1 -0
- package/src/lib/components/billing-ai-top-users.component.js.map +1 -1
- package/src/lib/components/billing-assist-overage-card.component.js +2 -0
- package/src/lib/components/billing-assist-overage-card.component.js.map +1 -1
- package/src/lib/components/member-ai-usage-card.component.js +10 -0
- package/src/lib/components/member-ai-usage-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-card.component.js +1 -0
- package/src/lib/components/staff-org-ai-card.component.js.map +1 -1
- package/src/lib/components/staff-org-ai-conversations.component.d.ts +23 -0
- package/src/lib/components/staff-org-ai-conversations.component.js +346 -0
- package/src/lib/components/staff-org-ai-conversations.component.js.map +1 -0
- package/src/lib/jobs/ai-build-capabilities.d.ts +13 -1
- package/src/lib/jobs/ai-build-capabilities.js +101 -7
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-build-unit-outcome.js +4 -2
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.d.ts +29 -0
- package/src/lib/jobs/ai-business-profile-prefill.js +85 -0
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -0
- package/src/lib/jobs/ai-job-auto-confirm.d.ts +8 -0
- package/src/lib/jobs/ai-job-auto-confirm.js +30 -6
- package/src/lib/jobs/ai-job-auto-confirm.js.map +1 -1
- package/src/lib/jobs/ai-job-build-step.js +14 -1
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-edit-step.d.ts +225 -0
- package/src/lib/jobs/ai-job-edit-step.js +631 -0
- package/src/lib/jobs/ai-job-edit-step.js.map +1 -0
- package/src/lib/jobs/ai-job-generation.d.ts +8 -0
- package/src/lib/jobs/ai-job-generation.js +13 -2
- package/src/lib/jobs/ai-job-generation.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.js +29 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.d.ts +78 -0
- package/src/lib/jobs/ai-job-layout-language.js +200 -0
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +13 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js +18 -2
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-step.d.ts +5 -0
- package/src/lib/jobs/ai-job-layout-step.js +107 -6
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +20 -0
- package/src/lib/jobs/ai-job-logic-step.js +87 -6
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.d.ts +194 -0
- package/src/lib/jobs/ai-job-page-language.js +418 -0
- package/src/lib/jobs/ai-job-page-language.js.map +1 -0
- package/src/lib/jobs/ai-job-page-sections.d.ts +37 -1
- package/src/lib/jobs/ai-job-page-sections.js +93 -11
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.d.ts +4 -4
- package/src/lib/jobs/ai-job-page-step.js +115 -14
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +20 -6
- package/src/lib/jobs/ai-job-plan-step.js +62 -22
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +163 -0
- package/src/lib/jobs/ai-job-site-content.js +443 -0
- package/src/lib/jobs/ai-job-site-content.js.map +1 -0
- package/src/lib/jobs/ai-job-site-look.d.ts +112 -0
- package/src/lib/jobs/ai-job-site-look.js +322 -0
- package/src/lib/jobs/ai-job-site-look.js.map +1 -0
- package/src/lib/jobs/ai-job-site-step.d.ts +30 -5
- package/src/lib/jobs/ai-job-site-step.js +183 -27
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.js +2 -2
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +6 -1
- package/src/lib/jobs/ai-jobs-notify.js +15 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +1 -1
- package/src/lib/jobs/ai-jobs.js +17 -8
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-layout-full-height.d.ts +40 -0
- package/src/lib/jobs/ai-layout-full-height.js +82 -0
- package/src/lib/jobs/ai-layout-full-height.js.map +1 -0
- package/src/lib/jobs/ai-layout-site-facts.d.ts +54 -0
- package/src/lib/jobs/ai-layout-site-facts.js +153 -0
- package/src/lib/jobs/ai-layout-site-facts.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +154 -0
- package/src/lib/layout-language/ai-layout-compiler.js +1235 -0
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -0
- package/src/lib/layout-language/ai-layout-copy.d.ts +73 -0
- package/src/lib/layout-language/ai-layout-copy.js +137 -0
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -0
- package/src/lib/layout-language/ai-layout-frame.d.ts +52 -0
- package/src/lib/layout-language/ai-layout-frame.js +458 -0
- package/src/lib/layout-language/ai-layout-frame.js.map +1 -0
- package/src/lib/layout-language/ai-layout-gaps.d.ts +63 -0
- package/src/lib/layout-language/ai-layout-gaps.js +345 -0
- package/src/lib/layout-language/ai-layout-gaps.js.map +1 -0
- package/src/lib/layout-language/ai-layout-language.d.ts +168 -0
- package/src/lib/layout-language/ai-layout-language.js +529 -0
- package/src/lib/layout-language/ai-layout-language.js.map +1 -0
- package/src/lib/layout-language/ai-layout-links.d.ts +80 -0
- package/src/lib/layout-language/ai-layout-links.js +109 -0
- package/src/lib/layout-language/ai-layout-links.js.map +1 -0
- package/src/lib/layout-language/ai-layout-pictures.d.ts +144 -0
- package/src/lib/layout-language/ai-layout-pictures.js +214 -0
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -0
- package/src/lib/layout-language/ai-layout-store.d.ts +41 -0
- package/src/lib/layout-language/ai-layout-store.js +75 -0
- package/src/lib/layout-language/ai-layout-store.js.map +1 -0
- package/src/lib/layout-language/ai-layout-tree.d.ts +59 -0
- package/src/lib/layout-language/ai-layout-tree.js +84 -0
- package/src/lib/layout-language/ai-layout-tree.js.map +1 -0
- package/src/lib/lazy-widget.d.ts +45 -0
- package/src/lib/lazy-widget.js +53 -0
- package/src/lib/lazy-widget.js.map +1 -0
- package/src/lib/model/ai-build-job.js +2 -1
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +20 -3
- package/src/lib/model/ai-build-progress.js +52 -7
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-edit-job.d.ts +109 -0
- package/src/lib/model/ai-edit-job.js +157 -0
- package/src/lib/model/ai-edit-job.js.map +1 -0
- package/src/lib/model/ai-host-publish-context.d.ts +51 -0
- package/src/lib/model/ai-host-publish-context.js +104 -0
- package/src/lib/model/ai-host-publish-context.js.map +1 -0
- package/src/lib/model/ai-job-activity.d.ts +22 -0
- package/src/lib/model/ai-job-activity.js +23 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +38 -1
- package/src/lib/model/ai-job-failure-copy.js +56 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -1
- package/src/lib/model/ai-job-notice.js +31 -9
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +22 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +18 -1
- package/src/lib/model/ai-logic-job.js +24 -3
- package/src/lib/model/ai-logic-job.js.map +1 -1
- package/src/lib/model/ai-media-credits.d.ts +8 -4
- package/src/lib/model/ai-media-credits.js +11 -4
- package/src/lib/model/ai-media-credits.js.map +1 -1
- package/src/lib/model/ai-media-kinds.d.ts +68 -0
- package/src/lib/model/ai-media-kinds.js +98 -0
- package/src/lib/model/ai-media-kinds.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +9 -1
- package/src/lib/model/ai-site-build-progress.js +54 -14
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +111 -0
- package/src/lib/model/ai-site-context.js +222 -0
- package/src/lib/model/ai-site-context.js.map +1 -0
- package/src/lib/model/ai-site-job.d.ts +18 -2
- package/src/lib/model/ai-site-job.js +21 -5
- package/src/lib/model/ai-site-job.js.map +1 -1
- package/src/lib/model/ai-site-kinds.d.ts +164 -0
- package/src/lib/model/ai-site-kinds.js +1942 -0
- package/src/lib/model/ai-site-kinds.js.map +1 -0
- package/src/lib/model/ai-site-look.d.ts +150 -0
- package/src/lib/model/ai-site-look.js +1236 -0
- package/src/lib/model/ai-site-look.js.map +1 -0
- package/src/lib/model/ai-site-memory.d.ts +80 -0
- package/src/lib/model/ai-site-memory.js +171 -0
- package/src/lib/model/ai-site-memory.js.map +1 -0
- package/src/lib/model/ai-site-start-seo.d.ts +1 -1
- package/src/lib/model/ai-site-start.d.ts +15 -3
- package/src/lib/model/ai-site-start.js +21 -4
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/assist-follow-up.d.ts +92 -0
- package/src/lib/model/assist-follow-up.js +128 -0
- package/src/lib/model/assist-follow-up.js.map +1 -0
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +99 -48
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.d.ts +9 -1
- package/src/lib/providers/anthropic.js +143 -9
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +38 -8
- package/src/lib/providers/catalog.js +106 -14
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +29 -0
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +22 -1
- package/src/lib/providers/image-contract.js +40 -1
- package/src/lib/providers/image-contract.js.map +1 -1
- package/src/lib/providers/image-endpoints.d.ts +24 -0
- package/src/lib/providers/image-endpoints.js +28 -0
- package/src/lib/providers/image-endpoints.js.map +1 -0
- package/src/lib/providers/model-choice.js +23 -2
- package/src/lib/providers/model-choice.js.map +1 -1
- package/src/lib/providers/routing.js +18 -0
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/providers/vertex-image-endpoint.d.ts +48 -0
- package/src/lib/providers/vertex-image-endpoint.js +80 -0
- package/src/lib/providers/vertex-image-endpoint.js.map +1 -0
- package/src/lib/providers/vertex-image.d.ts +13 -27
- package/src/lib/providers/vertex-image.js +15 -42
- package/src/lib/providers/vertex-image.js.map +1 -1
- package/src/lib/runtime/ai-blog-post-generation.d.ts +62 -0
- package/src/lib/runtime/ai-blog-post-generation.js +89 -0
- package/src/lib/runtime/ai-blog-post-generation.js.map +1 -0
- package/src/lib/runtime/ai-dev-env.d.ts +53 -0
- package/src/lib/runtime/ai-dev-env.js +72 -0
- package/src/lib/runtime/ai-dev-env.js.map +1 -0
- package/src/lib/runtime/ai-dev-replay.d.ts +116 -0
- package/src/lib/runtime/ai-dev-replay.js +313 -0
- package/src/lib/runtime/ai-dev-replay.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +82 -0
- package/src/lib/runtime/ai-doctrine-validators.js +436 -4
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.js +16 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-eval-live.js +4 -0
- package/src/lib/runtime/ai-eval-live.js.map +1 -1
- package/src/lib/runtime/ai-eval.d.ts +13 -1
- package/src/lib/runtime/ai-eval.js +84 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +257 -0
- package/src/lib/runtime/ai-icon-library.js +254 -0
- package/src/lib/runtime/ai-icon-library.js.map +1 -0
- package/src/lib/runtime/ai-live-batch.d.ts +45 -0
- package/src/lib/runtime/ai-live-batch.js +89 -0
- package/src/lib/runtime/ai-live-batch.js.map +1 -0
- package/src/lib/runtime/ai-node-tree.d.ts +9 -0
- package/src/lib/runtime/ai-node-tree.js +30 -4
- package/src/lib/runtime/ai-node-tree.js.map +1 -1
- package/src/lib/runtime/ai-palette.d.ts +6 -0
- package/src/lib/runtime/ai-palette.generated.js +70 -0
- package/src/lib/runtime/ai-palette.generated.js.map +1 -1
- package/src/lib/runtime/ai-palette.js.map +1 -1
- package/src/lib/runtime/ai-runtime.js +46 -2
- package/src/lib/runtime/ai-runtime.js.map +1 -1
- package/src/lib/runtime/site-context.d.ts +58 -0
- package/src/lib/runtime/site-context.js +127 -0
- package/src/lib/runtime/site-context.js.map +1 -0
- package/src/lib/server/ai-admin-conversations.d.ts +22 -0
- package/src/lib/server/ai-admin-conversations.js +197 -0
- package/src/lib/server/ai-admin-conversations.js.map +1 -0
- package/src/lib/server/ai-media-image.d.ts +5 -5
- package/src/lib/server/ai-media-image.js +25 -11
- package/src/lib/server/ai-media-image.js.map +1 -1
- package/src/lib/server/ai-media-raster-prompt.d.ts +34 -0
- package/src/lib/server/ai-media-raster-prompt.js +53 -0
- package/src/lib/server/ai-media-raster-prompt.js.map +1 -0
- package/src/lib/server/assist-chat.d.ts +11 -0
- package/src/lib/server/assist-chat.js +57 -18
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit-applied.js +16 -3
- package/src/lib/server/assist-edit-applied.js.map +1 -1
- package/src/lib/server/assist-view-context.d.ts +19 -1
- package/src/lib/server/assist-view-context.js +38 -1
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +10 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/subprocessors.js +17 -1
- package/src/lib/subprocessors.js.map +1 -1
- package/src/lib/tools/ai-blog-post-tool.d.ts +60 -0
- package/src/lib/tools/ai-blog-post-tool.js +175 -0
- package/src/lib/tools/ai-blog-post-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +6 -2
- package/src/lib/tools/ai-workflow-tool.js +16 -14
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +3 -0
- package/src/lib/usage/assist-usage.js +4 -2
- package/src/lib/usage/assist-usage.js.map +1 -1
- package/src/lib/usage/staff-org-ai-conversations.d.ts +103 -0
- package/src/lib/usage/staff-org-ai-conversations.js +152 -0
- package/src/lib/usage/staff-org-ai-conversations.js.map +1 -0
|
@@ -294,6 +294,7 @@ export function BillingAssistOverageControls({ orgId, canManage }) {
|
|
|
294
294
|
spacing: 2,
|
|
295
295
|
children: [
|
|
296
296
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
297
|
+
useFlexGap: true,
|
|
297
298
|
direction: "row",
|
|
298
299
|
spacing: 1,
|
|
299
300
|
sx: {
|
|
@@ -378,6 +379,7 @@ export function BillingAssistOverageControls({ orgId, canManage }) {
|
|
|
378
379
|
children: offersCeiling ? capUsd === null ? 'Or keep going, but only so far: set a monthly ceiling ' + 'on the extra credits you will pay for. The assistant ' + 'stops for the rest of the month once this month’s ' + 'overage reaches it. Optional, and separate from the ' + 'switch above.' : `The assistant stops for the rest of the month once ` + `this month’s overage reaches $${capUsd.toFixed(2)}. ` + `Raise, lower or remove the ceiling at any time.` : `A ceiling of $${(capUsd != null ? capUsd : 0).toFixed(2)} is set, but ` + `with the assistant stopping at the included band there ` + `is no overage for it to bound. You can safely remove it.`
|
|
379
380
|
}),
|
|
380
381
|
/*#__PURE__*/ _jsxs(Stack, {
|
|
382
|
+
useFlexGap: true,
|
|
381
383
|
direction: "row",
|
|
382
384
|
spacing: 2,
|
|
383
385
|
sx: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/billing-assist-overage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { CardDisplay, useConfirmationContext, useLoading } from '@aglyn/shared-ui-jsx'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n FormControlLabel,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { aiAddonName } from '@aglyn/aglyn'\nimport {\n ASSIST_OVERAGE_CAP_CONTROL_LABEL,\n ASSIST_OVERAGE_CAP_MAX_USD,\n ASSIST_OVERAGE_CAP_MIN_USD,\n} from '../usage/assist-credits'\n\n/** The `get` payload of `/api/billing/assist-overage`. */\ninterface AssistOverageState {\n /** Whether the org asked to be stopped at its band. Absent by default. */\n hardCap: boolean\n /** The org's ceiling on this month's overage in USD, or `null` for none. */\n capUsd: number | null\n /** The plan's included credits a month, or `null` where it sells none. */\n bandCredits: number | null\n /** The per-1,000 rate credits past the band bill at, or `null`. */\n overageRateUsdPer1k: number | null\n /** True when there is a band AND a rate to sell past it at. */\n sellsOverage: boolean\n /** The switch's label, served so the refusal and the switch share it. */\n label: string\n /** The ceiling's label, served for the same reason. */\n capLabel: string\n /** The range the route accepts for the ceiling. */\n minCapUsd: number\n maxCapUsd: number\n}\n\nexport interface BillingAssistOverageCardProps {\n orgId?: string | null\n /** billing.manage: the switch acts; view-only otherwise. */\n canManage: boolean\n}\n\n/**\n * The org's own controls on AI assist spend: the hard cap at the band\n * (AGL-2653) and the dollar ceiling on overage (AGL-2898).\n *\n * ## What this card is\n *\n * Credits past a plan's included assist band are SOLD by default, at the\n * plan's per-1,000 rate, the way storage past its band bills by default. This\n * card offers the two controls a customer has over that: a switch that makes\n * the band a wall, so the assistant refuses there and nothing past it is ever\n * billed; and, with the switch off, a monthly ceiling in dollars on the\n * overage the org will buy before the assistant stops. Both are optional and\n * unset unless chosen — the storage cap's siblings, for the same reason a\n * ceiling on metered spend is the END USER's control, offered rather than\n * imposed.\n *\n * ## Why a switch AND a number\n *\n * The switch answers \"sell past the band at all?\" and needs no figure: the\n * band is the plan's, and a second band here would drift from it. The\n * ceiling answers \"and if so, how much?\", which is a figure only the customer\n * can name — it is a bound on THEIR invoice, not a quantity the plan sold.\n * The ceiling is offered only while the switch is off, because past a wall\n * there is no overage to bound.\n *\n * ## Why the switch is edited here and enforced there\n *\n * The route owns every refusal: a plan that sells no overage, a value that\n * is not a boolean, a missing permission. This card sends the boolean and\n * renders the route's own answer rather than pre-validating against constants\n * that would then be a second source of truth. The rate it quotes arrives on\n * the same payload, so what the card says the overage costs is what the\n * invoice bills.\n *\n * ## Why turning it OFF is confirmed and turning it ON is not\n *\n * Off is the direction that can raise a bill, so it asks; on never raises\n * one, so it is a plain click. Off is also always available, on any plan and\n * from any state — an org that wants the sale back must not have to argue\n * with a precondition.\n */\n/**\n * The card as the Billing → Usage page's plugin band mounts it (AGL-2939):\n * the overage controls under the heading and help the page used to draw\n * around them.\n */\nexport default function BillingAssistOverageCard(props: BillingAssistOverageCardProps) {\n return (\n <div id=\"assist-overage\">\n <CardDisplay\n header={'AI credits overage'}\n subheader={\n 'Extra AI credits past your included band are billed on your ' +\n 'monthly invoice. Stop at the band, or stop once the overage ' +\n 'reaches an amount you choose.'\n }\n help={pluginDocsHelp('billing', {\n anchor: '#assist-overage',\n excerpt:\n 'On a paid plan the assistant keeps answering past your ' +\n 'included credits and the extra is billed at your plan\\'s ' +\n 'per-1,000 rate, unless you switch on the stop at the included band.',\n })}\n contentGutterX\n contentGutterY\n >\n <BillingAssistOverageControls {...props} />\n </CardDisplay>\n </div>\n )\n}\n\nexport function BillingAssistOverageControls({\n orgId,\n canManage,\n}: BillingAssistOverageCardProps) {\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const { confirm } = useConfirmationContext()\n const [state, setState] = useState<AssistOverageState | null>(null)\n const [busy, setBusy] = useState(false)\n /** The ceiling field, as typed; the route validates, this only carries. */\n const [capInput, setCapInput] = useState('')\n /**\n * Three outcomes, not two (the AGL-1380 rule). \"The assistant keeps going\n * past your band\" is a claim about this org's billing settings; rendering\n * it because a request FAILED would tell an org that asked to be stopped\n * that it had not.\n */\n const [loadState, setLoadState] = useState<'pending' | 'error' | 'loaded'>(\n 'pending',\n )\n /** Bumped by Retry, and after every write, to re-run the load effect. */\n const [retryNonce, setRetryNonce] = useState(0)\n\n const sendRequest = useCallback(\n async (\n body: Record<string, unknown>,\n ): Promise<{ ok: boolean; payload?: any }> => {\n const response = await authorizedFetch(\n user,\n '/api/ai/billing/overage',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, ...body }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n // The route's own sentence, verbatim: its 409 explains why a plan\n // with no rate has nothing to switch, which is more specific than\n // anything this card could invent.\n enqueueSnackbar(payload?.error ?? 'AI assist setting request failed', {\n variant: 'warning',\n persist: false,\n })\n return { ok: false, payload }\n }\n return { ok: true, payload }\n },\n [user, orgId, enqueueSnackbar],\n )\n\n useEffect(() => {\n if (!orgId || !user) return\n let cancelled = false\n setLoadState('pending')\n sendRequest({ action: 'get' })\n .then((outcome) => {\n if (cancelled) return\n if (!outcome.ok) return void setLoadState('error')\n const next = outcome.payload as AssistOverageState\n // A card that cannot name the price must not quote one. The rate\n // appears in the sentence beside the switch whenever overage is\n // sold, and a defaulted 0 would tell a person about to leave the\n // switch off that the extra costs \"$0.00 per 1,000\". A payload that\n // says overage is sold but cannot say at what is a LOAD FAILURE.\n if (\n typeof next?.hardCap !== 'boolean' ||\n typeof next?.sellsOverage !== 'boolean' ||\n (next.sellsOverage &&\n (!Number.isFinite(Number(next.overageRateUsdPer1k)) ||\n !Number.isFinite(Number(next.bandCredits))))\n ) {\n return void setLoadState('error')\n }\n // The ceiling half of the payload is read strictly, the way the\n // resolver reads the stored field: anything that is not a finite\n // number is NO ceiling, never a ceiling of `undefined` that renders\n // as \"$NaN\". The label and bounds fall back to the same constants the\n // route serves them from.\n const capUsd =\n typeof next.capUsd === 'number' && Number.isFinite(next.capUsd)\n ? next.capUsd\n : null\n setState({\n ...next,\n capUsd,\n capLabel:\n typeof next.capLabel === 'string' && next.capLabel\n ? next.capLabel\n : ASSIST_OVERAGE_CAP_CONTROL_LABEL,\n minCapUsd: Number.isFinite(Number(next.minCapUsd))\n ? Number(next.minCapUsd)\n : ASSIST_OVERAGE_CAP_MIN_USD,\n maxCapUsd: Number.isFinite(Number(next.maxCapUsd))\n ? Number(next.maxCapUsd)\n : ASSIST_OVERAGE_CAP_MAX_USD,\n })\n setCapInput(capUsd === null ? '' : String(capUsd))\n setLoadState('loaded')\n })\n // A rejected fetch — offline, a wedged route — never reached the\n // snackbar inside the request, so this is the only place it can become\n // something the card renders.\n .catch(() => {\n if (!cancelled) setLoadState('error')\n })\n return () => {\n cancelled = true\n }\n }, [orgId, user, sendRequest, retryNonce])\n\n const setHardCap = useCallback(\n async (hardCap: boolean) => {\n const rate = state?.overageRateUsdPer1k\n if (!hardCap) {\n // The loading overlay must drop BEFORE the confirm dialog opens — it\n // sits above the dialog and swallows the Confirm click (AGL-535). So\n // the confirm runs first and `queueLoading` only wraps the write.\n const accepted = await confirm({\n title: 'Keep AI assist going past your included credits?',\n description:\n 'The assistant will keep answering once your included credits ' +\n 'are used, and the extra credits will be billed on your monthly ' +\n `invoice${\n rate != null ? ` at $${rate.toFixed(2)} per 1,000` : ''\n }. You can turn the stop back on at any time.`,\n confirmationText: 'Keep going',\n })\n .then(() => true)\n .catch(() => false)\n if (!accepted) return\n }\n\n setBusy(true)\n const dequeue = queueLoading()\n try {\n const outcome = await sendRequest({ action: 'setHardCap', hardCap })\n if (!outcome.ok) return\n enqueueSnackbar(\n hardCap\n ? 'AI assist will stop at your included credits. Nothing past ' +\n 'the band is billed.'\n : 'AI assist keeps going past your included credits' +\n (rate != null\n ? `, billed at $${rate.toFixed(2)} per 1,000.`\n : '.'),\n { variant: 'success', persist: false },\n )\n setRetryNonce((nonce) => nonce + 1)\n } catch {\n enqueueSnackbar('Could not update your AI assist setting', {\n variant: 'warning',\n persist: false,\n })\n } finally {\n dequeue()\n setBusy(false)\n }\n },\n [state, confirm, queueLoading, sendRequest, enqueueSnackbar],\n )\n\n /**\n * Set or clear the ceiling. Neither direction asks first: setting one\n * only ever lowers a bill, and clearing one returns the org to the\n * open-ended overage the switch already told it about. The route owns the\n * range check, so a figure it refuses comes back as its own sentence.\n */\n const setCap = useCallback(\n async (capUsd: number | null) => {\n setBusy(true)\n const dequeue = queueLoading()\n try {\n const outcome = await sendRequest({ action: 'setCap', capUsd })\n if (!outcome.ok) return\n enqueueSnackbar(\n capUsd === null\n ? 'AI assist overage ceiling removed. Extra credits keep billing ' +\n 'at your plan’s rate with no monthly stop.'\n : `AI assist will stop once this month’s overage reaches ` +\n `$${capUsd.toFixed(2)}.`,\n { variant: 'success', persist: false },\n )\n setRetryNonce((nonce) => nonce + 1)\n } catch {\n enqueueSnackbar('Could not update your AI assist ceiling', {\n variant: 'warning',\n persist: false,\n })\n } finally {\n dequeue()\n setBusy(false)\n }\n },\n [queueLoading, sendRequest, enqueueSnackbar],\n )\n\n if (loadState === 'pending') {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading your AI assist settings…\n </Typography>\n )\n }\n\n if (loadState === 'error' || !state) {\n return (\n <Alert\n severity=\"warning\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() => setRetryNonce((nonce) => nonce + 1)}\n >\n Retry\n </Button>\n }\n >\n We couldn’t load your AI assist settings. Nothing has changed.\n </Alert>\n )\n }\n\n const {\n hardCap,\n capUsd,\n bandCredits,\n overageRateUsdPer1k,\n sellsOverage,\n label,\n capLabel,\n minCapUsd,\n maxCapUsd,\n } = state\n const band = bandCredits == null ? null : Math.round(bandCredits)\n const rate = overageRateUsdPer1k == null ? null : overageRateUsdPer1k.toFixed(2)\n // The ceiling is a bound on overage, and overage exists only while the\n // switch is off on a plan that sells past its band. It stays reachable to\n // CLEAR whenever one is set, so an org that turned the switch on afterwards\n // is not left carrying a figure it cannot remove.\n const offersCeiling = sellsOverage && !hardCap\n const showsCeiling = offersCeiling || capUsd !== null\n\n // A plan that sells no overage has nothing for the switch to stop, so\n // there is nothing to configure. Say that plainly rather than rendering a\n // switch the route would refuse with a 409 — but keep the switch reachable\n // for an org that turned it on and then moved to a plan that sells none,\n // so it can turn it off.\n const nothingToStop = !sellsOverage && !hardCap\n\n return (\n <Stack spacing={2}>\n <Stack direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', gap: 1 }}>\n <Chip\n label={\n hardCap\n ? 'Stops at the included band'\n : nothingToStop\n ? band === null\n ? 'No AI assist credits'\n : 'Included credits only'\n : capUsd !== null\n ? `Stops at $${capUsd.toFixed(2)} of overage`\n : 'No stop — extra credits bill'\n }\n size=\"small\"\n color={hardCap || capUsd !== null ? 'primary' : 'default'}\n />\n {band !== null ? (\n <Chip label={`${band.toLocaleString()} credits/mo included`} size=\"small\" />\n ) : null}\n </Stack>\n\n {nothingToStop && band === null ? (\n // No band at all, and nothing ever billed for AI, so neither stop\n // applies — the switch has no band to stop at and the ceiling has no\n // overage to bound. Since AGL-3203 every plan row carries a band, so\n // this is an org whose band was overridden to zero.\n <Alert severity=\"info\">\n Your plan includes no AI credits and is never charged for any, so\n there is no band to stop at, no overage to put a dollar ceiling on,\n and nothing to configure here. Upgrade above, or add{' '}\n {aiAddonName()} on a paid plan, to get a band.\n </Alert>\n ) : nothingToStop ? (\n // Enterprise: a band, and no rate to sell past it at. The assistant\n // stops at the band on its own and nothing is ever billed for it.\n <Alert severity=\"info\">\n Your plan includes <strong>{band?.toLocaleString()}</strong> AI assist\n credits a month and sells none past them: the assistant stops at the\n included band and you are <strong>never charged</strong> for AI\n assist, so there is nothing to switch.\n </Alert>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {sellsOverage\n ? `Your plan includes ${band?.toLocaleString()} AI assist credits ` +\n `a month. Past that, the assistant keeps answering and the ` +\n `extra credits are billed at $${rate} per 1,000 on your ` +\n `monthly invoice.`\n : `Your plan no longer sells AI assist credits past its band, ` +\n `so this switch is not doing anything. You can safely turn ` +\n `it off.`}\n </Typography>\n {sellsOverage ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n If you would rather the assistant stopped at the included band,\n turn this on. This is optional — most people leave it off.\n </Typography>\n ) : null}\n <Stack spacing={0.5}>\n <FormControlLabel\n control={\n <Switch\n checked={hardCap}\n disabled={!canManage || busy || (!hardCap && !sellsOverage)}\n onChange={(event) => void setHardCap(event.target.checked)}\n />\n }\n label={label}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {hardCap\n ? `On: the assistant stops once your included credits are ` +\n `used, and you are never billed for AI assist.` +\n (sellsOverage\n ? ` Turn it off to keep going at $${rate} per 1,000 credits.`\n : '')\n : `Off: extra credits past the included band are billed at ` +\n `$${rate} per 1,000.`}\n </Typography>\n </Stack>\n\n {showsCeiling ? (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {offersCeiling\n ? capUsd === null\n ? 'Or keep going, but only so far: set a monthly ceiling ' +\n 'on the extra credits you will pay for. The assistant ' +\n 'stops for the rest of the month once this month’s ' +\n 'overage reaches it. Optional, and separate from the ' +\n 'switch above.'\n : `The assistant stops for the rest of the month once ` +\n `this month’s overage reaches $${capUsd.toFixed(2)}. ` +\n `Raise, lower or remove the ceiling at any time.`\n : `A ceiling of $${(capUsd ?? 0).toFixed(2)} is set, but ` +\n `with the assistant stopping at the included band there ` +\n `is no overage for it to bound. You can safely remove it.`}\n </Typography>\n <Stack\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'flex-start', flexWrap: 'wrap', rowGap: 2 }}\n >\n {offersCeiling ? (\n <>\n <TextField\n label={capLabel}\n type=\"number\"\n size=\"small\"\n value={capInput}\n disabled={!canManage || busy}\n onChange={(event) => setCapInput(event.target.value)}\n slotProps={{\n htmlInput: { min: minCapUsd, max: maxCapUsd, step: 1 },\n }}\n helperText={`In USD, between $${minCapUsd} and $${maxCapUsd.toLocaleString()}.`}\n />\n <Button\n variant=\"outlined\"\n disabled={!canManage || busy || capInput.trim() === ''}\n onClick={() => void setCap(Number(capInput))}\n >\n {capUsd === null ? 'Set a monthly ceiling' : 'Save ceiling'}\n </Button>\n </>\n ) : null}\n {capUsd !== null ? (\n <Button\n variant=\"text\"\n color=\"warning\"\n disabled={!canManage || busy}\n onClick={() => void setCap(null)}\n >\n Remove ceiling\n </Button>\n ) : null}\n </Stack>\n </Stack>\n ) : null}\n </>\n )}\n\n {!canManage ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n You need the Manage billing permission to change this.\n </Typography>\n ) : null}\n </Stack>\n )\n}\n"],"names":["CardDisplay","useConfirmationContext","useLoading","pluginDocsHelp","useSnackbar","Alert","Button","Chip","FormControlLabel","Stack","Switch","TextField","Typography","useCallback","useEffect","useState","useUser","authorizedFetch","aiAddonName","ASSIST_OVERAGE_CAP_CONTROL_LABEL","ASSIST_OVERAGE_CAP_MAX_USD","ASSIST_OVERAGE_CAP_MIN_USD","BillingAssistOverageCard","props","div","id","header","subheader","help","anchor","excerpt","contentGutterX","contentGutterY","BillingAssistOverageControls","orgId","canManage","data","user","enqueueSnackbar","queueLoading","confirm","state","setState","busy","setBusy","capInput","setCapInput","loadState","setLoadState","retryNonce","setRetryNonce","sendRequest","body","response","method","headers","JSON","stringify","payload","json","catch","ok","error","variant","persist","cancelled","action","then","outcome","next","hardCap","sellsOverage","Number","isFinite","overageRateUsdPer1k","bandCredits","capUsd","capLabel","minCapUsd","maxCapUsd","String","setHardCap","rate","accepted","title","description","toFixed","confirmationText","dequeue","nonce","setCap","color","severity","size","onClick","label","band","Math","round","offersCeiling","showsCeiling","nothingToStop","spacing","direction","sx","flexWrap","gap","toLocaleString","strong","control","checked","disabled","onChange","event","target","alignItems","rowGap","type","value","slotProps","htmlInput","min","max","step","helperText","trim"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/billing-assist-overage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { CardDisplay, useConfirmationContext, useLoading } from '@aglyn/shared-ui-jsx'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n Alert,\n Button,\n Chip,\n FormControlLabel,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { useCallback, useEffect, useState } from 'react'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { aiAddonName } from '@aglyn/aglyn'\nimport {\n ASSIST_OVERAGE_CAP_CONTROL_LABEL,\n ASSIST_OVERAGE_CAP_MAX_USD,\n ASSIST_OVERAGE_CAP_MIN_USD,\n} from '../usage/assist-credits'\n\n/** The `get` payload of `/api/billing/assist-overage`. */\ninterface AssistOverageState {\n /** Whether the org asked to be stopped at its band. Absent by default. */\n hardCap: boolean\n /** The org's ceiling on this month's overage in USD, or `null` for none. */\n capUsd: number | null\n /** The plan's included credits a month, or `null` where it sells none. */\n bandCredits: number | null\n /** The per-1,000 rate credits past the band bill at, or `null`. */\n overageRateUsdPer1k: number | null\n /** True when there is a band AND a rate to sell past it at. */\n sellsOverage: boolean\n /** The switch's label, served so the refusal and the switch share it. */\n label: string\n /** The ceiling's label, served for the same reason. */\n capLabel: string\n /** The range the route accepts for the ceiling. */\n minCapUsd: number\n maxCapUsd: number\n}\n\nexport interface BillingAssistOverageCardProps {\n orgId?: string | null\n /** billing.manage: the switch acts; view-only otherwise. */\n canManage: boolean\n}\n\n/**\n * The org's own controls on AI assist spend: the hard cap at the band\n * (AGL-2653) and the dollar ceiling on overage (AGL-2898).\n *\n * ## What this card is\n *\n * Credits past a plan's included assist band are SOLD by default, at the\n * plan's per-1,000 rate, the way storage past its band bills by default. This\n * card offers the two controls a customer has over that: a switch that makes\n * the band a wall, so the assistant refuses there and nothing past it is ever\n * billed; and, with the switch off, a monthly ceiling in dollars on the\n * overage the org will buy before the assistant stops. Both are optional and\n * unset unless chosen — the storage cap's siblings, for the same reason a\n * ceiling on metered spend is the END USER's control, offered rather than\n * imposed.\n *\n * ## Why a switch AND a number\n *\n * The switch answers \"sell past the band at all?\" and needs no figure: the\n * band is the plan's, and a second band here would drift from it. The\n * ceiling answers \"and if so, how much?\", which is a figure only the customer\n * can name — it is a bound on THEIR invoice, not a quantity the plan sold.\n * The ceiling is offered only while the switch is off, because past a wall\n * there is no overage to bound.\n *\n * ## Why the switch is edited here and enforced there\n *\n * The route owns every refusal: a plan that sells no overage, a value that\n * is not a boolean, a missing permission. This card sends the boolean and\n * renders the route's own answer rather than pre-validating against constants\n * that would then be a second source of truth. The rate it quotes arrives on\n * the same payload, so what the card says the overage costs is what the\n * invoice bills.\n *\n * ## Why turning it OFF is confirmed and turning it ON is not\n *\n * Off is the direction that can raise a bill, so it asks; on never raises\n * one, so it is a plain click. Off is also always available, on any plan and\n * from any state — an org that wants the sale back must not have to argue\n * with a precondition.\n */\n/**\n * The card as the Billing → Usage page's plugin band mounts it (AGL-2939):\n * the overage controls under the heading and help the page used to draw\n * around them.\n */\nexport default function BillingAssistOverageCard(props: BillingAssistOverageCardProps) {\n return (\n <div id=\"assist-overage\">\n <CardDisplay\n header={'AI credits overage'}\n subheader={\n 'Extra AI credits past your included band are billed on your ' +\n 'monthly invoice. Stop at the band, or stop once the overage ' +\n 'reaches an amount you choose.'\n }\n help={pluginDocsHelp('billing', {\n anchor: '#assist-overage',\n excerpt:\n 'On a paid plan the assistant keeps answering past your ' +\n 'included credits and the extra is billed at your plan\\'s ' +\n 'per-1,000 rate, unless you switch on the stop at the included band.',\n })}\n contentGutterX\n contentGutterY\n >\n <BillingAssistOverageControls {...props} />\n </CardDisplay>\n </div>\n )\n}\n\nexport function BillingAssistOverageControls({\n orgId,\n canManage,\n}: BillingAssistOverageCardProps) {\n const { data: user } = useUser()\n const { enqueueSnackbar } = useSnackbar()\n const { queueLoading } = useLoading()\n const { confirm } = useConfirmationContext()\n const [state, setState] = useState<AssistOverageState | null>(null)\n const [busy, setBusy] = useState(false)\n /** The ceiling field, as typed; the route validates, this only carries. */\n const [capInput, setCapInput] = useState('')\n /**\n * Three outcomes, not two (the AGL-1380 rule). \"The assistant keeps going\n * past your band\" is a claim about this org's billing settings; rendering\n * it because a request FAILED would tell an org that asked to be stopped\n * that it had not.\n */\n const [loadState, setLoadState] = useState<'pending' | 'error' | 'loaded'>(\n 'pending',\n )\n /** Bumped by Retry, and after every write, to re-run the load effect. */\n const [retryNonce, setRetryNonce] = useState(0)\n\n const sendRequest = useCallback(\n async (\n body: Record<string, unknown>,\n ): Promise<{ ok: boolean; payload?: any }> => {\n const response = await authorizedFetch(\n user,\n '/api/ai/billing/overage',\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ orgId, ...body }),\n },\n )\n const payload = await response.json().catch(() => ({}))\n if (!response.ok) {\n // The route's own sentence, verbatim: its 409 explains why a plan\n // with no rate has nothing to switch, which is more specific than\n // anything this card could invent.\n enqueueSnackbar(payload?.error ?? 'AI assist setting request failed', {\n variant: 'warning',\n persist: false,\n })\n return { ok: false, payload }\n }\n return { ok: true, payload }\n },\n [user, orgId, enqueueSnackbar],\n )\n\n useEffect(() => {\n if (!orgId || !user) return\n let cancelled = false\n setLoadState('pending')\n sendRequest({ action: 'get' })\n .then((outcome) => {\n if (cancelled) return\n if (!outcome.ok) return void setLoadState('error')\n const next = outcome.payload as AssistOverageState\n // A card that cannot name the price must not quote one. The rate\n // appears in the sentence beside the switch whenever overage is\n // sold, and a defaulted 0 would tell a person about to leave the\n // switch off that the extra costs \"$0.00 per 1,000\". A payload that\n // says overage is sold but cannot say at what is a LOAD FAILURE.\n if (\n typeof next?.hardCap !== 'boolean' ||\n typeof next?.sellsOverage !== 'boolean' ||\n (next.sellsOverage &&\n (!Number.isFinite(Number(next.overageRateUsdPer1k)) ||\n !Number.isFinite(Number(next.bandCredits))))\n ) {\n return void setLoadState('error')\n }\n // The ceiling half of the payload is read strictly, the way the\n // resolver reads the stored field: anything that is not a finite\n // number is NO ceiling, never a ceiling of `undefined` that renders\n // as \"$NaN\". The label and bounds fall back to the same constants the\n // route serves them from.\n const capUsd =\n typeof next.capUsd === 'number' && Number.isFinite(next.capUsd)\n ? next.capUsd\n : null\n setState({\n ...next,\n capUsd,\n capLabel:\n typeof next.capLabel === 'string' && next.capLabel\n ? next.capLabel\n : ASSIST_OVERAGE_CAP_CONTROL_LABEL,\n minCapUsd: Number.isFinite(Number(next.minCapUsd))\n ? Number(next.minCapUsd)\n : ASSIST_OVERAGE_CAP_MIN_USD,\n maxCapUsd: Number.isFinite(Number(next.maxCapUsd))\n ? Number(next.maxCapUsd)\n : ASSIST_OVERAGE_CAP_MAX_USD,\n })\n setCapInput(capUsd === null ? '' : String(capUsd))\n setLoadState('loaded')\n })\n // A rejected fetch — offline, a wedged route — never reached the\n // snackbar inside the request, so this is the only place it can become\n // something the card renders.\n .catch(() => {\n if (!cancelled) setLoadState('error')\n })\n return () => {\n cancelled = true\n }\n }, [orgId, user, sendRequest, retryNonce])\n\n const setHardCap = useCallback(\n async (hardCap: boolean) => {\n const rate = state?.overageRateUsdPer1k\n if (!hardCap) {\n // The loading overlay must drop BEFORE the confirm dialog opens — it\n // sits above the dialog and swallows the Confirm click (AGL-535). So\n // the confirm runs first and `queueLoading` only wraps the write.\n const accepted = await confirm({\n title: 'Keep AI assist going past your included credits?',\n description:\n 'The assistant will keep answering once your included credits ' +\n 'are used, and the extra credits will be billed on your monthly ' +\n `invoice${\n rate != null ? ` at $${rate.toFixed(2)} per 1,000` : ''\n }. You can turn the stop back on at any time.`,\n confirmationText: 'Keep going',\n })\n .then(() => true)\n .catch(() => false)\n if (!accepted) return\n }\n\n setBusy(true)\n const dequeue = queueLoading()\n try {\n const outcome = await sendRequest({ action: 'setHardCap', hardCap })\n if (!outcome.ok) return\n enqueueSnackbar(\n hardCap\n ? 'AI assist will stop at your included credits. Nothing past ' +\n 'the band is billed.'\n : 'AI assist keeps going past your included credits' +\n (rate != null\n ? `, billed at $${rate.toFixed(2)} per 1,000.`\n : '.'),\n { variant: 'success', persist: false },\n )\n setRetryNonce((nonce) => nonce + 1)\n } catch {\n enqueueSnackbar('Could not update your AI assist setting', {\n variant: 'warning',\n persist: false,\n })\n } finally {\n dequeue()\n setBusy(false)\n }\n },\n [state, confirm, queueLoading, sendRequest, enqueueSnackbar],\n )\n\n /**\n * Set or clear the ceiling. Neither direction asks first: setting one\n * only ever lowers a bill, and clearing one returns the org to the\n * open-ended overage the switch already told it about. The route owns the\n * range check, so a figure it refuses comes back as its own sentence.\n */\n const setCap = useCallback(\n async (capUsd: number | null) => {\n setBusy(true)\n const dequeue = queueLoading()\n try {\n const outcome = await sendRequest({ action: 'setCap', capUsd })\n if (!outcome.ok) return\n enqueueSnackbar(\n capUsd === null\n ? 'AI assist overage ceiling removed. Extra credits keep billing ' +\n 'at your plan’s rate with no monthly stop.'\n : `AI assist will stop once this month’s overage reaches ` +\n `$${capUsd.toFixed(2)}.`,\n { variant: 'success', persist: false },\n )\n setRetryNonce((nonce) => nonce + 1)\n } catch {\n enqueueSnackbar('Could not update your AI assist ceiling', {\n variant: 'warning',\n persist: false,\n })\n } finally {\n dequeue()\n setBusy(false)\n }\n },\n [queueLoading, sendRequest, enqueueSnackbar],\n )\n\n if (loadState === 'pending') {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading your AI assist settings…\n </Typography>\n )\n }\n\n if (loadState === 'error' || !state) {\n return (\n <Alert\n severity=\"warning\"\n action={\n <Button\n size=\"small\"\n color=\"inherit\"\n onClick={() => setRetryNonce((nonce) => nonce + 1)}\n >\n Retry\n </Button>\n }\n >\n We couldn’t load your AI assist settings. Nothing has changed.\n </Alert>\n )\n }\n\n const {\n hardCap,\n capUsd,\n bandCredits,\n overageRateUsdPer1k,\n sellsOverage,\n label,\n capLabel,\n minCapUsd,\n maxCapUsd,\n } = state\n const band = bandCredits == null ? null : Math.round(bandCredits)\n const rate = overageRateUsdPer1k == null ? null : overageRateUsdPer1k.toFixed(2)\n // The ceiling is a bound on overage, and overage exists only while the\n // switch is off on a plan that sells past its band. It stays reachable to\n // CLEAR whenever one is set, so an org that turned the switch on afterwards\n // is not left carrying a figure it cannot remove.\n const offersCeiling = sellsOverage && !hardCap\n const showsCeiling = offersCeiling || capUsd !== null\n\n // A plan that sells no overage has nothing for the switch to stop, so\n // there is nothing to configure. Say that plainly rather than rendering a\n // switch the route would refuse with a 409 — but keep the switch reachable\n // for an org that turned it on and then moved to a plan that sells none,\n // so it can turn it off.\n const nothingToStop = !sellsOverage && !hardCap\n\n return (\n <Stack spacing={2}>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ flexWrap: 'wrap', gap: 1 }}>\n <Chip\n label={\n hardCap\n ? 'Stops at the included band'\n : nothingToStop\n ? band === null\n ? 'No AI assist credits'\n : 'Included credits only'\n : capUsd !== null\n ? `Stops at $${capUsd.toFixed(2)} of overage`\n : 'No stop — extra credits bill'\n }\n size=\"small\"\n color={hardCap || capUsd !== null ? 'primary' : 'default'}\n />\n {band !== null ? (\n <Chip label={`${band.toLocaleString()} credits/mo included`} size=\"small\" />\n ) : null}\n </Stack>\n\n {nothingToStop && band === null ? (\n // No band at all, and nothing ever billed for AI, so neither stop\n // applies — the switch has no band to stop at and the ceiling has no\n // overage to bound. Since AGL-3203 every plan row carries a band, so\n // this is an org whose band was overridden to zero.\n <Alert severity=\"info\">\n Your plan includes no AI credits and is never charged for any, so\n there is no band to stop at, no overage to put a dollar ceiling on,\n and nothing to configure here. Upgrade above, or add{' '}\n {aiAddonName()} on a paid plan, to get a band.\n </Alert>\n ) : nothingToStop ? (\n // Enterprise: a band, and no rate to sell past it at. The assistant\n // stops at the band on its own and nothing is ever billed for it.\n <Alert severity=\"info\">\n Your plan includes <strong>{band?.toLocaleString()}</strong> AI assist\n credits a month and sells none past them: the assistant stops at the\n included band and you are <strong>never charged</strong> for AI\n assist, so there is nothing to switch.\n </Alert>\n ) : (\n <>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {sellsOverage\n ? `Your plan includes ${band?.toLocaleString()} AI assist credits ` +\n `a month. Past that, the assistant keeps answering and the ` +\n `extra credits are billed at $${rate} per 1,000 on your ` +\n `monthly invoice.`\n : `Your plan no longer sells AI assist credits past its band, ` +\n `so this switch is not doing anything. You can safely turn ` +\n `it off.`}\n </Typography>\n {sellsOverage ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n If you would rather the assistant stopped at the included band,\n turn this on. This is optional — most people leave it off.\n </Typography>\n ) : null}\n <Stack spacing={0.5}>\n <FormControlLabel\n control={\n <Switch\n checked={hardCap}\n disabled={!canManage || busy || (!hardCap && !sellsOverage)}\n onChange={(event) => void setHardCap(event.target.checked)}\n />\n }\n label={label}\n />\n <Typography variant=\"caption\" color=\"text.secondary\">\n {hardCap\n ? `On: the assistant stops once your included credits are ` +\n `used, and you are never billed for AI assist.` +\n (sellsOverage\n ? ` Turn it off to keep going at $${rate} per 1,000 credits.`\n : '')\n : `Off: extra credits past the included band are billed at ` +\n `$${rate} per 1,000.`}\n </Typography>\n </Stack>\n\n {showsCeiling ? (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {offersCeiling\n ? capUsd === null\n ? 'Or keep going, but only so far: set a monthly ceiling ' +\n 'on the extra credits you will pay for. The assistant ' +\n 'stops for the rest of the month once this month’s ' +\n 'overage reaches it. Optional, and separate from the ' +\n 'switch above.'\n : `The assistant stops for the rest of the month once ` +\n `this month’s overage reaches $${capUsd.toFixed(2)}. ` +\n `Raise, lower or remove the ceiling at any time.`\n : `A ceiling of $${(capUsd ?? 0).toFixed(2)} is set, but ` +\n `with the assistant stopping at the included band there ` +\n `is no overage for it to bound. You can safely remove it.`}\n </Typography>\n <Stack\n useFlexGap\n direction=\"row\"\n spacing={2}\n sx={{ alignItems: 'flex-start', flexWrap: 'wrap', rowGap: 2 }}\n >\n {offersCeiling ? (\n <>\n <TextField\n label={capLabel}\n type=\"number\"\n size=\"small\"\n value={capInput}\n disabled={!canManage || busy}\n onChange={(event) => setCapInput(event.target.value)}\n slotProps={{\n htmlInput: { min: minCapUsd, max: maxCapUsd, step: 1 },\n }}\n helperText={`In USD, between $${minCapUsd} and $${maxCapUsd.toLocaleString()}.`}\n />\n <Button\n variant=\"outlined\"\n disabled={!canManage || busy || capInput.trim() === ''}\n onClick={() => void setCap(Number(capInput))}\n >\n {capUsd === null ? 'Set a monthly ceiling' : 'Save ceiling'}\n </Button>\n </>\n ) : null}\n {capUsd !== null ? (\n <Button\n variant=\"text\"\n color=\"warning\"\n disabled={!canManage || busy}\n onClick={() => void setCap(null)}\n >\n Remove ceiling\n </Button>\n ) : null}\n </Stack>\n </Stack>\n ) : null}\n </>\n )}\n\n {!canManage ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n You need the Manage billing permission to change this.\n </Typography>\n ) : null}\n </Stack>\n )\n}\n"],"names":["CardDisplay","useConfirmationContext","useLoading","pluginDocsHelp","useSnackbar","Alert","Button","Chip","FormControlLabel","Stack","Switch","TextField","Typography","useCallback","useEffect","useState","useUser","authorizedFetch","aiAddonName","ASSIST_OVERAGE_CAP_CONTROL_LABEL","ASSIST_OVERAGE_CAP_MAX_USD","ASSIST_OVERAGE_CAP_MIN_USD","BillingAssistOverageCard","props","div","id","header","subheader","help","anchor","excerpt","contentGutterX","contentGutterY","BillingAssistOverageControls","orgId","canManage","data","user","enqueueSnackbar","queueLoading","confirm","state","setState","busy","setBusy","capInput","setCapInput","loadState","setLoadState","retryNonce","setRetryNonce","sendRequest","body","response","method","headers","JSON","stringify","payload","json","catch","ok","error","variant","persist","cancelled","action","then","outcome","next","hardCap","sellsOverage","Number","isFinite","overageRateUsdPer1k","bandCredits","capUsd","capLabel","minCapUsd","maxCapUsd","String","setHardCap","rate","accepted","title","description","toFixed","confirmationText","dequeue","nonce","setCap","color","severity","size","onClick","label","band","Math","round","offersCeiling","showsCeiling","nothingToStop","spacing","useFlexGap","direction","sx","flexWrap","gap","toLocaleString","strong","control","checked","disabled","onChange","event","target","alignItems","rowGap","type","value","slotProps","htmlInput","min","max","step","helperText","trim"],"mappings":"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"}
|
|
@@ -151,6 +151,16 @@ import { aiUsageMonthLabel, formatAiUsageShare } from "../usage/ai-usage-wire.js
|
|
|
151
151
|
children: [
|
|
152
152
|
/*#__PURE__*/ _jsxs(ScrollTable, {
|
|
153
153
|
size: "small",
|
|
154
|
+
// Below md the columns scroll sideways rather than folding each
|
|
155
|
+
// header onto several lines.
|
|
156
|
+
sx: {
|
|
157
|
+
'& th': {
|
|
158
|
+
whiteSpace: {
|
|
159
|
+
xs: 'nowrap',
|
|
160
|
+
md: 'normal'
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
154
164
|
children: [
|
|
155
165
|
/*#__PURE__*/ _jsx(TableHead, {
|
|
156
166
|
children: /*#__PURE__*/ _jsxs(TableRow, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/member-ai-usage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { aiUsageMonthKeys, topAiUsageKinds } from '../model/ai-usage-by-user'\nimport {\n aiUsageMonthLabel,\n formatAiUsageShare,\n type UserAiUsageMonthWire,\n type UserAiUsageWire,\n} from '../usage/ai-usage-wire'\n\nexport interface MemberAiUsageCardProps {\n orgId: string\n uid: string\n orgSlug?: string\n /** The org's sites, for naming the per-site split. */\n hosts?: ReadonlyArray<{ $id: string; displayName?: string; subdomain?: string }>\n}\n\n/**\n * ONE MEMBER'S AI USAGE (AGL-2928) — this month and last, on their page.\n *\n * Credits and the share of the workspace's pool, requests, the kinds they\n * mostly asked for, and the split by site — the agency question, answered\n * for one person. Read through `/api/ai/usage?uid=`, which admits the\n * person themselves and members holding `billing.view` or `org.auditLog`.\n *\n * A refusal renders NOTHING rather than a warning: the page is open to a\n * manager who may not hold either permission, and for them an absent card\n * is the correct answer, not a broken one.\n */\nexport function MemberAiUsageCard(props: MemberAiUsageCardProps) {\n const { orgId, uid } = props\n const orgSlug = props.orgSlug ?? ''\n const hosts = props.hosts ?? []\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity (AGL-2928).\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [state, setState] = useState<{\n status: 'loading' | 'ready' | 'refused' | 'error'\n months: UserAiUsageMonthWire[]\n }>({ status: 'loading', months: [] })\n const name = aiAddonName()\n const [thisMonth, lastMonth] = useMemo(() => aiUsageMonthKeys(new Date(), 2), [])\n\n useEffect(() => {\n const user = userRef.current\n if (!orgId || !uid || !signedInUid || !user) return undefined\n let active = true\n void (async () => {\n try {\n const params = new URLSearchParams({ orgId, uid, limit: '2' })\n const response = await authorizedFetch(\n user,\n `/api/ai/usage?${params.toString()}`,\n )\n if (!active) return\n if (response.status === 403) {\n setState({ status: 'refused', months: [] })\n return\n }\n const payload = (await response.json().catch(() => null)) as UserAiUsageWire | null\n if (!active) return\n if (!response.ok || !payload) {\n setState({ status: 'error', months: [] })\n return\n }\n setState({ status: 'ready', months: payload.months })\n } catch {\n if (active) setState({ status: 'error', months: [] })\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid, signedInUid])\n\n if (state.status === 'refused') return null\n\n const hostName = (hostId: string) => {\n const host = hosts.find((entry) => entry.$id === hostId)\n return host?.displayName ?? host?.subdomain ?? hostId\n }\n const byMonth = new Map(state.months.map((entry) => [entry.month, entry]))\n const shown = [thisMonth, lastMonth].map(\n (month) =>\n byMonth.get(month) ?? {\n month,\n credits: 0,\n share: 0,\n requests: 0,\n refusals: 0,\n byKind: {},\n byHost: {},\n },\n )\n const sites = shown.flatMap((entry) =>\n Object.entries(entry.byHost).map(([hostId, credits]) => ({\n month: entry.month,\n hostId,\n credits,\n })),\n )\n\n return (\n <CardDisplay\n header={'AI usage'}\n help={pluginDocsHelp('inviteTeammates', {\n anchor: '#ai-usage',\n excerpt:\n `This member's ${name} credits this month and last, their share of the workspace's pool, and the split by site and by kind.`,\n })}\n contentGutterX\n contentGutterY\n >\n {state.status === 'error' ? (\n <Alert severity=\"warning\">\n {'Could not read this member’s AI usage — a failed read, not zero usage.'}\n </Alert>\n ) : state.status === 'loading' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : (\n <Stack spacing={1.5}>\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Month'}</TableCell>\n <TableCell align=\"right\">{'Credits'}</TableCell>\n <TableCell align=\"right\">{'Share of pool'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell>{'Mostly'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {shown.map((entry) => (\n <TableRow key={entry.month}>\n <TableCell>{aiUsageMonthLabel(entry.month)}</TableCell>\n <TableCell align=\"right\">{entry.credits.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{formatAiUsageShare(entry.share)}</TableCell>\n <TableCell align=\"right\">{entry.requests.toLocaleString()}</TableCell>\n <TableCell>\n {topAiUsageKinds(entry.byKind, 3)\n .map((kind) => `${kind.kind} ${kind.credits.toLocaleString()}`)\n .join(', ') || '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n {sites.length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'By site'}\n </Typography>\n {sites.map((entry) => (\n <Typography key={`${entry.month}-${entry.hostId}`} variant=\"body2\">\n {`${hostName(entry.hostId)} — ${entry.credits.toLocaleString()} credits in ${aiUsageMonthLabel(entry.month)}`}\n </Typography>\n ))}\n </Stack>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Every member for the month is on '}\n <AppLink href={buildRoute(Route.MANAGE_BILLING_USAGE, { orgSlug })}>\n {'Billing → Usage'}\n </AppLink>\n {'.'}\n </Typography>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nMemberAiUsageCard.displayName = 'MemberAiUsageCard'\n\nexport default MemberAiUsageCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","AppLink","CardDisplay","ScrollTable","authorizedFetch","useUser","Alert","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useEffect","useMemo","useRef","useState","aiUsageMonthKeys","topAiUsageKinds","aiUsageMonthLabel","formatAiUsageShare","MemberAiUsageCard","props","orgId","uid","orgSlug","hosts","data","user","signedInUid","userRef","current","state","setState","status","months","name","thisMonth","lastMonth","Date","undefined","active","params","URLSearchParams","limit","response","toString","payload","json","catch","ok","hostName","hostId","host","find","entry","$id","displayName","subdomain","byMonth","Map","map","month","shown","get","credits","share","requests","refusals","byKind","byHost","sites","flatMap","Object","entries","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","variant","color","spacing","size","align","toLocaleString","kind","join","length","href","MANAGE_BILLING_USAGE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/member-ai-usage-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport { aiUsageMonthKeys, topAiUsageKinds } from '../model/ai-usage-by-user'\nimport {\n aiUsageMonthLabel,\n formatAiUsageShare,\n type UserAiUsageMonthWire,\n type UserAiUsageWire,\n} from '../usage/ai-usage-wire'\n\nexport interface MemberAiUsageCardProps {\n orgId: string\n uid: string\n orgSlug?: string\n /** The org's sites, for naming the per-site split. */\n hosts?: ReadonlyArray<{ $id: string; displayName?: string; subdomain?: string }>\n}\n\n/**\n * ONE MEMBER'S AI USAGE (AGL-2928) — this month and last, on their page.\n *\n * Credits and the share of the workspace's pool, requests, the kinds they\n * mostly asked for, and the split by site — the agency question, answered\n * for one person. Read through `/api/ai/usage?uid=`, which admits the\n * person themselves and members holding `billing.view` or `org.auditLog`.\n *\n * A refusal renders NOTHING rather than a warning: the page is open to a\n * manager who may not hold either permission, and for them an absent card\n * is the correct answer, not a broken one.\n */\nexport function MemberAiUsageCard(props: MemberAiUsageCardProps) {\n const { orgId, uid } = props\n const orgSlug = props.orgSlug ?? ''\n const hosts = props.hosts ?? []\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity (AGL-2928).\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [state, setState] = useState<{\n status: 'loading' | 'ready' | 'refused' | 'error'\n months: UserAiUsageMonthWire[]\n }>({ status: 'loading', months: [] })\n const name = aiAddonName()\n const [thisMonth, lastMonth] = useMemo(() => aiUsageMonthKeys(new Date(), 2), [])\n\n useEffect(() => {\n const user = userRef.current\n if (!orgId || !uid || !signedInUid || !user) return undefined\n let active = true\n void (async () => {\n try {\n const params = new URLSearchParams({ orgId, uid, limit: '2' })\n const response = await authorizedFetch(\n user,\n `/api/ai/usage?${params.toString()}`,\n )\n if (!active) return\n if (response.status === 403) {\n setState({ status: 'refused', months: [] })\n return\n }\n const payload = (await response.json().catch(() => null)) as UserAiUsageWire | null\n if (!active) return\n if (!response.ok || !payload) {\n setState({ status: 'error', months: [] })\n return\n }\n setState({ status: 'ready', months: payload.months })\n } catch {\n if (active) setState({ status: 'error', months: [] })\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, uid, signedInUid])\n\n if (state.status === 'refused') return null\n\n const hostName = (hostId: string) => {\n const host = hosts.find((entry) => entry.$id === hostId)\n return host?.displayName ?? host?.subdomain ?? hostId\n }\n const byMonth = new Map(state.months.map((entry) => [entry.month, entry]))\n const shown = [thisMonth, lastMonth].map(\n (month) =>\n byMonth.get(month) ?? {\n month,\n credits: 0,\n share: 0,\n requests: 0,\n refusals: 0,\n byKind: {},\n byHost: {},\n },\n )\n const sites = shown.flatMap((entry) =>\n Object.entries(entry.byHost).map(([hostId, credits]) => ({\n month: entry.month,\n hostId,\n credits,\n })),\n )\n\n return (\n <CardDisplay\n header={'AI usage'}\n help={pluginDocsHelp('inviteTeammates', {\n anchor: '#ai-usage',\n excerpt:\n `This member's ${name} credits this month and last, their share of the workspace's pool, and the split by site and by kind.`,\n })}\n contentGutterX\n contentGutterY\n >\n {state.status === 'error' ? (\n <Alert severity=\"warning\">\n {'Could not read this member’s AI usage — a failed read, not zero usage.'}\n </Alert>\n ) : state.status === 'loading' ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : (\n <Stack spacing={1.5}>\n <ScrollTable\n size=\"small\"\n // Below md the columns scroll sideways rather than folding each\n // header onto several lines.\n sx={{ '& th': { whiteSpace: { xs: 'nowrap', md: 'normal' } } }}\n >\n <TableHead>\n <TableRow>\n <TableCell>{'Month'}</TableCell>\n <TableCell align=\"right\">{'Credits'}</TableCell>\n <TableCell align=\"right\">{'Share of pool'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell>{'Mostly'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {shown.map((entry) => (\n <TableRow key={entry.month}>\n <TableCell>{aiUsageMonthLabel(entry.month)}</TableCell>\n <TableCell align=\"right\">{entry.credits.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{formatAiUsageShare(entry.share)}</TableCell>\n <TableCell align=\"right\">{entry.requests.toLocaleString()}</TableCell>\n <TableCell>\n {topAiUsageKinds(entry.byKind, 3)\n .map((kind) => `${kind.kind} ${kind.credits.toLocaleString()}`)\n .join(', ') || '—'}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n {sites.length ? (\n <Stack spacing={0.5}>\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'By site'}\n </Typography>\n {sites.map((entry) => (\n <Typography key={`${entry.month}-${entry.hostId}`} variant=\"body2\">\n {`${hostName(entry.hostId)} — ${entry.credits.toLocaleString()} credits in ${aiUsageMonthLabel(entry.month)}`}\n </Typography>\n ))}\n </Stack>\n ) : null}\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Every member for the month is on '}\n <AppLink href={buildRoute(Route.MANAGE_BILLING_USAGE, { orgSlug })}>\n {'Billing → Usage'}\n </AppLink>\n {'.'}\n </Typography>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nMemberAiUsageCard.displayName = 'MemberAiUsageCard'\n\nexport default MemberAiUsageCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","AppLink","CardDisplay","ScrollTable","authorizedFetch","useUser","Alert","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useEffect","useMemo","useRef","useState","aiUsageMonthKeys","topAiUsageKinds","aiUsageMonthLabel","formatAiUsageShare","MemberAiUsageCard","props","orgId","uid","orgSlug","hosts","data","user","signedInUid","userRef","current","state","setState","status","months","name","thisMonth","lastMonth","Date","undefined","active","params","URLSearchParams","limit","response","toString","payload","json","catch","ok","hostName","hostId","host","find","entry","$id","displayName","subdomain","byMonth","Map","map","month","shown","get","credits","share","requests","refusals","byKind","byHost","sites","flatMap","Object","entries","header","help","anchor","excerpt","contentGutterX","contentGutterY","severity","variant","color","spacing","size","sx","whiteSpace","xs","md","align","toLocaleString","kind","join","length","href","MANAGE_BILLING_USAGE"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Chip,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n StaffOrgAiJob,\n StaffOrgAiJobs,\n StaffOrgAiMargin,\n StaffOrgAiOverage,\n StaffOrgAiPool,\n StaffOrgAiRefusals,\n StaffOrgAiResponse,\n StaffOrgAiTokens,\n StaffOrgAiUser,\n} from '../usage/staff-org-ai'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/**\n * THE STAFF AI CARD (AGL-2930): everything about one org's AI usage on the\n * page staff already open to look at that org.\n *\n * Read through `/api/ai/admin/org`, one request on mount. Every figure on it\n * is the route's, and every figure the route serves is composed from the\n * same helpers the meter refuses with — see `usage/staff-org-ai.ts`. This\n * file only lays them out.\n *\n * ## What each section is for\n *\n * - **Add-on** — is the org paying for AI, at what, since when.\n * - **Credit pool** — the band as three NAMED parts, because \"why does\n * this org have 13,000 credits\" has three answers (the plan, an override,\n * the add-on) and a single total cannot say which.\n * - **Overage** — what is past the band, what it is sold at, and the two\n * controls the org itself holds over it.\n * - **Refusals** — how often, and why, the gate said no this month.\n * - **Jobs** and **Top users** — the generative and per-person halves,\n * each of which says plainly when its rollup has nothing yet.\n * - **Tokens** — what each kind of request costs per request, what it\n * sends and generates, and how much of its prompt the cache served.\n * - **Margin** — spend against what AI brings in, red when it is over.\n * - **Actions** — where the override editor and the AI pause live.\n *\n * The header carries the card's two acts (AGL-3595): give credits back to\n * the band (and, on a Free workspace, to the owner's allowance), or reset\n * the month — and each recent job offers the same give-back, prefilled with\n * that job's spend.\n */\n\nconst credits = (value: number | null | undefined): string =>\n value == null ? '—' : Math.round(value).toLocaleString()\n\n/**\n * Four decimals under a dollar, two above (the assist-signals page's rule):\n * a month that really cost eight cents must not read as `$0.00`.\n */\nconst usd = (value: number | null | undefined): string => {\n if (value == null) return '—'\n const amount = Number(value) || 0\n return `$${amount.toFixed(Math.abs(amount) < 1 ? 4 : 2)}`\n}\n\nconst when = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString() : '—'\n\n/** The pool as one sentence naming every part that is non-zero. */\nexport function poolPartsSentence(pool: StaffOrgAiPool): string {\n const parts: string[] = []\n if (pool.overrideCredits !== null) {\n parts.push(`${credits(pool.overrideCredits)} staff override`)\n } else {\n parts.push(`${credits(pool.planCredits)} plan band`)\n }\n if (pool.addonCredits > 0) {\n parts.push(`${credits(pool.addonCredits)} ${aiAddonName()} add-on`)\n }\n return parts.join(' + ')\n}\n\n/**\n * The overage's state as one line: sold, walled, or capped — or, for an\n * uncapped staff comp (AGL-3049), nothing to sell past at all.\n */\nexport function overageStateSentence(\n overage: StaffOrgAiOverage,\n uncapped = false,\n): string {\n if (uncapped) {\n return 'Nothing is sold past a band: an uncapped staff comp has none, and bills nothing.'\n }\n if (overage.hardCap) return 'Stopped at the band — the org’s band switch is on.'\n if (!overage.sellsOverage) return 'Not sold past the band on this plan.'\n const rate =\n overage.rateUsdPer1k === null ? '' : ` at ${usd(overage.rateUsdPer1k)} per 1,000`\n if (overage.capUsd !== null) {\n return overage.capReached\n ? `Stopped — the org’s ${usd(overage.capUsd)} overage ceiling is reached.`\n : `Sold${rate}, up to the org’s ${usd(overage.capUsd)} ceiling.`\n }\n return `Sold${rate}, no ceiling set.`\n}\n\n/** Whether the margin section renders red. */\nexport function marginTone(margin: StaffOrgAiMargin): 'error' | 'warning' | 'success' {\n if (margin.underwater) return 'error'\n if (margin.multiple >= 1) return 'warning'\n return 'success'\n}\n\nfunction RefusalsRow({ refusals }: { refusals: StaffOrgAiRefusals }) {\n const labels: Record<keyof Omit<StaffOrgAiRefusals, 'total'>, string> = {\n band: 'band',\n cap: 'ceiling',\n messages: 'messages',\n budget: 'operator backstop',\n // A hard allotment a manager set on a member, a collaborator or a site\n // (AGL-2942).\n allotment: 'allotments',\n // The Free taste's rungs (AGL-2925): only a Free workspace collects these.\n account: 'free account',\n requests: 'free requests/day',\n refusals: 'free refusals/day',\n platform: 'platform ceiling',\n }\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(Object.keys(labels) as Array<keyof typeof labels>).map((reason) => {\n // A reason the answering server does not yet count reads as zero,\n // so a console newer than its route draws the row rather than\n // failing the card.\n const count = Number(refusals[reason] ?? 0)\n return (\n <Chip\n key={reason}\n size=\"small\"\n variant={count > 0 ? 'filled' : 'outlined'}\n color={count > 0 ? 'warning' : 'default'}\n label={`${labels[reason]} ${count.toLocaleString()}`}\n />\n )\n })}\n </Stack>\n )\n}\n\nfunction JobsSection({\n jobs,\n onGiveBack,\n}: {\n jobs: StaffOrgAiJobs | null\n onGiveBack: (job: StaffOrgAiJob) => void\n}) {\n const columns = useMemo(() => jobColumns(onGiveBack), [onGiveBack])\n if (jobs === null) {\n return (\n <Alert severity=\"warning\">\n {'Could not read the generation jobs — a failed read, not an idle org.'}\n </Alert>\n )\n }\n const total = Object.values(jobs.counts).reduce((sum, n) => sum + n, 0)\n if (total === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No generation jobs yet.'}\n </Typography>\n )\n }\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(['queued', 'running', 'needs_input', 'needs_review', 'failed'] as const).map((status) => (\n <Chip\n key={status}\n size=\"small\"\n color={\n status === 'failed' && jobs.counts[status] > 0\n ? 'error'\n : (status === 'needs_input' || status === 'needs_review') &&\n jobs.counts[status] > 0\n ? 'warning'\n : 'default'\n }\n label={`${status.replace('_', ' ')} ${jobs.counts[status]}`}\n />\n ))}\n <Chip size=\"small\" variant=\"outlined\" label={`done ${jobs.counts.done}`} />\n </Stack>\n {jobs.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Counted from the newest jobs only — these counts are a floor.'}\n </Typography>\n ) : null}\n {/* A record list: the grid scrolls its own columns, so a job id and a\n starter's uid never push the card's content past its edge. */}\n <ListTable\n rows={jobs.recent}\n columns={columns}\n getRowId={(job: StaffOrgAiJob) => job.id}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No recent jobs\"\n // The route's ten newest jobs: a bounded readout, so no filter over\n // the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n </Stack>\n )\n}\n\n/**\n * The recent jobs, one column per fact. Who started a job is a column of\n * its own rather than a link tucked beside the date, so it sorts like the\n * rest; the credits sort by what was SPENT, which is the figure the\n * pool was charged. The row's action gives that spend back (AGL-3595).\n */\nconst jobColumns = (\n onGiveBack: (job: StaffOrgAiJob) => void,\n): GridColDef<StaffOrgAiJob>[] => [\n {\n field: 'id',\n headerName: 'Job',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }) => (\n <Typography variant=\"body2\" noWrap title={row.id} sx={{ fontFamily: 'monospace' }}>\n {row.id}\n </Typography>\n ),\n },\n {\n field: 'kind',\n headerName: 'Kind',\n width: 120,\n valueFormatter: (value: string) => value || '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n valueFormatter: (value: string) => String(value ?? '').replace('_', ' '),\n },\n {\n field: 'creditsSpent',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 140,\n valueFormatter: (value: number, row) =>\n `${credits(value)} / ${credits(row.creditsReserved)}`,\n },\n {\n field: 'createdAt',\n headerName: 'Started',\n width: 120,\n valueFormatter: (value: string | null) => when(value),\n },\n {\n field: 'createdBy',\n headerName: 'Started by',\n flex: 1,\n minWidth: 240,\n renderCell: ({ row }) =>\n row.createdBy ? (\n <AppLink\n href={buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.createdBy })}\n variant=\"body2\"\n underline=\"hover\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.createdBy}\n </AppLink>\n ) : (\n '—'\n ),\n },\n listActionsColumn((row: StaffOrgAiJob) => (\n <ListRowActions\n label={row.id}\n items={[\n {\n key: 'give-back',\n label: 'Give back this job’s credits',\n disabled: row.creditsSpent <= 0,\n disabledReason: row.creditsSpent <= 0 ? 'This job spent no credits.' : undefined,\n onClick: () => onGiveBack(row),\n },\n ]}\n />\n )),\n]\n\nconst tokenCount = (value: number): string => Math.round(value).toLocaleString()\n\nconst rate = (value: number | null): string =>\n value == null ? '—' : `${Math.round(value * 100)}%`\n\n/** The month's tokens, and what each kind of request spends of them (AGL-2937). */\nfunction TokensSection({ tokens }: { tokens: StaffOrgAiTokens }) {\n const { total } = tokens\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Typography variant=\"body2\">\n {`${tokenCount(total.input)} sent · ${tokenCount(total.cached)} read from cache · ${tokenCount(total.cacheWrite)} written to cache · ${tokenCount(total.output)} generated`}\n </Typography>\n <Chip\n size=\"small\"\n color={tokens.cacheHitRate != null && tokens.cacheHitRate >= 0.5 ? 'success' : 'default'}\n label={`cache hit rate ${rate(tokens.cacheHitRate)}`}\n />\n </Stack>\n {tokens.kinds.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No model requests recorded by kind this month.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Kind'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell align=\"right\">{'Per request'}</TableCell>\n <TableCell align=\"right\">{'Sent'}</TableCell>\n <TableCell align=\"right\">{'From cache'}</TableCell>\n <TableCell align=\"right\">{'Generated'}</TableCell>\n <TableCell align=\"right\">{'Cache hit'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {tokens.kinds.map((row) => (\n <TableRow key={row.kind}>\n <TableCell>{row.kind}</TableCell>\n <TableCell align=\"right\">{row.requests.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{usd(row.costPerRequestUsd)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.input)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.cached)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.output)}</TableCell>\n <TableCell align=\"right\">{rate(row.cacheHitRate)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )\n}\n\n/**\n * The month's top spenders, one row per person. The row opens the account;\n * the uid rides beneath the roster's name because two people can share one.\n */\nfunction TopUsersSection({ users }: { users: StaffOrgAiUser[] }) {\n const router = useRouter()\n const columns = useMemo<GridColDef<StaffOrgAiUser>[]>(\n () => [\n {\n field: 'name',\n headerName: 'Person',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap title={row.name}>\n {row.name}\n </Typography>\n {row.name !== row.uid ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.uid}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => credits(value),\n },\n {\n field: 'share',\n headerName: 'Share',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 90,\n valueFormatter: (value: number) => `${Math.round(value * 100)}%`,\n },\n {\n field: 'estCostUsd',\n headerName: 'Provider $',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n valueFormatter: (value: number) => usd(value),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffOrgAiUser) => (\n <ListRowActions\n label={row.name}\n quick={{\n icon: mdiOpenInNew.path,\n label: 'View account',\n to: buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.uid }),\n }}\n items={[]}\n />\n )),\n ],\n [],\n )\n return (\n <ListTable\n rows={users}\n columns={columns}\n getRowId={(row: StaffOrgAiUser) => row.uid}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(uid) => router.push(buildRoute(Route.ADMIN_USER_DETAIL, { uid }))}\n // The route's ten dearest members: a bounded readout, so no filter\n // over the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n )\n}\n\nconst StaffOrgAiCard = ({ orgId }: { orgId: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity.\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [data, setData] = useState<StaffOrgAiResponse | null>(null)\n const [ready, setReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // Bumped after a give-back, so the card re-reads the figures it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<{\n mode: 'give' | 'reset'\n jobId?: string\n jobCredits?: number\n } | null>(null)\n const giveBackJob = useCallback(\n (job: StaffOrgAiJob) =>\n setReturning({ mode: 'give', jobId: job.id, jobCredits: job.creditsSpent }),\n [],\n )\n\n useEffect(() => {\n // Mounted only for a confirmed staff reader — the `staffOrg` zone sits\n // inside the page's `StaffOnly`, which renders nothing while the claim\n // loads — because the route records an access row on every successful\n // open, and an open the page did not mean to make would be an access\n // nobody made.\n const user = userRef.current\n if (!orgId || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/org?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI lookup failed')\n setData(null)\n } else {\n setData(payload as StaffOrgAiResponse)\n }\n } catch {\n if (active) {\n setError('AI lookup failed')\n setData(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, signedInUid, version])\n\n const name = aiAddonName()\n return (\n <CardDisplay\n header={name}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#the-ai-card',\n excerpt:\n 'The add-on, the credit pool and its parts, this month’s overage and refusals, generation jobs, tokens and the cache hit rate by kind, the people spending the most, and the margin — for this organization.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!data}\n onGive={() => setReturning({ mode: 'give' })}\n onReset={() => setReturning({ mode: 'reset' })}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {data ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning?.mode ?? 'give'}\n target={{ orgId }}\n workspace={{ used: data.pool.usedCredits, limit: data.pool.totalCredits }}\n account={\n data.account\n ? { used: data.account.usedCredits, limit: data.account.limitCredits }\n : null\n }\n jobId={returning?.jobId ?? null}\n jobCredits={returning?.jobCredits ?? null}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !data ? (\n <Alert severity=\"warning\">\n {`Could not read this organization’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n {/* Add-on */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Add-on'}\n </Typography>\n <Stack direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={data.addon.on ? 'primary' : 'default'}\n label={data.addon.on ? `${name} on` : `${name} off`}\n />\n {data.addon.priceUsd !== null ? (\n <Typography variant=\"body2\">\n {`${usd(data.addon.priceUsd)}/mo on this plan`}\n </Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Not sold on this plan'}\n </Typography>\n )}\n {data.addon.on ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {data.addon.since\n ? `since ${when(data.addon.since)}`\n : data.addon.sinceSource === 'unavailable'\n ? 'since — (Stripe not reachable)'\n : 'since — (no subscription item)'}\n </Typography>\n ) : null}\n </Stack>\n </Stack>\n\n {/* Credit pool */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Credit pool · ${data.month}`}\n </Typography>\n {data.pool.totalCredits === null && data.pool.uncapped ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'}\n </Typography>\n ) : data.pool.totalCredits === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI credit band on this plan — bounded by the message cap and the operator backstop.'}\n </Typography>\n ) : (\n <Typography variant=\"body2\">\n {`${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool)}`}\n </Typography>\n )}\n <Typography variant=\"body2\">\n {`Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`}\n {data.pool.remainingCredits !== null\n ? ` · ${credits(data.pool.remainingCredits)} remaining`\n : ''}\n </Typography>\n {data.pool.returnedCredits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${credits(data.pool.returnedCredits)} credits given back this month — already taken off the figure above.`}\n </Typography>\n ) : null}\n {data.account ? (\n <Typography variant=\"body2\">\n {`Owner’s Free allowance: ${credits(data.account.usedCredits)} of ${credits(data.account.limitCredits)} used across their free workspaces` +\n (data.account.returnedCredits\n ? ` (${credits(data.account.returnedCredits)} given back)`\n : '')}\n </Typography>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Projected month-end at this pace: ${credits(data.pool.projectedCredits)} credits (${usd(data.pool.projectedUsd)}) · ${data.pool.messages.toLocaleString()} model turns, ${data.pool.deflected.toLocaleString()} answered from the docs`}\n </Typography>\n </Stack>\n\n {/* Overage */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Overage'}\n </Typography>\n <Typography variant=\"body2\">\n {`${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {overageStateSentence(data.overage, data.pool.uncapped === true)}\n </Typography>\n </Stack>\n\n {/* Refusals */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Refusals this month · ${data.refusals.total.toLocaleString()}`}\n </Typography>\n <RefusalsRow refusals={data.refusals} />\n </Stack>\n\n {/* Jobs */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Generation jobs'}\n </Typography>\n <JobsSection jobs={data.jobs} onGiveBack={giveBackJob} />\n </Stack>\n\n {/* Tokens — absent from a route older than the card */}\n {data.tokens ? (\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Tokens · ${data.month}`}\n </Typography>\n <TokensSection tokens={data.tokens} />\n </Stack>\n ) : null}\n\n {/* Top users */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Top users this month'}\n </Typography>\n {data.users.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to a person this month.'}\n </Typography>\n ) : (\n <TopUsersSection users={data.users} />\n )}\n </Stack>\n\n {/* Margin */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Margin'}\n </Typography>\n <Alert severity={marginTone(data.margin)}>\n {`Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` +\n `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` +\n `${usd(data.margin.overageRevenueUsd)} overage.`}\n {data.margin.underwater\n ? ' Spend exceeds the add-on plus the plan’s assist share.'\n : ''}\n {data.margin.multiple >= 1\n ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.`\n : ''}\n </Alert>\n {data.margin.contribution.marginPct !== null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`}\n </Typography>\n ) : null}\n </Stack>\n\n {/* Actions */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Actions'}\n </Typography>\n <Typography variant=\"body2\">\n {'Credit override and feature toggles: '}\n <AppLink href={buildRoute(Route.ADMIN_ORGS)} underline=\"hover\">\n {'the entitlement override editor'}\n </AppLink>\n {' (also under Staff actions above). Pause AI for this org: '}\n <AppLink href={buildRoute(Route.ADMIN_LOCKDOWN)} underline=\"hover\">\n {'Lockdown'}\n </AppLink>\n {', feature keys ai-assist and ai-generate scoped to the org.'}\n </Typography>\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nStaffOrgAiCard.displayName = 'StaffOrgAiCard'\n\nexport default StaffOrgAiCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","AppLink","CardDisplay","ListRowActions","ListTable","listActionsColumn","ScrollTable","TABLE_ROW_HEIGHT","useUser","authorizedFetch","Alert","Chip","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useCallback","useEffect","useMemo","useRef","useState","StaffCreditReturnActions","StaffCreditReturnDialog","credits","value","Math","round","toLocaleString","usd","amount","Number","toFixed","abs","when","iso","Date","toLocaleDateString","poolPartsSentence","pool","parts","overrideCredits","push","planCredits","addonCredits","join","overageStateSentence","overage","uncapped","hardCap","sellsOverage","rate","rateUsdPer1k","capUsd","capReached","marginTone","margin","underwater","multiple","RefusalsRow","refusals","labels","band","cap","messages","budget","allotment","account","requests","platform","direction","spacing","useFlexGap","sx","flexWrap","Object","keys","map","reason","count","size","variant","color","label","JobsSection","jobs","onGiveBack","columns","jobColumns","severity","total","values","counts","reduce","sum","n","status","replace","done","truncated","rows","recent","getRowId","job","id","rowHeight","noRowsLabel","disableColumnFilter","quickFilter","field","headerName","flex","minWidth","renderCell","row","noWrap","title","fontFamily","width","valueFormatter","String","type","align","headerAlign","creditsReserved","createdBy","href","ADMIN_USER_DETAIL","uid","underline","items","key","disabled","creditsSpent","disabledReason","undefined","onClick","tokenCount","TokensSection","tokens","alignItems","input","cached","cacheWrite","output","cacheHitRate","kinds","length","kind","costPerRequestUsd","TopUsersSection","users","router","name","quick","icon","path","to","onOpen","StaffOrgAiCard","orgId","data","user","signedInUid","userRef","current","setData","ready","setReady","error","setError","version","setVersion","notice","setNotice","returning","setReturning","giveBackJob","mode","jobId","jobCredits","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","onGive","onReset","contentGutterX","contentGutterY","open","target","workspace","used","usedCredits","limit","totalCredits","limitCredits","onClose","onReturned","message","mb","addon","on","priceUsd","since","sinceSource","month","providerUsd","remainingCredits","returnedCredits","projectedCredits","projectedUsd","deflected","overageCredits","accruedUsd","spendUsd","revenueUsd","addonRevenueUsd","planAssistShareUsd","overageRevenueUsd","thresholdUsd","contribution","marginPct","ADMIN_ORGS","ADMIN_LOCKDOWN","displayName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/staff-org-ai-card.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use client'\n\nimport { aiAddonName } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiOpenInNew } from '@aglyn/shared-data-mdi'\nimport { AppLink, CardDisplay } from '@aglyn/shared-ui-jsx'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { ScrollTable } from '@aglyn/shared-ui-jsx/components/scroll-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport {\n Alert,\n Chip,\n Stack,\n TableBody,\n TableCell,\n TableHead,\n TableRow,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n StaffOrgAiJob,\n StaffOrgAiJobs,\n StaffOrgAiMargin,\n StaffOrgAiOverage,\n StaffOrgAiPool,\n StaffOrgAiRefusals,\n StaffOrgAiResponse,\n StaffOrgAiTokens,\n StaffOrgAiUser,\n} from '../usage/staff-org-ai'\nimport {\n StaffCreditReturnActions,\n StaffCreditReturnDialog,\n} from './staff-ai-credit-return.component'\n\n/**\n * THE STAFF AI CARD (AGL-2930): everything about one org's AI usage on the\n * page staff already open to look at that org.\n *\n * Read through `/api/ai/admin/org`, one request on mount. Every figure on it\n * is the route's, and every figure the route serves is composed from the\n * same helpers the meter refuses with — see `usage/staff-org-ai.ts`. This\n * file only lays them out.\n *\n * ## What each section is for\n *\n * - **Add-on** — is the org paying for AI, at what, since when.\n * - **Credit pool** — the band as three NAMED parts, because \"why does\n * this org have 13,000 credits\" has three answers (the plan, an override,\n * the add-on) and a single total cannot say which.\n * - **Overage** — what is past the band, what it is sold at, and the two\n * controls the org itself holds over it.\n * - **Refusals** — how often, and why, the gate said no this month.\n * - **Jobs** and **Top users** — the generative and per-person halves,\n * each of which says plainly when its rollup has nothing yet.\n * - **Tokens** — what each kind of request costs per request, what it\n * sends and generates, and how much of its prompt the cache served.\n * - **Margin** — spend against what AI brings in, red when it is over.\n * - **Actions** — where the override editor and the AI pause live.\n *\n * The header carries the card's two acts (AGL-3595): give credits back to\n * the band (and, on a Free workspace, to the owner's allowance), or reset\n * the month — and each recent job offers the same give-back, prefilled with\n * that job's spend.\n */\n\nconst credits = (value: number | null | undefined): string =>\n value == null ? '—' : Math.round(value).toLocaleString()\n\n/**\n * Four decimals under a dollar, two above (the assist-signals page's rule):\n * a month that really cost eight cents must not read as `$0.00`.\n */\nconst usd = (value: number | null | undefined): string => {\n if (value == null) return '—'\n const amount = Number(value) || 0\n return `$${amount.toFixed(Math.abs(amount) < 1 ? 4 : 2)}`\n}\n\nconst when = (iso: string | null): string =>\n iso ? new Date(iso).toLocaleDateString() : '—'\n\n/** The pool as one sentence naming every part that is non-zero. */\nexport function poolPartsSentence(pool: StaffOrgAiPool): string {\n const parts: string[] = []\n if (pool.overrideCredits !== null) {\n parts.push(`${credits(pool.overrideCredits)} staff override`)\n } else {\n parts.push(`${credits(pool.planCredits)} plan band`)\n }\n if (pool.addonCredits > 0) {\n parts.push(`${credits(pool.addonCredits)} ${aiAddonName()} add-on`)\n }\n return parts.join(' + ')\n}\n\n/**\n * The overage's state as one line: sold, walled, or capped — or, for an\n * uncapped staff comp (AGL-3049), nothing to sell past at all.\n */\nexport function overageStateSentence(\n overage: StaffOrgAiOverage,\n uncapped = false,\n): string {\n if (uncapped) {\n return 'Nothing is sold past a band: an uncapped staff comp has none, and bills nothing.'\n }\n if (overage.hardCap) return 'Stopped at the band — the org’s band switch is on.'\n if (!overage.sellsOverage) return 'Not sold past the band on this plan.'\n const rate =\n overage.rateUsdPer1k === null ? '' : ` at ${usd(overage.rateUsdPer1k)} per 1,000`\n if (overage.capUsd !== null) {\n return overage.capReached\n ? `Stopped — the org’s ${usd(overage.capUsd)} overage ceiling is reached.`\n : `Sold${rate}, up to the org’s ${usd(overage.capUsd)} ceiling.`\n }\n return `Sold${rate}, no ceiling set.`\n}\n\n/** Whether the margin section renders red. */\nexport function marginTone(margin: StaffOrgAiMargin): 'error' | 'warning' | 'success' {\n if (margin.underwater) return 'error'\n if (margin.multiple >= 1) return 'warning'\n return 'success'\n}\n\nfunction RefusalsRow({ refusals }: { refusals: StaffOrgAiRefusals }) {\n const labels: Record<keyof Omit<StaffOrgAiRefusals, 'total'>, string> = {\n band: 'band',\n cap: 'ceiling',\n messages: 'messages',\n budget: 'operator backstop',\n // A hard allotment a manager set on a member, a collaborator or a site\n // (AGL-2942).\n allotment: 'allotments',\n // The Free taste's rungs (AGL-2925): only a Free workspace collects these.\n account: 'free account',\n requests: 'free requests/day',\n refusals: 'free refusals/day',\n platform: 'platform ceiling',\n }\n return (\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(Object.keys(labels) as Array<keyof typeof labels>).map((reason) => {\n // A reason the answering server does not yet count reads as zero,\n // so a console newer than its route draws the row rather than\n // failing the card.\n const count = Number(refusals[reason] ?? 0)\n return (\n <Chip\n key={reason}\n size=\"small\"\n variant={count > 0 ? 'filled' : 'outlined'}\n color={count > 0 ? 'warning' : 'default'}\n label={`${labels[reason]} ${count.toLocaleString()}`}\n />\n )\n })}\n </Stack>\n )\n}\n\nfunction JobsSection({\n jobs,\n onGiveBack,\n}: {\n jobs: StaffOrgAiJobs | null\n onGiveBack: (job: StaffOrgAiJob) => void\n}) {\n const columns = useMemo(() => jobColumns(onGiveBack), [onGiveBack])\n if (jobs === null) {\n return (\n <Alert severity=\"warning\">\n {'Could not read the generation jobs — a failed read, not an idle org.'}\n </Alert>\n )\n }\n const total = Object.values(jobs.counts).reduce((sum, n) => sum + n, 0)\n if (total === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No generation jobs yet.'}\n </Typography>\n )\n }\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ flexWrap: 'wrap' }}>\n {(['queued', 'running', 'needs_input', 'needs_review', 'failed'] as const).map((status) => (\n <Chip\n key={status}\n size=\"small\"\n color={\n status === 'failed' && jobs.counts[status] > 0\n ? 'error'\n : (status === 'needs_input' || status === 'needs_review') &&\n jobs.counts[status] > 0\n ? 'warning'\n : 'default'\n }\n label={`${status.replace('_', ' ')} ${jobs.counts[status]}`}\n />\n ))}\n <Chip size=\"small\" variant=\"outlined\" label={`done ${jobs.counts.done}`} />\n </Stack>\n {jobs.truncated ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {'Counted from the newest jobs only — these counts are a floor.'}\n </Typography>\n ) : null}\n {/* A record list: the grid scrolls its own columns, so a job id and a\n starter's uid never push the card's content past its edge. */}\n <ListTable\n rows={jobs.recent}\n columns={columns}\n getRowId={(job: StaffOrgAiJob) => job.id}\n rowHeight={TABLE_ROW_HEIGHT}\n noRowsLabel=\"No recent jobs\"\n // The route's ten newest jobs: a bounded readout, so no filter over\n // the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n </Stack>\n )\n}\n\n/**\n * The recent jobs, one column per fact. Who started a job is a column of\n * its own rather than a link tucked beside the date, so it sorts like the\n * rest; the credits sort by what was SPENT, which is the figure the\n * pool was charged. The row's action gives that spend back (AGL-3595).\n */\nconst jobColumns = (\n onGiveBack: (job: StaffOrgAiJob) => void,\n): GridColDef<StaffOrgAiJob>[] => [\n {\n field: 'id',\n headerName: 'Job',\n flex: 1,\n minWidth: 200,\n renderCell: ({ row }) => (\n <Typography variant=\"body2\" noWrap title={row.id} sx={{ fontFamily: 'monospace' }}>\n {row.id}\n </Typography>\n ),\n },\n {\n field: 'kind',\n headerName: 'Kind',\n width: 120,\n valueFormatter: (value: string) => value || '—',\n },\n {\n field: 'status',\n headerName: 'Status',\n width: 130,\n valueFormatter: (value: string) => String(value ?? '').replace('_', ' '),\n },\n {\n field: 'creditsSpent',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 140,\n valueFormatter: (value: number, row) =>\n `${credits(value)} / ${credits(row.creditsReserved)}`,\n },\n {\n field: 'createdAt',\n headerName: 'Started',\n width: 120,\n valueFormatter: (value: string | null) => when(value),\n },\n {\n field: 'createdBy',\n headerName: 'Started by',\n flex: 1,\n minWidth: 240,\n renderCell: ({ row }) =>\n row.createdBy ? (\n <AppLink\n href={buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.createdBy })}\n variant=\"body2\"\n underline=\"hover\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.createdBy}\n </AppLink>\n ) : (\n '—'\n ),\n },\n listActionsColumn((row: StaffOrgAiJob) => (\n <ListRowActions\n label={row.id}\n items={[\n {\n key: 'give-back',\n label: 'Give back this job’s credits',\n disabled: row.creditsSpent <= 0,\n disabledReason: row.creditsSpent <= 0 ? 'This job spent no credits.' : undefined,\n onClick: () => onGiveBack(row),\n },\n ]}\n />\n )),\n]\n\nconst tokenCount = (value: number): string => Math.round(value).toLocaleString()\n\nconst rate = (value: number | null): string =>\n value == null ? '—' : `${Math.round(value * 100)}%`\n\n/** The month's tokens, and what each kind of request spends of them (AGL-2937). */\nfunction TokensSection({ tokens }: { tokens: StaffOrgAiTokens }) {\n const { total } = tokens\n return (\n <Stack spacing={1}>\n <Stack direction=\"row\" spacing={1} useFlexGap sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Typography variant=\"body2\">\n {`${tokenCount(total.input)} sent · ${tokenCount(total.cached)} read from cache · ${tokenCount(total.cacheWrite)} written to cache · ${tokenCount(total.output)} generated`}\n </Typography>\n <Chip\n size=\"small\"\n color={tokens.cacheHitRate != null && tokens.cacheHitRate >= 0.5 ? 'success' : 'default'}\n label={`cache hit rate ${rate(tokens.cacheHitRate)}`}\n />\n </Stack>\n {tokens.kinds.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No model requests recorded by kind this month.'}\n </Typography>\n ) : (\n <ScrollTable size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{'Kind'}</TableCell>\n <TableCell align=\"right\">{'Requests'}</TableCell>\n <TableCell align=\"right\">{'Per request'}</TableCell>\n <TableCell align=\"right\">{'Sent'}</TableCell>\n <TableCell align=\"right\">{'From cache'}</TableCell>\n <TableCell align=\"right\">{'Generated'}</TableCell>\n <TableCell align=\"right\">{'Cache hit'}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {tokens.kinds.map((row) => (\n <TableRow key={row.kind}>\n <TableCell>{row.kind}</TableCell>\n <TableCell align=\"right\">{row.requests.toLocaleString()}</TableCell>\n <TableCell align=\"right\">{usd(row.costPerRequestUsd)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.input)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.cached)}</TableCell>\n <TableCell align=\"right\">{tokenCount(row.tokens.output)}</TableCell>\n <TableCell align=\"right\">{rate(row.cacheHitRate)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </ScrollTable>\n )}\n </Stack>\n )\n}\n\n/**\n * The month's top spenders, one row per person. The row opens the account;\n * the uid rides beneath the roster's name because two people can share one.\n */\nfunction TopUsersSection({ users }: { users: StaffOrgAiUser[] }) {\n const router = useRouter()\n const columns = useMemo<GridColDef<StaffOrgAiUser>[]>(\n () => [\n {\n field: 'name',\n headerName: 'Person',\n flex: 1,\n minWidth: 220,\n renderCell: ({ row }) => (\n <Stack sx={{ minWidth: 0 }}>\n <Typography variant=\"body2\" noWrap title={row.name}>\n {row.name}\n </Typography>\n {row.name !== row.uid ? (\n <Typography\n variant=\"caption\"\n color=\"text.secondary\"\n noWrap\n sx={{ fontFamily: 'monospace' }}\n >\n {row.uid}\n </Typography>\n ) : null}\n </Stack>\n ),\n },\n {\n field: 'credits',\n headerName: 'Credits',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => credits(value),\n },\n {\n field: 'share',\n headerName: 'Share',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 90,\n valueFormatter: (value: number) => `${Math.round(value * 100)}%`,\n },\n {\n field: 'estCostUsd',\n headerName: 'Provider $',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 120,\n valueFormatter: (value: number) => usd(value),\n },\n {\n field: 'requests',\n headerName: 'Requests',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n {\n field: 'refusals',\n headerName: 'Refusals',\n type: 'number',\n align: 'right',\n headerAlign: 'right',\n width: 110,\n valueFormatter: (value: number) => value.toLocaleString(),\n },\n listActionsColumn((row: StaffOrgAiUser) => (\n <ListRowActions\n label={row.name}\n quick={{\n icon: mdiOpenInNew.path,\n label: 'View account',\n to: buildRoute(Route.ADMIN_USER_DETAIL, { uid: row.uid }),\n }}\n items={[]}\n />\n )),\n ],\n [],\n )\n return (\n <ListTable\n rows={users}\n columns={columns}\n getRowId={(row: StaffOrgAiUser) => row.uid}\n rowHeight={TABLE_ROW_HEIGHT}\n onOpen={(uid) => router.push(buildRoute(Route.ADMIN_USER_DETAIL, { uid }))}\n // The route's ten dearest members: a bounded readout, so no filter\n // over the rows it holds (AGL-3321).\n disableColumnFilter\n quickFilter={false}\n />\n )\n}\n\nconst StaffOrgAiCard = ({ orgId }: { orgId: string }) => {\n const { data: user } = useUser()\n // Keyed on who is signed in, not on the user object's identity.\n const signedInUid = user?.uid ?? null\n const userRef = useRef(user)\n userRef.current = user\n const [data, setData] = useState<StaffOrgAiResponse | null>(null)\n const [ready, setReady] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // Bumped after a give-back, so the card re-reads the figures it changed.\n const [version, setVersion] = useState(0)\n const [notice, setNotice] = useState<string | null>(null)\n const [returning, setReturning] = useState<{\n mode: 'give' | 'reset'\n jobId?: string\n jobCredits?: number\n } | null>(null)\n const giveBackJob = useCallback(\n (job: StaffOrgAiJob) =>\n setReturning({ mode: 'give', jobId: job.id, jobCredits: job.creditsSpent }),\n [],\n )\n\n useEffect(() => {\n // Mounted only for a confirmed staff reader — the `staffOrg` zone sits\n // inside the page's `StaffOnly`, which renders nothing while the claim\n // loads — because the route records an access row on every successful\n // open, and an open the page did not mean to make would be an access\n // nobody made.\n const user = userRef.current\n if (!orgId || !signedInUid || !user) return undefined\n let active = true\n setReady(false)\n setError(null)\n void (async () => {\n try {\n const response = await authorizedFetch(\n user,\n `/api/ai/admin/org?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!active) return\n if (!response.ok) {\n setError(payload?.error ?? 'AI lookup failed')\n setData(null)\n } else {\n setData(payload as StaffOrgAiResponse)\n }\n } catch {\n if (active) {\n setError('AI lookup failed')\n setData(null)\n }\n } finally {\n if (active) setReady(true)\n }\n })()\n return () => {\n active = false\n }\n }, [orgId, signedInUid, version])\n\n const name = aiAddonName()\n return (\n <CardDisplay\n header={name}\n help={pluginDocsHelp('aiMonitoring', {\n anchor: '#the-ai-card',\n excerpt:\n 'The add-on, the credit pool and its parts, this month’s overage and refusals, generation jobs, tokens and the cache hit rate by kind, the people spending the most, and the margin — for this organization.',\n })}\n HeaderProps={{\n action: (\n <StaffCreditReturnActions\n disabled={!data}\n onGive={() => setReturning({ mode: 'give' })}\n onReset={() => setReturning({ mode: 'reset' })}\n />\n ),\n }}\n contentGutterX\n contentGutterY\n >\n {data ? (\n <StaffCreditReturnDialog\n open={returning !== null}\n mode={returning?.mode ?? 'give'}\n target={{ orgId }}\n workspace={{ used: data.pool.usedCredits, limit: data.pool.totalCredits }}\n account={\n data.account\n ? { used: data.account.usedCredits, limit: data.account.limitCredits }\n : null\n }\n jobId={returning?.jobId ?? null}\n jobCredits={returning?.jobCredits ?? null}\n onClose={() => setReturning(null)}\n onReturned={(message) => {\n setReturning(null)\n setNotice(message)\n setVersion((current) => current + 1)\n }}\n />\n ) : null}\n {notice ? (\n <Alert severity=\"success\" onClose={() => setNotice(null)} sx={{ mb: 2 }}>\n {notice}\n </Alert>\n ) : null}\n {!ready ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Loading…'}\n </Typography>\n ) : error || !data ? (\n <Alert severity=\"warning\">\n {`Could not read this organization’s AI usage — ${error ?? 'a failed read'}, not zero usage.`}\n </Alert>\n ) : (\n <Stack spacing={2}>\n {/* Add-on */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Add-on'}\n </Typography>\n <Stack useFlexGap direction=\"row\" spacing={1} sx={{ alignItems: 'center', flexWrap: 'wrap' }}>\n <Chip\n size=\"small\"\n color={data.addon.on ? 'primary' : 'default'}\n label={data.addon.on ? `${name} on` : `${name} off`}\n />\n {data.addon.priceUsd !== null ? (\n <Typography variant=\"body2\">\n {`${usd(data.addon.priceUsd)}/mo on this plan`}\n </Typography>\n ) : (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Not sold on this plan'}\n </Typography>\n )}\n {data.addon.on ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {data.addon.since\n ? `since ${when(data.addon.since)}`\n : data.addon.sinceSource === 'unavailable'\n ? 'since — (Stripe not reachable)'\n : 'since — (no subscription item)'}\n </Typography>\n ) : null}\n </Stack>\n </Stack>\n\n {/* Credit pool */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Credit pool · ${data.month}`}\n </Typography>\n {data.pool.totalCredits === null && data.pool.uncapped ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Uncapped staff comp — no AI credit band. Bounded only by the message cap and an operator’s explicit spend ceiling.'}\n </Typography>\n ) : data.pool.totalCredits === null ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI credit band on this plan — bounded by the message cap and the operator backstop.'}\n </Typography>\n ) : (\n <Typography variant=\"body2\">\n {`${credits(data.pool.totalCredits)} credits = ${poolPartsSentence(data.pool)}`}\n </Typography>\n )}\n <Typography variant=\"body2\">\n {`Used ${credits(data.pool.usedCredits)} credits (${usd(data.pool.providerUsd)} provider spend)`}\n {data.pool.remainingCredits !== null\n ? ` · ${credits(data.pool.remainingCredits)} remaining`\n : ''}\n </Typography>\n {data.pool.returnedCredits ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`${credits(data.pool.returnedCredits)} credits given back this month — already taken off the figure above.`}\n </Typography>\n ) : null}\n {data.account ? (\n <Typography variant=\"body2\">\n {`Owner’s Free allowance: ${credits(data.account.usedCredits)} of ${credits(data.account.limitCredits)} used across their free workspaces` +\n (data.account.returnedCredits\n ? ` (${credits(data.account.returnedCredits)} given back)`\n : '')}\n </Typography>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {`Projected month-end at this pace: ${credits(data.pool.projectedCredits)} credits (${usd(data.pool.projectedUsd)}) · ${data.pool.messages.toLocaleString()} model turns, ${data.pool.deflected.toLocaleString()} answered from the docs`}\n </Typography>\n </Stack>\n\n {/* Overage */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Overage'}\n </Typography>\n <Typography variant=\"body2\">\n {`${credits(data.overage.overageCredits)} credits over the band · ${usd(data.overage.accruedUsd)} accrued`}\n </Typography>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {overageStateSentence(data.overage, data.pool.uncapped === true)}\n </Typography>\n </Stack>\n\n {/* Refusals */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Refusals this month · ${data.refusals.total.toLocaleString()}`}\n </Typography>\n <RefusalsRow refusals={data.refusals} />\n </Stack>\n\n {/* Jobs */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Generation jobs'}\n </Typography>\n <JobsSection jobs={data.jobs} onGiveBack={giveBackJob} />\n </Stack>\n\n {/* Tokens — absent from a route older than the card */}\n {data.tokens ? (\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Tokens · ${data.month}`}\n </Typography>\n <TokensSection tokens={data.tokens} />\n </Stack>\n ) : null}\n\n {/* Top users */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Top users this month'}\n </Typography>\n {data.users.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No AI usage attributed to a person this month.'}\n </Typography>\n ) : (\n <TopUsersSection users={data.users} />\n )}\n </Stack>\n\n {/* Margin */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Margin'}\n </Typography>\n <Alert severity={marginTone(data.margin)}>\n {`Spend ${usd(data.margin.spendUsd)} against ${usd(data.margin.revenueUsd)} in — ` +\n `${usd(data.margin.addonRevenueUsd)} add-on, ${usd(data.margin.planAssistShareUsd)} plan assist share, ` +\n `${usd(data.margin.overageRevenueUsd)} overage.`}\n {data.margin.underwater\n ? ' Spend exceeds the add-on plus the plan’s assist share.'\n : ''}\n {data.margin.multiple >= 1\n ? ` ${data.margin.multiple}× the ${usd(data.margin.thresholdUsd)} staff review threshold.`\n : ''}\n </Alert>\n {data.margin.contribution.marginPct !== null ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`Whole-org contribution margin ${Math.round(data.margin.contribution.marginPct * 100)}% on the ${data.margin.contribution.month} rollup with this month’s AI spend.`}\n </Typography>\n ) : null}\n </Stack>\n\n {/* Actions */}\n <Stack spacing={0.5}>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Actions'}\n </Typography>\n <Typography variant=\"body2\">\n {'Credit override and feature toggles: '}\n <AppLink href={buildRoute(Route.ADMIN_ORGS)} underline=\"hover\">\n {'the entitlement override editor'}\n </AppLink>\n {' (also under Staff actions above). Pause AI for this org: '}\n <AppLink href={buildRoute(Route.ADMIN_LOCKDOWN)} underline=\"hover\">\n {'Lockdown'}\n </AppLink>\n {', feature keys ai-assist and ai-generate scoped to the org.'}\n </Typography>\n </Stack>\n </Stack>\n )}\n </CardDisplay>\n )\n}\nStaffOrgAiCard.displayName = 'StaffOrgAiCard'\n\nexport default StaffOrgAiCard\n"],"names":["aiAddonName","buildRoute","Route","pluginDocsHelp","mdiOpenInNew","AppLink","CardDisplay","ListRowActions","ListTable","listActionsColumn","ScrollTable","TABLE_ROW_HEIGHT","useUser","authorizedFetch","Alert","Chip","Stack","TableBody","TableCell","TableHead","TableRow","Typography","useRouter","useCallback","useEffect","useMemo","useRef","useState","StaffCreditReturnActions","StaffCreditReturnDialog","credits","value","Math","round","toLocaleString","usd","amount","Number","toFixed","abs","when","iso","Date","toLocaleDateString","poolPartsSentence","pool","parts","overrideCredits","push","planCredits","addonCredits","join","overageStateSentence","overage","uncapped","hardCap","sellsOverage","rate","rateUsdPer1k","capUsd","capReached","marginTone","margin","underwater","multiple","RefusalsRow","refusals","labels","band","cap","messages","budget","allotment","account","requests","platform","direction","spacing","useFlexGap","sx","flexWrap","Object","keys","map","reason","count","size","variant","color","label","JobsSection","jobs","onGiveBack","columns","jobColumns","severity","total","values","counts","reduce","sum","n","status","replace","done","truncated","rows","recent","getRowId","job","id","rowHeight","noRowsLabel","disableColumnFilter","quickFilter","field","headerName","flex","minWidth","renderCell","row","noWrap","title","fontFamily","width","valueFormatter","String","type","align","headerAlign","creditsReserved","createdBy","href","ADMIN_USER_DETAIL","uid","underline","items","key","disabled","creditsSpent","disabledReason","undefined","onClick","tokenCount","TokensSection","tokens","alignItems","input","cached","cacheWrite","output","cacheHitRate","kinds","length","kind","costPerRequestUsd","TopUsersSection","users","router","name","quick","icon","path","to","onOpen","StaffOrgAiCard","orgId","data","user","signedInUid","userRef","current","setData","ready","setReady","error","setError","version","setVersion","notice","setNotice","returning","setReturning","giveBackJob","mode","jobId","jobCredits","active","response","encodeURIComponent","payload","json","catch","ok","header","help","anchor","excerpt","HeaderProps","action","onGive","onReset","contentGutterX","contentGutterY","open","target","workspace","used","usedCredits","limit","totalCredits","limitCredits","onClose","onReturned","message","mb","addon","on","priceUsd","since","sinceSource","month","providerUsd","remainingCredits","returnedCredits","projectedCredits","projectedUsd","deflected","overageCredits","accruedUsd","spendUsd","revenueUsd","addonRevenueUsd","planAssistShareUsd","overageRevenueUsd","thresholdUsd","contribution","marginPct","ADMIN_ORGS","ADMIN_LOCKDOWN","displayName"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
declare const StaffOrgAiConversations: {
|
|
18
|
+
({ orgId }: {
|
|
19
|
+
orgId: string;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
displayName: string;
|
|
22
|
+
};
|
|
23
|
+
export default StaffOrgAiConversations;
|