@aglyn/plugins-ai 1.0.0-beta.228 → 1.0.0-beta.230
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/README.md +3 -3
- package/package.json +17 -17
- package/src/lib/activity/ai-activity-actions.js +7 -1
- package/src/lib/activity/ai-activity-actions.js.map +1 -1
- package/src/lib/components/ai-automation-zones.d.ts +38 -0
- package/src/lib/components/ai-automation-zones.js +5 -1
- package/src/lib/components/ai-automation-zones.js.map +1 -1
- package/src/lib/components/ai-brief-dialog.component.d.ts +20 -2
- package/src/lib/components/ai-brief-dialog.component.js +87 -8
- package/src/lib/components/ai-brief-dialog.component.js.map +1 -1
- package/src/lib/components/ai-campaign-create.component.d.ts +54 -0
- package/src/lib/components/ai-campaign-create.component.js +270 -0
- package/src/lib/components/ai-campaign-create.component.js.map +1 -0
- package/src/lib/components/ai-describe-automation.component.d.ts +8 -3
- package/src/lib/components/ai-describe-automation.component.js +15 -6
- package/src/lib/components/ai-describe-automation.component.js.map +1 -1
- package/src/lib/components/ai-describe-button.component.d.ts +20 -8
- package/src/lib/components/ai-describe-button.component.js +19 -36
- package/src/lib/components/ai-describe-button.component.js.map +1 -1
- package/src/lib/components/ai-describe-email.component.d.ts +31 -0
- package/src/lib/components/ai-describe-email.component.js +22 -0
- package/src/lib/components/ai-describe-email.component.js.map +1 -0
- package/src/lib/components/ai-describe-org-automation.component.d.ts +36 -0
- package/src/lib/components/ai-describe-org-automation.component.js +238 -0
- package/src/lib/components/ai-describe-org-automation.component.js.map +1 -0
- package/src/lib/components/ai-experiment-cards.component.d.ts +9 -1
- package/src/lib/components/ai-experiment-cards.component.js +104 -18
- package/src/lib/components/ai-experiment-cards.component.js.map +1 -1
- package/src/lib/components/ai-explain-automation.component.js +1 -5
- package/src/lib/components/ai-explain-automation.component.js.map +1 -1
- package/src/lib/components/ai-funnel-zones.component.d.ts +61 -0
- package/src/lib/components/ai-funnel-zones.component.js +178 -0
- package/src/lib/components/ai-funnel-zones.component.js.map +1 -0
- package/src/lib/components/ai-insight-card.component.d.ts +42 -0
- package/src/lib/components/ai-insight-card.component.js +137 -0
- package/src/lib/components/ai-insight-card.component.js.map +1 -0
- package/src/lib/components/ai-insight-dialog.component.d.ts +6 -0
- package/src/lib/components/ai-insight-dialog.component.js +9 -3
- package/src/lib/components/ai-insight-dialog.component.js.map +1 -1
- package/src/lib/components/ai-job-plan.component.d.ts +7 -2
- package/src/lib/components/ai-job-plan.component.js +35 -5
- package/src/lib/components/ai-job-plan.component.js.map +1 -1
- package/src/lib/components/ai-job-requests.d.ts +17 -1
- package/src/lib/components/ai-job-requests.js +39 -3
- package/src/lib/components/ai-job-requests.js.map +1 -1
- package/src/lib/components/ai-jobs-list-page.component.js +12 -1
- package/src/lib/components/ai-jobs-list-page.component.js.map +1 -1
- package/src/lib/components/ai-logic-zones.d.ts +64 -0
- package/src/lib/components/ai-logic-zones.js +18 -0
- package/src/lib/components/ai-logic-zones.js.map +1 -0
- package/src/lib/components/ai-logic.component.d.ts +77 -0
- package/src/lib/components/ai-logic.component.js +553 -0
- package/src/lib/components/ai-logic.component.js.map +1 -0
- package/src/lib/components/ai-marketing-insight.component.d.ts +30 -0
- package/src/lib/components/ai-marketing-insight.component.js +80 -0
- package/src/lib/components/ai-marketing-insight.component.js.map +1 -0
- package/src/lib/components/ai-media-create.component.d.ts +55 -0
- package/src/lib/components/ai-media-create.component.js +433 -0
- package/src/lib/components/ai-media-create.component.js.map +1 -0
- package/src/lib/components/ai-overlay-cards.component.d.ts +83 -0
- package/src/lib/components/ai-overlay-cards.component.js +505 -0
- package/src/lib/components/ai-overlay-cards.component.js.map +1 -0
- package/src/lib/components/ai-product-zones.d.ts +5 -0
- package/src/lib/components/ai-product-zones.js +1 -1
- package/src/lib/components/ai-product-zones.js.map +1 -1
- package/src/lib/components/ai-products-create-button.component.d.ts +42 -0
- package/src/lib/components/ai-products-create-button.component.js +67 -0
- package/src/lib/components/ai-products-create-button.component.js.map +1 -0
- package/src/lib/components/ai-products-hub-card.component.js +100 -81
- package/src/lib/components/ai-products-hub-card.component.js.map +1 -1
- package/src/lib/components/ai-revise-automation.component.d.ts +16 -0
- package/src/lib/components/ai-revise-automation.component.js +207 -0
- package/src/lib/components/ai-revise-automation.component.js.map +1 -0
- package/src/lib/components/ai-site-build-page.component.d.ts +1 -1
- package/src/lib/components/ai-site-build-page.component.js +177 -20
- package/src/lib/components/ai-site-build-page.component.js.map +1 -1
- package/src/lib/components/ai-site-start-card.component.d.ts +10 -1
- package/src/lib/components/ai-site-start-card.component.js +7 -4
- package/src/lib/components/ai-site-start-card.component.js.map +1 -1
- package/src/lib/components/ai-upsell-dialog.component.d.ts +53 -0
- package/src/lib/components/ai-upsell-dialog.component.js +177 -0
- package/src/lib/components/ai-upsell-dialog.component.js.map +1 -0
- package/src/lib/components/assist-build-card.component.d.ts +42 -0
- package/src/lib/components/assist-build-card.component.js +183 -0
- package/src/lib/components/assist-build-card.component.js.map +1 -0
- package/src/lib/components/assist-edit-canvas.d.ts +2 -0
- package/src/lib/components/assist-edit-canvas.js +56 -1
- package/src/lib/components/assist-edit-canvas.js.map +1 -1
- package/src/lib/components/assist-jobs-drawer.component.d.ts +11 -0
- package/src/lib/components/assist-jobs-drawer.component.js +85 -4
- package/src/lib/components/assist-jobs-drawer.component.js.map +1 -1
- package/src/lib/components/assist-panel.component.js +51 -2
- package/src/lib/components/assist-panel.component.js.map +1 -1
- package/src/lib/components/use-ai-job-run.d.ts +3 -14
- package/src/lib/components/use-ai-job-run.js +0 -50
- package/src/lib/components/use-ai-job-run.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.d.ts +58 -0
- package/src/lib/jobs/ai-build-capabilities.js +256 -0
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -0
- package/src/lib/jobs/ai-build-unit-outcome.d.ts +52 -0
- package/src/lib/jobs/ai-build-unit-outcome.js +138 -0
- package/src/lib/jobs/ai-build-unit-outcome.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.d.ts +144 -0
- package/src/lib/jobs/ai-job-build-step.js +657 -0
- package/src/lib/jobs/ai-job-build-step.js.map +1 -0
- package/src/lib/jobs/ai-job-draft-ids.js +9 -1
- package/src/lib/jobs/ai-job-draft-ids.js.map +1 -1
- package/src/lib/jobs/ai-job-drafts.d.ts +14 -4
- package/src/lib/jobs/ai-job-drafts.js +25 -4
- package/src/lib/jobs/ai-job-drafts.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-site-pages.d.ts +70 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js +216 -0
- package/src/lib/jobs/ai-job-layout-site-pages.js.map +1 -0
- package/src/lib/jobs/ai-job-layout-step.d.ts +2 -2
- package/src/lib/jobs/ai-job-layout-step.js +19 -1
- package/src/lib/jobs/ai-job-layout-step.js.map +1 -1
- package/src/lib/jobs/ai-job-logic-step.d.ts +107 -0
- package/src/lib/jobs/ai-job-logic-step.js +395 -0
- package/src/lib/jobs/ai-job-logic-step.js.map +1 -0
- package/src/lib/jobs/ai-job-overlay-copy.d.ts +70 -0
- package/src/lib/jobs/ai-job-overlay-copy.js +178 -0
- package/src/lib/jobs/ai-job-overlay-copy.js.map +1 -0
- package/src/lib/jobs/ai-job-page-step.js +6 -6
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +26 -1
- package/src/lib/jobs/ai-job-plan-step.js +79 -11
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.d.ts +15 -1
- package/src/lib/jobs/ai-job-site-step.js +207 -58
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.d.ts +22 -1
- package/src/lib/jobs/ai-job-text-step.js +12 -0
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-workflow-step.d.ts +54 -4
- package/src/lib/jobs/ai-job-workflow-step.js +318 -13
- package/src/lib/jobs/ai-job-workflow-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs.d.ts +90 -2
- package/src/lib/jobs/ai-jobs.js +495 -61
- package/src/lib/jobs/ai-jobs.js.map +1 -1
- package/src/lib/jobs/ai-logic-records.d.ts +40 -0
- package/src/lib/jobs/ai-logic-records.js +55 -0
- package/src/lib/jobs/ai-logic-records.js.map +1 -0
- package/src/lib/jobs/ai-site-publish.d.ts +7 -0
- package/src/lib/jobs/ai-site-publish.js +9 -0
- package/src/lib/jobs/ai-site-publish.js.map +1 -1
- package/src/lib/jobs/ai-workflow-records.d.ts +4 -0
- package/src/lib/jobs/ai-workflow-records.js +40 -1
- package/src/lib/jobs/ai-workflow-records.js.map +1 -1
- package/src/lib/model/ai-automation-draft.d.ts +18 -0
- package/src/lib/model/ai-automation-draft.js +86 -0
- package/src/lib/model/ai-automation-draft.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +201 -0
- package/src/lib/model/ai-build-job.js +471 -0
- package/src/lib/model/ai-build-job.js.map +1 -0
- package/src/lib/model/ai-build-plan.d.ts +61 -0
- package/src/lib/model/ai-build-plan.js +154 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.d.ts +56 -0
- package/src/lib/model/ai-build-progress.js +128 -0
- package/src/lib/model/ai-build-progress.js.map +1 -0
- package/src/lib/model/ai-insight.d.ts +20 -10
- package/src/lib/model/ai-insight.js +72 -9
- package/src/lib/model/ai-insight.js.map +1 -1
- package/src/lib/model/ai-job-activity.js +3 -1
- package/src/lib/model/ai-job-activity.js.map +1 -1
- package/src/lib/model/ai-job-failure-copy.d.ts +20 -1
- package/src/lib/model/ai-job-failure-copy.js +28 -1
- package/src/lib/model/ai-job-failure-copy.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +3 -0
- package/src/lib/model/ai-job-notice.js +11 -2
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +87 -2
- package/src/lib/model/ai-jobs.types.js +3 -1
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-logic-job.d.ts +108 -0
- package/src/lib/model/ai-logic-job.js +327 -0
- package/src/lib/model/ai-logic-job.js.map +1 -0
- package/src/lib/model/ai-media-credits.d.ts +52 -0
- package/src/lib/model/ai-media-credits.js +61 -0
- package/src/lib/model/ai-media-credits.js.map +1 -0
- package/src/lib/model/ai-overlay-copy.d.ts +114 -0
- package/src/lib/model/ai-overlay-copy.js +175 -0
- package/src/lib/model/ai-overlay-copy.js.map +1 -0
- package/src/lib/model/ai-site-build-progress.d.ts +28 -3
- package/src/lib/model/ai-site-build-progress.js +231 -17
- package/src/lib/model/ai-site-build-progress.js.map +1 -1
- package/src/lib/model/ai-site-start-seo.d.ts +16 -0
- package/src/lib/model/ai-site-start-seo.js +54 -4
- package/src/lib/model/ai-site-start-seo.js.map +1 -1
- package/src/lib/model/ai-site-start.d.ts +14 -0
- package/src/lib/model/ai-site-start.js +43 -0
- package/src/lib/model/ai-site-start.js.map +1 -1
- package/src/lib/model/ai-svg.d.ts +69 -0
- package/src/lib/model/ai-svg.js +265 -0
- package/src/lib/model/ai-svg.js.map +1 -0
- package/src/lib/model/ai-usage-by-user.d.ts +3 -2
- package/src/lib/model/ai-usage-by-user.js.map +1 -1
- package/src/lib/model/ai-workflow-job.d.ts +92 -4
- package/src/lib/model/ai-workflow-job.js +124 -7
- package/src/lib/model/ai-workflow-job.js.map +1 -1
- package/src/lib/model/assist-build.d.ts +58 -0
- package/src/lib/model/assist-build.js +52 -0
- package/src/lib/model/assist-build.js.map +1 -0
- package/src/lib/model/assist-edit.d.ts +48 -3
- package/src/lib/model/assist-edit.js +48 -4
- package/src/lib/model/assist-edit.js.map +1 -1
- package/src/lib/plugin-config.js +2 -0
- package/src/lib/plugin-config.js.map +1 -1
- package/src/lib/plugin.js +240 -11
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/catalog.d.ts +81 -4
- package/src/lib/providers/catalog.js +80 -9
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/image-contract.d.ts +116 -0
- package/src/lib/providers/image-contract.js +74 -0
- package/src/lib/providers/image-contract.js.map +1 -0
- package/src/lib/providers/model-choice.js +11 -0
- 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.d.ts +121 -0
- package/src/lib/providers/vertex-image.js +312 -0
- package/src/lib/providers/vertex-image.js.map +1 -0
- package/src/lib/runtime/ai-doctrine-validators.d.ts +19 -0
- package/src/lib/runtime/ai-doctrine-validators.js +89 -0
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +6 -0
- package/src/lib/runtime/ai-doctrine.js +16 -6
- 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 +15 -1
- package/src/lib/runtime/ai-eval.js +52 -1
- package/src/lib/runtime/ai-eval.js.map +1 -1
- package/src/lib/runtime/experiment-variant-copy.d.ts +34 -0
- package/src/lib/runtime/experiment-variant-copy.js +102 -0
- package/src/lib/runtime/experiment-variant-copy.js.map +1 -0
- package/src/lib/server/ai-experiment-versions.d.ts +51 -0
- package/src/lib/server/ai-experiment-versions.js +270 -0
- package/src/lib/server/ai-experiment-versions.js.map +1 -0
- package/src/lib/server/ai-jobs-resume.js +74 -4
- package/src/lib/server/ai-jobs-resume.js.map +1 -1
- package/src/lib/server/ai-media-image.d.ts +66 -0
- package/src/lib/server/ai-media-image.js +521 -0
- package/src/lib/server/ai-media-image.js.map +1 -0
- package/src/lib/server/ai-media-svg.d.ts +91 -0
- package/src/lib/server/ai-media-svg.js +186 -0
- package/src/lib/server/ai-media-svg.js.map +1 -0
- package/src/lib/server/assist-build.d.ts +54 -0
- package/src/lib/server/assist-build.js +121 -0
- package/src/lib/server/assist-build.js.map +1 -0
- package/src/lib/server/assist-chat.js +108 -12
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-edit.js +213 -3
- package/src/lib/server/assist-edit.js.map +1 -1
- package/src/lib/server/assist-view-context.js +242 -4
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +26 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-logic-tool.d.ts +27 -0
- package/src/lib/tools/ai-logic-tool.js +207 -0
- package/src/lib/tools/ai-logic-tool.js.map +1 -0
- package/src/lib/tools/ai-overlay-tool.d.ts +35 -0
- package/src/lib/tools/ai-overlay-tool.js +107 -0
- package/src/lib/tools/ai-overlay-tool.js.map +1 -0
- package/src/lib/tools/ai-workflow-tool.d.ts +11 -0
- package/src/lib/tools/ai-workflow-tool.js +31 -0
- package/src/lib/tools/ai-workflow-tool.js.map +1 -1
- package/src/lib/usage/assist-job-refund.d.ts +31 -3
- package/src/lib/usage/assist-job-refund.js +46 -10
- package/src/lib/usage/assist-job-refund.js.map +1 -1
- package/src/lib/usage/assist-usage.d.ts +5 -0
- package/src/lib/usage/assist-usage.js +2 -0
- package/src/lib/usage/assist-usage.js.map +1 -1
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2026 Aglyn LLC
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this file except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*/ import { PLATFORM_BRAND_NAME } from "@aglyn/aglyn";
|
|
18
|
+
import { mdiCreation } from "@aglyn/shared-data-mdi";
|
|
19
|
+
import { MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
20
|
+
import { useUser } from "@aglyn/tenant-feature-instance";
|
|
21
|
+
import { Alert, Button, Dialog, DialogActions, DialogContent, DialogTitle, Stack, TextField, Typography } from "@mui/material";
|
|
22
|
+
import { usePathname } from "next/navigation";
|
|
23
|
+
import { useState } from "react";
|
|
24
|
+
import { aiInsightSurfaceForPath } from "../model/ai-insight.js";
|
|
25
|
+
import { AiInsightDialog } from "./ai-insight-dialog.component.js";
|
|
26
|
+
import { AiUpsellButton } from "./ai-upsell-dialog.component.js";
|
|
27
|
+
export const AI_FUNNEL_COPY = {
|
|
28
|
+
create: 'Create with AI',
|
|
29
|
+
title: 'Describe a funnel',
|
|
30
|
+
label: 'Which steps should visitors take?',
|
|
31
|
+
placeholder: 'People who read a blog post, then viewed pricing, then booked a consultation',
|
|
32
|
+
next: `${PLATFORM_BRAND_NAME} AI drafts the steps from your site’s real pages, forms and products. You review the draft before anything is saved.`,
|
|
33
|
+
submit: 'Draft the funnel',
|
|
34
|
+
ask: 'Ask AI about this funnel'
|
|
35
|
+
};
|
|
36
|
+
/** The question "Ask AI about this funnel" opens with. */ export function aiFunnelQuestion(funnelName, days) {
|
|
37
|
+
return `In my funnel "${funnelName}" over the last ${days} days, where do visitors drop off most, and what might explain it?`;
|
|
38
|
+
}
|
|
39
|
+
export function AiFunnelCreateButton({ propose, entitled, upgrade }) {
|
|
40
|
+
const [open, setOpen] = useState(false);
|
|
41
|
+
const [brief, setBrief] = useState('');
|
|
42
|
+
const [busy, setBusy] = useState(false);
|
|
43
|
+
const [error, setError] = useState(null);
|
|
44
|
+
if (entitled === false) {
|
|
45
|
+
return upgrade ? /*#__PURE__*/ _jsx(AiUpsellButton, {
|
|
46
|
+
kind: "funnel",
|
|
47
|
+
upgrade: upgrade
|
|
48
|
+
}) : null;
|
|
49
|
+
}
|
|
50
|
+
const submit = async ()=>{
|
|
51
|
+
setBusy(true);
|
|
52
|
+
setError(null);
|
|
53
|
+
const problem = await propose(brief.trim());
|
|
54
|
+
setBusy(false);
|
|
55
|
+
if (problem) setError(problem);
|
|
56
|
+
else {
|
|
57
|
+
setOpen(false);
|
|
58
|
+
setBrief('');
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
62
|
+
children: [
|
|
63
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
64
|
+
size: "small",
|
|
65
|
+
variant: "outlined",
|
|
66
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
67
|
+
path: mdiCreation.path
|
|
68
|
+
}),
|
|
69
|
+
onClick: ()=>setOpen(true),
|
|
70
|
+
children: AI_FUNNEL_COPY.create
|
|
71
|
+
}),
|
|
72
|
+
/*#__PURE__*/ _jsxs(Dialog, {
|
|
73
|
+
open: open,
|
|
74
|
+
onClose: busy ? undefined : ()=>setOpen(false),
|
|
75
|
+
fullWidth: true,
|
|
76
|
+
maxWidth: "sm",
|
|
77
|
+
children: [
|
|
78
|
+
/*#__PURE__*/ _jsx(DialogTitle, {
|
|
79
|
+
children: AI_FUNNEL_COPY.title
|
|
80
|
+
}),
|
|
81
|
+
/*#__PURE__*/ _jsx(DialogContent, {
|
|
82
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
83
|
+
spacing: 2,
|
|
84
|
+
sx: {
|
|
85
|
+
pt: 1
|
|
86
|
+
},
|
|
87
|
+
children: [
|
|
88
|
+
/*#__PURE__*/ _jsx(TextField, {
|
|
89
|
+
label: AI_FUNNEL_COPY.label,
|
|
90
|
+
placeholder: AI_FUNNEL_COPY.placeholder,
|
|
91
|
+
value: brief,
|
|
92
|
+
multiline: true,
|
|
93
|
+
minRows: 3,
|
|
94
|
+
slotProps: {
|
|
95
|
+
htmlInput: {
|
|
96
|
+
maxLength: 1000
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
onChange: (event)=>setBrief(event.target.value)
|
|
100
|
+
}),
|
|
101
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
102
|
+
variant: "body2",
|
|
103
|
+
color: "text.secondary",
|
|
104
|
+
children: AI_FUNNEL_COPY.next
|
|
105
|
+
}),
|
|
106
|
+
error ? /*#__PURE__*/ _jsx(Alert, {
|
|
107
|
+
severity: "error",
|
|
108
|
+
children: error
|
|
109
|
+
}) : null
|
|
110
|
+
]
|
|
111
|
+
})
|
|
112
|
+
}),
|
|
113
|
+
/*#__PURE__*/ _jsxs(DialogActions, {
|
|
114
|
+
children: [
|
|
115
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
116
|
+
onClick: ()=>setOpen(false),
|
|
117
|
+
disabled: busy,
|
|
118
|
+
children: 'Cancel'
|
|
119
|
+
}),
|
|
120
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
121
|
+
variant: "contained",
|
|
122
|
+
onClick: ()=>void submit(),
|
|
123
|
+
disabled: busy || !brief.trim(),
|
|
124
|
+
children: busy ? 'Drafting…' : AI_FUNNEL_COPY.submit
|
|
125
|
+
})
|
|
126
|
+
]
|
|
127
|
+
})
|
|
128
|
+
]
|
|
129
|
+
})
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function AiFunnelAskButton({ hostId, orgId, funnelName, days, entitled, upgrade }) {
|
|
134
|
+
var _String_split_filter_, _ref, _ref1;
|
|
135
|
+
var _aiInsightSurfaceForPath;
|
|
136
|
+
const { data: user } = useUser();
|
|
137
|
+
const pathname = usePathname();
|
|
138
|
+
const [open, setOpen] = useState(false);
|
|
139
|
+
if (entitled === false) {
|
|
140
|
+
return upgrade ? /*#__PURE__*/ _jsx(AiUpsellButton, {
|
|
141
|
+
kind: "insight",
|
|
142
|
+
upgrade: upgrade
|
|
143
|
+
}) : null;
|
|
144
|
+
}
|
|
145
|
+
if (!orgId) return null;
|
|
146
|
+
const orgSlug = (_String_split_filter_ = String(pathname != null ? pathname : '').split('/').filter(Boolean)[0]) != null ? _String_split_filter_ : '';
|
|
147
|
+
const host = (_ref = (_aiInsightSurfaceForPath = aiInsightSurfaceForPath(pathname)) == null ? void 0 : _aiInsightSurfaceForPath.host) != null ? _ref : null;
|
|
148
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
149
|
+
children: [
|
|
150
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
151
|
+
size: "small",
|
|
152
|
+
variant: "outlined",
|
|
153
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
154
|
+
path: mdiCreation.path
|
|
155
|
+
}),
|
|
156
|
+
onClick: ()=>setOpen(true),
|
|
157
|
+
sx: {
|
|
158
|
+
alignSelf: 'flex-start'
|
|
159
|
+
},
|
|
160
|
+
children: AI_FUNNEL_COPY.ask
|
|
161
|
+
}),
|
|
162
|
+
/*#__PURE__*/ _jsx(AiInsightDialog, {
|
|
163
|
+
open: open,
|
|
164
|
+
onClose: ()=>setOpen(false),
|
|
165
|
+
orgId: orgId,
|
|
166
|
+
orgSlug: orgSlug,
|
|
167
|
+
hostId: hostId,
|
|
168
|
+
host: host,
|
|
169
|
+
surface: "analytics",
|
|
170
|
+
user: user,
|
|
171
|
+
uid: (_ref1 = user == null ? void 0 : user.uid) != null ? _ref1 : null,
|
|
172
|
+
question: aiFunnelQuestion(funnelName, days)
|
|
173
|
+
})
|
|
174
|
+
]
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=ai-funnel-zones.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-funnel-zones.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\nimport { PLATFORM_BRAND_NAME } from '@aglyn/aglyn'\nimport type { ConsoleWidgetEntitlementProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport { mdiCreation } from '@aglyn/shared-data-mdi'\nimport { MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useUser } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n TextField,\n Typography,\n} from '@mui/material'\nimport { usePathname } from 'next/navigation'\nimport { useState } from 'react'\nimport { aiInsightSurfaceForPath } from '../model/ai-insight'\nimport { AiInsightDialog } from './ai-insight-dialog.component'\nimport { AiUpsellButton } from './ai-upsell-dialog.component'\n\n/**\n * Funnels by AI (AGL-3605), in the two zones the funnels plugin hosts on its\n * card. Both are UI only:\n *\n * - **Create with AI** (`funnelsCreate`) takes a description and hands it to\n * the card's `propose`, which asks the funnels plugin's own door for a draft\n * checked against the site — through core's text-generation seam, so it is\n * metered and gated by this plugin's generator like any other generation.\n * - **Ask AI about this funnel** (`funnelInsight`) opens the insight dialog\n * with a question about the funnel shown; the insight job reads the\n * `funnels.*` figures the funnels plugin registers.\n *\n * Both draw at once from the shell's own gates — the `ai.generate`\n * permission, the plan's `aiGenerative` feature and the site's AI switch,\n * all settled before the slot mounts a widget — and ask the server nothing\n * until they are used: a refusal (the release flag, credits, a lockdown) is\n * the door's to say, in the dialog, when the description or question is sent.\n */\n\n/** The props the funnels plugin hands `funnelsCreate`, restated: plugins do not import each other. */\nexport interface ConsoleFunnelsCreateZoneProps {\n hostId: string\n orgId: string | undefined\n propose: (brief: string) => Promise<string | null>\n}\n\n/** The props the funnels plugin hands `funnelInsight`, restated. */\nexport interface ConsoleFunnelInsightZoneProps {\n hostId: string\n orgId: string | undefined\n funnelName: string\n days: number\n}\n\nexport const AI_FUNNEL_COPY = {\n create: 'Create with AI',\n title: 'Describe a funnel',\n label: 'Which steps should visitors take?',\n placeholder: 'People who read a blog post, then viewed pricing, then booked a consultation',\n next: `${PLATFORM_BRAND_NAME} AI drafts the steps from your site’s real pages, forms and products. You review the draft before anything is saved.`,\n submit: 'Draft the funnel',\n ask: 'Ask AI about this funnel',\n} as const\n\n/** The question \"Ask AI about this funnel\" opens with. */\nexport function aiFunnelQuestion(funnelName: string, days: number): string {\n return `In my funnel \"${funnelName}\" over the last ${days} days, where do visitors drop off most, and what might explain it?`\n}\n\nexport function AiFunnelCreateButton({\n propose,\n entitled,\n upgrade,\n}: ConsoleFunnelsCreateZoneProps & ConsoleWidgetEntitlementProps) {\n const [open, setOpen] = useState(false)\n const [brief, setBrief] = useState('')\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n if (entitled === false) {\n return upgrade ? <AiUpsellButton kind=\"funnel\" upgrade={upgrade} /> : null\n }\n\n const submit = async () => {\n setBusy(true)\n setError(null)\n const problem = await propose(brief.trim())\n setBusy(false)\n if (problem) setError(problem)\n else {\n setOpen(false)\n setBrief('')\n }\n }\n\n return (\n <>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n onClick={() => setOpen(true)}\n >\n {AI_FUNNEL_COPY.create}\n </Button>\n <Dialog open={open} onClose={busy ? undefined : () => setOpen(false)} fullWidth maxWidth=\"sm\">\n <DialogTitle>{AI_FUNNEL_COPY.title}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n <TextField\n label={AI_FUNNEL_COPY.label}\n placeholder={AI_FUNNEL_COPY.placeholder}\n value={brief}\n multiline\n minRows={3}\n slotProps={{ htmlInput: { maxLength: 1_000 } }}\n onChange={(event) => setBrief(event.target.value)}\n />\n <Typography variant=\"body2\" color=\"text.secondary\">\n {AI_FUNNEL_COPY.next}\n </Typography>\n {error ? <Alert severity=\"error\">{error}</Alert> : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setOpen(false)} disabled={busy}>\n {'Cancel'}\n </Button>\n <Button variant=\"contained\" onClick={() => void submit()} disabled={busy || !brief.trim()}>\n {busy ? 'Drafting…' : AI_FUNNEL_COPY.submit}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n )\n}\n\nexport function AiFunnelAskButton({\n hostId,\n orgId,\n funnelName,\n days,\n entitled,\n upgrade,\n}: ConsoleFunnelInsightZoneProps & ConsoleWidgetEntitlementProps) {\n const { data: user } = useUser()\n const pathname = usePathname()\n const [open, setOpen] = useState(false)\n\n if (entitled === false) {\n return upgrade ? <AiUpsellButton kind=\"insight\" upgrade={upgrade} /> : null\n }\n if (!orgId) return null\n const orgSlug = String(pathname ?? '').split('/').filter(Boolean)[0] ?? ''\n const host = aiInsightSurfaceForPath(pathname)?.host ?? null\n\n return (\n <>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n onClick={() => setOpen(true)}\n sx={{ alignSelf: 'flex-start' }}\n >\n {AI_FUNNEL_COPY.ask}\n </Button>\n <AiInsightDialog\n open={open}\n onClose={() => setOpen(false)}\n orgId={orgId}\n orgSlug={orgSlug}\n hostId={hostId}\n host={host}\n surface=\"analytics\"\n user={user}\n uid={(user as { uid?: string } | null | undefined)?.uid ?? null}\n question={aiFunnelQuestion(funnelName, days)}\n />\n </>\n )\n}\n"],"names":["PLATFORM_BRAND_NAME","mdiCreation","MdiIcon","useUser","Alert","Button","Dialog","DialogActions","DialogContent","DialogTitle","Stack","TextField","Typography","usePathname","useState","aiInsightSurfaceForPath","AiInsightDialog","AiUpsellButton","AI_FUNNEL_COPY","create","title","label","placeholder","next","submit","ask","aiFunnelQuestion","funnelName","days","AiFunnelCreateButton","propose","entitled","upgrade","open","setOpen","brief","setBrief","busy","setBusy","error","setError","kind","problem","trim","size","variant","startIcon","path","onClick","onClose","undefined","fullWidth","maxWidth","spacing","sx","pt","value","multiline","minRows","slotProps","htmlInput","maxLength","onChange","event","target","color","severity","disabled","AiFunnelAskButton","hostId","orgId","String","data","user","pathname","orgSlug","split","filter","Boolean","host","alignSelf","surface","uid","question"],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
import type { ConsoleWidgetEntitlementProps } from '@aglyn/aglyn/plugin-manager/feature-plugins';
|
|
18
|
+
import type { ConsolePluginOrgMount } from '@aglyn/aglyn';
|
|
19
|
+
/**
|
|
20
|
+
* "Ask AI about these numbers" (AGL-3603): a tile on a site's dashboard and
|
|
21
|
+
* Analytics page (`hostDashboard`, which both draw) and on the workspace's
|
|
22
|
+
* sites page (`orgDashboard`), opening the insight dialog the Assist panel
|
|
23
|
+
* opens. A question becomes an `insight` job — the figures read by code
|
|
24
|
+
* through the readers the surface offers, every insight traced to the rows
|
|
25
|
+
* it cites — charged as that job is. Drawn from the shell's gates alone
|
|
26
|
+
* (AGL-3601): nothing asks a server until a question is asked, and on a plan
|
|
27
|
+
* without the AI add-on the same tile opens the add-on's dialog.
|
|
28
|
+
*/
|
|
29
|
+
export declare const AI_INSIGHT_CARD_COPY: {
|
|
30
|
+
readonly title: "Ask AI about these numbers";
|
|
31
|
+
readonly site: "Ask a question about this site’s traffic, sales, bookings, automation runs and pipeline. Every answer shows the figures it is built from.";
|
|
32
|
+
readonly workspace: "Ask a question about your workspace’s pipeline, the deals it closed and your datasets. Every answer shows the figures it is built from.";
|
|
33
|
+
readonly action: "Ask a question";
|
|
34
|
+
};
|
|
35
|
+
/** On a site's dashboard and its Analytics page. */
|
|
36
|
+
export declare function AiInsightHostCard({ hostId, entitled, upgrade, }: {
|
|
37
|
+
hostId: string;
|
|
38
|
+
} & ConsoleWidgetEntitlementProps): import("react").JSX.Element;
|
|
39
|
+
/** On the workspace's sites page, totaling the organization rather than one site. */
|
|
40
|
+
export declare function AiInsightOrgCard({ orgMount, entitled, upgrade, }: {
|
|
41
|
+
orgMount?: ConsolePluginOrgMount | null;
|
|
42
|
+
} & ConsoleWidgetEntitlementProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { pluginDocsHelp } from "@aglyn/aglyn/app-utils/docs-help";
|
|
4
|
+
import { mdiCreation } from "@aglyn/shared-data-mdi";
|
|
5
|
+
import { CardDisplay, MdiIcon } from "@aglyn/shared-ui-jsx";
|
|
6
|
+
import { useConsoleHostRoute, useHostOrgId, useUser } from "@aglyn/tenant-feature-instance";
|
|
7
|
+
import { Button, Stack, Typography } from "@mui/material";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { AiInsightDialog } from "./ai-insight-dialog.component.js";
|
|
10
|
+
import { AiUpsellDialog } from "./ai-upsell-dialog.component.js";
|
|
11
|
+
/**
|
|
12
|
+
* "Ask AI about these numbers" (AGL-3603): a tile on a site's dashboard and
|
|
13
|
+
* Analytics page (`hostDashboard`, which both draw) and on the workspace's
|
|
14
|
+
* sites page (`orgDashboard`), opening the insight dialog the Assist panel
|
|
15
|
+
* opens. A question becomes an `insight` job — the figures read by code
|
|
16
|
+
* through the readers the surface offers, every insight traced to the rows
|
|
17
|
+
* it cites — charged as that job is. Drawn from the shell's gates alone
|
|
18
|
+
* (AGL-3601): nothing asks a server until a question is asked, and on a plan
|
|
19
|
+
* without the AI add-on the same tile opens the add-on's dialog.
|
|
20
|
+
*/ export const AI_INSIGHT_CARD_COPY = {
|
|
21
|
+
title: 'Ask AI about these numbers',
|
|
22
|
+
site: 'Ask a question about this site’s traffic, sales, bookings, automation runs and pipeline. Every answer shows the figures it is built from.',
|
|
23
|
+
workspace: 'Ask a question about your workspace’s pipeline, the deals it closed and your datasets. Every answer shows the figures it is built from.',
|
|
24
|
+
action: 'Ask a question'
|
|
25
|
+
};
|
|
26
|
+
function InsightTile(props) {
|
|
27
|
+
const help = pluginDocsHelp('aiInsights');
|
|
28
|
+
return /*#__PURE__*/ _jsx(CardDisplay, {
|
|
29
|
+
header: AI_INSIGHT_CARD_COPY.title,
|
|
30
|
+
help: help,
|
|
31
|
+
contentGutterX: true,
|
|
32
|
+
contentGutterY: true,
|
|
33
|
+
children: /*#__PURE__*/ _jsxs(Stack, {
|
|
34
|
+
spacing: 1.5,
|
|
35
|
+
sx: {
|
|
36
|
+
alignItems: 'flex-start'
|
|
37
|
+
},
|
|
38
|
+
children: [
|
|
39
|
+
/*#__PURE__*/ _jsx(Typography, {
|
|
40
|
+
variant: "body2",
|
|
41
|
+
color: "text.secondary",
|
|
42
|
+
children: props.text
|
|
43
|
+
}),
|
|
44
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
45
|
+
size: "small",
|
|
46
|
+
variant: "outlined",
|
|
47
|
+
startIcon: /*#__PURE__*/ _jsx(MdiIcon, {
|
|
48
|
+
path: mdiCreation.path
|
|
49
|
+
}),
|
|
50
|
+
onClick: props.onAsk,
|
|
51
|
+
children: AI_INSIGHT_CARD_COPY.action
|
|
52
|
+
})
|
|
53
|
+
]
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** The tile on a plan without the AI add-on: the same tile, opening the add-on's dialog. */ function InsightUpsellTile({ text, upgrade }) {
|
|
58
|
+
const [open, setOpen] = useState(false);
|
|
59
|
+
if (!upgrade) return null;
|
|
60
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
61
|
+
children: [
|
|
62
|
+
/*#__PURE__*/ _jsx(InsightTile, {
|
|
63
|
+
text: text,
|
|
64
|
+
onAsk: ()=>setOpen(true)
|
|
65
|
+
}),
|
|
66
|
+
/*#__PURE__*/ _jsx(AiUpsellDialog, {
|
|
67
|
+
kind: "insight",
|
|
68
|
+
open: open,
|
|
69
|
+
onClose: ()=>setOpen(false),
|
|
70
|
+
upgrade: upgrade
|
|
71
|
+
})
|
|
72
|
+
]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** On a site's dashboard and its Analytics page. */ export function AiInsightHostCard({ hostId, entitled, upgrade }) {
|
|
76
|
+
var _route_orgSlug, _ref;
|
|
77
|
+
const { data: user } = useUser();
|
|
78
|
+
const orgId = useHostOrgId(hostId);
|
|
79
|
+
const route = useConsoleHostRoute(hostId);
|
|
80
|
+
const [open, setOpen] = useState(false);
|
|
81
|
+
if (entitled === false) return /*#__PURE__*/ _jsx(InsightUpsellTile, {
|
|
82
|
+
text: AI_INSIGHT_CARD_COPY.site,
|
|
83
|
+
upgrade: upgrade
|
|
84
|
+
});
|
|
85
|
+
if (!orgId) return null;
|
|
86
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
87
|
+
children: [
|
|
88
|
+
/*#__PURE__*/ _jsx(InsightTile, {
|
|
89
|
+
text: AI_INSIGHT_CARD_COPY.site,
|
|
90
|
+
onAsk: ()=>setOpen(true)
|
|
91
|
+
}),
|
|
92
|
+
open ? /*#__PURE__*/ _jsx(AiInsightDialog, {
|
|
93
|
+
open: true,
|
|
94
|
+
onClose: ()=>setOpen(false),
|
|
95
|
+
orgId: orgId,
|
|
96
|
+
orgSlug: (_route_orgSlug = route.orgSlug) != null ? _route_orgSlug : '',
|
|
97
|
+
hostId: hostId,
|
|
98
|
+
host: route.subdomain,
|
|
99
|
+
surface: "analytics",
|
|
100
|
+
user: user,
|
|
101
|
+
uid: (_ref = user == null ? void 0 : user.uid) != null ? _ref : null
|
|
102
|
+
}) : null
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
/** On the workspace's sites page, totaling the organization rather than one site. */ export function AiInsightOrgCard({ orgMount, entitled, upgrade }) {
|
|
107
|
+
var _ref, _ref1, _ref2;
|
|
108
|
+
const { data: user } = useUser();
|
|
109
|
+
const orgId = (_ref = orgMount == null ? void 0 : orgMount.orgId) != null ? _ref : null;
|
|
110
|
+
const [open, setOpen] = useState(false);
|
|
111
|
+
if (entitled === false) return /*#__PURE__*/ _jsx(InsightUpsellTile, {
|
|
112
|
+
text: AI_INSIGHT_CARD_COPY.workspace,
|
|
113
|
+
upgrade: upgrade
|
|
114
|
+
});
|
|
115
|
+
if (!orgId) return null;
|
|
116
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
117
|
+
children: [
|
|
118
|
+
/*#__PURE__*/ _jsx(InsightTile, {
|
|
119
|
+
text: AI_INSIGHT_CARD_COPY.workspace,
|
|
120
|
+
onAsk: ()=>setOpen(true)
|
|
121
|
+
}),
|
|
122
|
+
open ? /*#__PURE__*/ _jsx(AiInsightDialog, {
|
|
123
|
+
open: true,
|
|
124
|
+
onClose: ()=>setOpen(false),
|
|
125
|
+
orgId: orgId,
|
|
126
|
+
orgSlug: (_ref1 = orgMount == null ? void 0 : orgMount.orgSlug) != null ? _ref1 : '',
|
|
127
|
+
hostId: null,
|
|
128
|
+
host: null,
|
|
129
|
+
surface: "workspace",
|
|
130
|
+
user: user,
|
|
131
|
+
uid: (_ref2 = user == null ? void 0 : user.uid) != null ? _ref2 : null
|
|
132
|
+
}) : null
|
|
133
|
+
]
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//# sourceMappingURL=ai-insight-card.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-insight-card.component.tsx"],"sourcesContent":["'use client'\n\n/**\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\nimport type { ConsoleWidgetEntitlementProps } from '@aglyn/aglyn/plugin-manager/feature-plugins'\nimport type { ConsolePluginOrgMount } from '@aglyn/aglyn'\nimport { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiCreation } from '@aglyn/shared-data-mdi'\nimport { CardDisplay, MdiIcon } from '@aglyn/shared-ui-jsx'\nimport { useConsoleHostRoute, useHostOrgId, useUser } from '@aglyn/tenant-feature-instance'\nimport { Button, Stack, Typography } from '@mui/material'\nimport { useState } from 'react'\nimport { AiInsightDialog } from './ai-insight-dialog.component'\nimport { AiUpsellDialog } from './ai-upsell-dialog.component'\n\n/**\n * \"Ask AI about these numbers\" (AGL-3603): a tile on a site's dashboard and\n * Analytics page (`hostDashboard`, which both draw) and on the workspace's\n * sites page (`orgDashboard`), opening the insight dialog the Assist panel\n * opens. A question becomes an `insight` job — the figures read by code\n * through the readers the surface offers, every insight traced to the rows\n * it cites — charged as that job is. Drawn from the shell's gates alone\n * (AGL-3601): nothing asks a server until a question is asked, and on a plan\n * without the AI add-on the same tile opens the add-on's dialog.\n */\n\nexport const AI_INSIGHT_CARD_COPY = {\n title: 'Ask AI about these numbers',\n site: 'Ask a question about this site’s traffic, sales, bookings, automation runs and pipeline. Every answer shows the figures it is built from.',\n workspace: 'Ask a question about your workspace’s pipeline, the deals it closed and your datasets. Every answer shows the figures it is built from.',\n action: 'Ask a question',\n} as const\n\nfunction InsightTile(props: {\n text: string\n onAsk: () => void\n}) {\n const help = pluginDocsHelp('aiInsights')\n return (\n <CardDisplay header={AI_INSIGHT_CARD_COPY.title} help={help} contentGutterX contentGutterY>\n <Stack spacing={1.5} sx={{ alignItems: 'flex-start' }}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {props.text}\n </Typography>\n <Button\n size=\"small\"\n variant=\"outlined\"\n startIcon={<MdiIcon path={mdiCreation.path} />}\n onClick={props.onAsk}\n >\n {AI_INSIGHT_CARD_COPY.action}\n </Button>\n </Stack>\n </CardDisplay>\n )\n}\n\n/** The tile on a plan without the AI add-on: the same tile, opening the add-on's dialog. */\nfunction InsightUpsellTile({ text, upgrade }: { text: string; upgrade: ConsoleWidgetEntitlementProps['upgrade'] }) {\n const [open, setOpen] = useState(false)\n if (!upgrade) return null\n return (\n <>\n <InsightTile text={text} onAsk={() => setOpen(true)} />\n <AiUpsellDialog kind=\"insight\" open={open} onClose={() => setOpen(false)} upgrade={upgrade} />\n </>\n )\n}\n\n/** On a site's dashboard and its Analytics page. */\nexport function AiInsightHostCard({\n hostId,\n entitled,\n upgrade,\n}: { hostId: string } & ConsoleWidgetEntitlementProps) {\n const { data: user } = useUser()\n const orgId = useHostOrgId(hostId)\n const route = useConsoleHostRoute(hostId)\n const [open, setOpen] = useState(false)\n if (entitled === false) return <InsightUpsellTile text={AI_INSIGHT_CARD_COPY.site} upgrade={upgrade} />\n if (!orgId) return null\n return (\n <>\n <InsightTile text={AI_INSIGHT_CARD_COPY.site} onAsk={() => setOpen(true)} />\n {open ? (\n <AiInsightDialog\n open\n onClose={() => setOpen(false)}\n orgId={orgId}\n orgSlug={route.orgSlug ?? ''}\n hostId={hostId}\n host={route.subdomain}\n surface=\"analytics\"\n user={user}\n uid={(user as { uid?: string } | null | undefined)?.uid ?? null}\n />\n ) : null}\n </>\n )\n}\n\n/** On the workspace's sites page, totaling the organization rather than one site. */\nexport function AiInsightOrgCard({\n orgMount,\n entitled,\n upgrade,\n}: { orgMount?: ConsolePluginOrgMount | null } & ConsoleWidgetEntitlementProps) {\n const { data: user } = useUser()\n const orgId = orgMount?.orgId ?? null\n const [open, setOpen] = useState(false)\n if (entitled === false) return <InsightUpsellTile text={AI_INSIGHT_CARD_COPY.workspace} upgrade={upgrade} />\n if (!orgId) return null\n return (\n <>\n <InsightTile text={AI_INSIGHT_CARD_COPY.workspace} onAsk={() => setOpen(true)} />\n {open ? (\n <AiInsightDialog\n open\n onClose={() => setOpen(false)}\n orgId={orgId}\n orgSlug={orgMount?.orgSlug ?? ''}\n hostId={null}\n host={null}\n surface=\"workspace\"\n user={user}\n uid={(user as { uid?: string } | null | undefined)?.uid ?? null}\n />\n ) : null}\n </>\n )\n}\n"],"names":["pluginDocsHelp","mdiCreation","CardDisplay","MdiIcon","useConsoleHostRoute","useHostOrgId","useUser","Button","Stack","Typography","useState","AiInsightDialog","AiUpsellDialog","AI_INSIGHT_CARD_COPY","title","site","workspace","action","InsightTile","props","help","header","contentGutterX","contentGutterY","spacing","sx","alignItems","variant","color","text","size","startIcon","path","onClick","onAsk","InsightUpsellTile","upgrade","open","setOpen","kind","onClose","AiInsightHostCard","hostId","entitled","route","data","user","orgId","orgSlug","host","subdomain","surface","uid","AiInsightOrgCard","orgMount"],"mappings":"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"}
|
|
@@ -32,6 +32,12 @@ export interface AiInsightDialogProps {
|
|
|
32
32
|
uid: string | null;
|
|
33
33
|
/** An answered job to show rather than a question to ask. */
|
|
34
34
|
jobId?: string | null;
|
|
35
|
+
/**
|
|
36
|
+
* A question to start from, put in the box each time the dialog opens to
|
|
37
|
+
* ask (AGL-3603): the page that opened it knows what it is about. The
|
|
38
|
+
* person may change it before asking; absent, the box keeps what it held.
|
|
39
|
+
*/
|
|
40
|
+
question?: string;
|
|
35
41
|
}
|
|
36
42
|
export declare function AiInsightDialog(props: AiInsightDialogProps): import("react").JSX.Element;
|
|
37
43
|
export default AiInsightDialog;
|
|
@@ -40,10 +40,14 @@ import { openAiJobs, publishAiJob } from "./ai-jobs-store.js";
|
|
|
40
40
|
*/ /** Placeholder questions by surface, in the words a person would use. */ const PLACEHOLDERS = {
|
|
41
41
|
analytics: 'Which pages brought in the most visitors, and did anything change?',
|
|
42
42
|
datasets: 'What is the average order total by state?',
|
|
43
|
-
'crm-reports': 'Which campaign or form brought in the most people this month?'
|
|
43
|
+
'crm-reports': 'Which campaign or form brought in the most people this month?',
|
|
44
|
+
marketing: 'Which campaign brought in the most form submissions, and what did it earn?',
|
|
45
|
+
automations: 'Which automations failed most this month?',
|
|
46
|
+
bookings: 'Which services were booked most, and how many were canceled?',
|
|
47
|
+
workspace: 'How much is in the pipeline, and what closed this month?'
|
|
44
48
|
};
|
|
45
49
|
export function AiInsightDialog(props) {
|
|
46
|
-
const { open, onClose, orgId, orgSlug, hostId, host, surface, jobId } = props;
|
|
50
|
+
const { open, onClose, orgId, orgSlug, hostId, host, surface, jobId, question: startFrom } = props;
|
|
47
51
|
// Held in a ref: a request reads who is signed in, and nothing keys on the
|
|
48
52
|
// identity of the object that says so.
|
|
49
53
|
const userRef = useRef(props.user);
|
|
@@ -141,11 +145,13 @@ export function AiInsightDialog(props) {
|
|
|
141
145
|
} else {
|
|
142
146
|
setAnswer(null);
|
|
143
147
|
setPhase('ask');
|
|
148
|
+
if (startFrom) setQuestion(startFrom.slice(0, AI_INSIGHT_QUESTION_MAX_CHARS));
|
|
144
149
|
}
|
|
145
150
|
}, [
|
|
146
151
|
open,
|
|
147
152
|
jobId,
|
|
148
|
-
readAnswer
|
|
153
|
+
readAnswer,
|
|
154
|
+
startFrom
|
|
149
155
|
]);
|
|
150
156
|
// The person's own weekly-insights switch, read when the dialog opens.
|
|
151
157
|
useEffect(()=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-insight-dialog.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\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { INSIGHT_DIGESTS_FIELD, insightDigestSubscribed } from '@aglyn/aglyn/app-utils/notifications'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc, getDoc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_INSIGHT_DEFAULT_DAYS,\n AI_INSIGHT_QUESTION_MAX_CHARS,\n AI_INSIGHT_WINDOWS,\n aiInsightRowText,\n aiInsightSourceHref,\n type AiInsightAnswerWire,\n type AiInsightSurface,\n} from '../model/ai-insight'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { readEventFrames } from './ai-job-events'\nimport { openAiJobs, publishAiJob } from './ai-jobs-store'\n\n/**\n * \"Ask about your numbers\" (AGL-2915): the Assist panel's question box on a\n * site's Analytics, Data and CRM Reports pages and the workspace's Data page,\n * and the answer an `insight` job wrote.\n *\n * A question starts a job, which reads the figures and answers on the jobs\n * beat; the dialog watches the job and then reads the answer through\n * `/api/ai/insights/{jobId}`, the only door to it. Every insight shows the\n * rows it was traced to and links the console page they come from, so a\n * person can check a number before acting on it. Nothing here writes anything\n * but the person's own weekly-insights switch.\n */\n\n/** Placeholder questions by surface, in the words a person would use. */\nconst PLACEHOLDERS: Record<Exclude<AiInsightSurface, 'digest'>, string> = {\n analytics: 'Which pages brought in the most visitors, and did anything change?',\n datasets: 'What is the average order total by state?',\n 'crm-reports': 'Which campaign or form brought in the most people this month?',\n}\n\nexport interface AiInsightDialogProps {\n open: boolean\n onClose: () => void\n orgId: string\n /** The org's slug, for the links to the pages a table's figures come from. */\n orgSlug: string\n /** The site the question is about; `null` on the workspace's Data page. */\n hostId: string | null\n /** The site's subdomain, which a console link names a site by. */\n host: string | null\n surface: Exclude<AiInsightSurface, 'digest'>\n user: Parameters<typeof authorizedFetch>[0]\n /** The signed-in person, whose weekly-insights switch the dialog shows. */\n uid: string | null\n /** An answered job to show rather than a question to ask. */\n jobId?: string | null\n}\n\ntype Phase = 'ask' | 'working' | 'answer'\n\nexport function AiInsightDialog(props: AiInsightDialogProps) {\n const { open, onClose, orgId, orgSlug, hostId, host, surface, jobId } = props\n // Held in a ref: a request reads who is signed in, and nothing keys on the\n // identity of the object that says so.\n const userRef = useRef(props.user)\n userRef.current = props.user\n const uid = props.uid\n const firestore = useFirestore()\n const [phase, setPhase] = useState<Phase>('ask')\n const [question, setQuestion] = useState('')\n const [days, setDays] = useState(AI_INSIGHT_DEFAULT_DAYS)\n const [notice, setNotice] = useState<string | null>(null)\n const [answer, setAnswer] = useState<AiInsightAnswerWire | null>(null)\n const [openCitations, setOpenCitations] = useState<number | null>(null)\n const [subscribed, setSubscribed] = useState<boolean | null>(null)\n /**\n * The job whose answer was still being written when the dialog stopped\n * waiting (AGL-3593): its notice offers to open AI jobs on it.\n */\n const [stillWriting, setStillWriting] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n const readAnswer = useCallback(\n async (id: string) => {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/insights/${encodeURIComponent(id)}?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!response.ok || !payload?.answer) {\n setNotice(String(payload?.error ?? 'The answer could not be loaded. Try again.'))\n setPhase('ask')\n return\n }\n setAnswer(payload.answer as AiInsightAnswerWire)\n setPhase('answer')\n },\n [orgId],\n )\n\n /** Follows a job until it settles, then reads its answer or says why there is none. */\n const follow = useCallback(\n async (job: AiJobSummary) => {\n const settle = async (settled: AiJobSummary) => {\n if (settled.status === 'done') await readAnswer(settled.id)\n else {\n setNotice(settled.error ?? 'The question could not be answered. Try again.')\n setPhase('ask')\n }\n }\n if (AI_JOB_TERMINAL_STATUSES.includes(job.status)) return settle(job)\n setPhase('working')\n const controller = new AbortController()\n watchRef.current?.abort()\n watchRef.current = controller\n let again = true\n while (again && !controller.signal.aborted) {\n again = false\n let settled: AiJobSummary | null = null\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs/${encodeURIComponent(job.id)}/events?orgId=${encodeURIComponent(orgId)}`,\n { signal: controller.signal },\n )\n if (!response.ok || !response.body) break\n await readEventFrames(response.body, (event) => {\n if (event['type'] === 'reconnect') again = true\n if (event['type'] !== 'state') return\n const next = event['job'] as AiJobSummary\n publishAiJob(next)\n if (next.status === 'needs_input') {\n setNotice(next.error ?? 'This workspace cannot run AI jobs right now.')\n }\n if (AI_JOB_TERMINAL_STATUSES.includes(next.status)) settled = next\n })\n } catch {\n if (controller.signal.aborted) return\n break\n }\n if (settled) return settle(settled)\n }\n if (!controller.signal.aborted) {\n setNotice('The answer is still being written. Find it in AI jobs in a minute.')\n setStillWriting(job.id)\n setPhase('ask')\n }\n },\n [orgId, readAnswer],\n )\n\n useEffect(() => {\n if (!open) {\n watchRef.current?.abort()\n return\n }\n setNotice(null)\n setStillWriting(null)\n setOpenCitations(null)\n if (jobId) {\n setPhase('working')\n void readAnswer(jobId)\n } else {\n setAnswer(null)\n setPhase('ask')\n }\n }, [open, jobId, readAnswer])\n\n // The person's own weekly-insights switch, read when the dialog opens.\n useEffect(() => {\n if (!open || !uid || !orgId) return\n let active = true\n void getDoc(doc(firestore, 'users', uid))\n .then((snapshot) => {\n if (active) {\n setSubscribed(\n insightDigestSubscribed(snapshot.get(INSIGHT_DIGESTS_FIELD) as Record<string, boolean>, orgId),\n )\n }\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n }, [open, uid, orgId, firestore])\n\n const toggleDigest = useCallback(async () => {\n if (!uid || subscribed === null) return\n const next = !subscribed\n setSubscribed(next)\n try {\n await setDoc(doc(firestore, 'users', uid), { [INSIGHT_DIGESTS_FIELD]: { [orgId]: next } }, { merge: true })\n } catch {\n setSubscribed(!next)\n setNotice('Your weekly insights setting could not be saved. Try again.')\n }\n }, [uid, subscribed, firestore, orgId])\n\n const ask = useCallback(async () => {\n const text = question.trim()\n if (!text) return\n setNotice(null)\n setStillWriting(null)\n setPhase('working')\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'insight',\n brief: text,\n inputs: { surface, days },\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok || !payload?.job) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? 'The question could not be asked. Try again.'))\n setPhase('ask')\n return\n }\n publishAiJob(payload.job as AiJobSummary)\n await follow(payload.job as AiJobSummary)\n } catch {\n setNotice('The question could not be asked. Try again.')\n setPhase('ask')\n }\n }, [question, orgId, hostId, surface, days, follow])\n\n const tableByRef = (ref: string) => answer?.tables.find((table) => table.ref === ref) ?? null\n\n return (\n <Dialog open={open} onClose={onClose} fullWidth maxWidth=\"sm\" aria-labelledby=\"ai-insight-title\">\n <DialogTitle id=\"ai-insight-title\">{'Ask about your numbers'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n {phase === 'ask' ? (\n <>\n <TextField\n label=\"What do you want to know?\"\n placeholder={PLACEHOLDERS[surface]}\n multiline\n minRows={2}\n value={question}\n onChange={(event) => setQuestion(event.target.value.slice(0, AI_INSIGHT_QUESTION_MAX_CHARS))}\n autoFocus\n />\n {surface !== 'datasets' ? (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id=\"ai-insight-window-label\">\n {'Over the last'}\n </Typography>\n <Stack direction=\"row\" useFlexGap role=\"group\" aria-labelledby=\"ai-insight-window-label\" sx={{ flexWrap: 'wrap', gap: 1 }}>\n {AI_INSIGHT_WINDOWS.map((window) => (\n <Chip\n key={window}\n label={`${window} days`}\n color={days === window ? 'primary' : 'default'}\n variant={days === window ? 'filled' : 'outlined'}\n onClick={() => setDays(window)}\n aria-pressed={days === window}\n />\n ))}\n </Stack>\n </Stack>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The answer is read from your own figures, and every number in it links to where it comes from. '}\n {'Nothing is changed or sent.'}\n </Typography>\n </>\n ) : null}\n {phase === 'working' ? (\n <Stack direction=\"row\" spacing={1.5} sx={{ alignItems: 'center' }} role=\"status\">\n <CircularProgress size={18} />\n <Typography variant=\"body2\">{'Reading your figures. This usually takes a minute.'}</Typography>\n </Stack>\n ) : null}\n {phase === 'answer' && answer ? (\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{answer.question}</Typography>\n {answer.insights.length ? (\n <Stack component=\"ol\" spacing={1.5} sx={{ pl: 2.5, m: 0 }}>\n {answer.insights.map((insight, index) => (\n <Box component=\"li\" key={index}>\n <Typography variant=\"body2\">{insight.text}</Typography>\n <Button\n size=\"small\"\n onClick={() => setOpenCitations((current) => (current === index ? null : index))}\n aria-expanded={openCitations === index}\n >\n {openCitations === index ? 'Hide the figures' : 'Show the figures'}\n </Button>\n {openCitations === index ? (\n <Stack spacing={1} sx={{ pl: 1, borderLeft: 2, borderColor: 'divider' }}>\n {insight.cites.map((cite) => {\n const table = tableByRef(cite.table)\n if (!table) return null\n const href = aiInsightSourceHref(table, { orgSlug, host })\n return (\n <Box key={cite.table}>\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\">\n {table.period\n ? `${table.title} · ${table.period.from} to ${table.period.to}`\n : table.title}\n {href ? (\n <>\n {' · '}\n <AppLink componentVariant=\"naked\" href={href}>\n {`Open ${table.source.label}`}\n </AppLink>\n </>\n ) : null}\n </Typography>\n {cite.rows.map((row) => (\n <Typography key={row} variant=\"caption\" component=\"div\">\n {aiInsightRowText(table, row)}\n </Typography>\n ))}\n </Box>\n )\n })}\n </Stack>\n ) : null}\n </Box>\n ))}\n </Stack>\n ) : (\n <Alert severity=\"info\">{'The figures read here did not answer this question.'}</Alert>\n )}\n {answer.gap ? <Alert severity=\"info\">{answer.gap}</Alert> : null}\n {answer.left > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${answer.left} ${answer.left === 1 ? 'insight was' : 'insights were'} left out because ${\n answer.left === 1 ? 'its numbers' : 'their numbers'\n } could not be traced to the figures.`}\n </Typography>\n ) : null}\n </Stack>\n ) : null}\n {notice ? (\n <Alert\n severity=\"warning\"\n action={\n stillWriting ? (\n <Button\n color=\"inherit\"\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n openAiJobs({ jobId: stillWriting })\n onClose()\n }}\n >\n {'Open AI jobs'}\n </Button>\n ) : undefined\n }\n >\n {notice}\n </Alert>\n ) : null}\n {surface !== 'datasets' && subscribed !== null ? (\n <FormControlLabel\n control={<Switch checked={subscribed} onChange={() => void toggleDigest()} />}\n label=\"Send me weekly insights for this workspace every Monday\"\n />\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n {phase === 'answer' && !jobId ? (\n <Button\n onClick={() => {\n setAnswer(null)\n setQuestion('')\n setPhase('ask')\n }}\n >\n {'Ask another'}\n </Button>\n ) : null}\n <Button onClick={onClose}>{'Close'}</Button>\n {phase === 'ask' ? (\n <Button variant=\"contained\" onClick={() => void ask()} disabled={!question.trim()}>\n {'Ask'}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\nexport default AiInsightDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","AppLink","authorizedFetch","useFirestore","Alert","Box","Button","Chip","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","Stack","Switch","TextField","Typography","doc","getDoc","setDoc","useCallback","useEffect","useRef","useState","AI_INSIGHT_DEFAULT_DAYS","AI_INSIGHT_QUESTION_MAX_CHARS","AI_INSIGHT_WINDOWS","aiInsightRowText","aiInsightSourceHref","AI_JOB_TERMINAL_STATUSES","readEventFrames","openAiJobs","publishAiJob","PLACEHOLDERS","analytics","datasets","AiInsightDialog","props","open","onClose","orgId","orgSlug","hostId","host","surface","jobId","userRef","user","current","uid","firestore","phase","setPhase","question","setQuestion","days","setDays","notice","setNotice","answer","setAnswer","openCitations","setOpenCitations","subscribed","setSubscribed","stillWriting","setStillWriting","watchRef","readAnswer","id","response","encodeURIComponent","payload","json","catch","ok","String","error","follow","job","settle","settled","status","includes","controller","AbortController","abort","again","signal","aborted","body","event","next","active","then","snapshot","get","undefined","toggleDigest","merge","ask","text","trim","method","headers","JSON","stringify","kind","brief","inputs","locked","tableByRef","ref","tables","find","table","fullWidth","maxWidth","aria-labelledby","spacing","sx","pt","label","placeholder","multiline","minRows","value","onChange","target","slice","autoFocus","variant","color","direction","useFlexGap","role","flexWrap","gap","map","window","onClick","aria-pressed","alignItems","size","insights","length","component","pl","m","insight","index","aria-expanded","borderLeft","borderColor","cites","cite","href","period","title","from","to","componentVariant","source","rows","row","severity","left","action","control","checked","disabled"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/components/ai-insight-dialog.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\nimport { lockdownRefusalText, parseLockdownRefusal } from '@aglyn/aglyn'\nimport { INSIGHT_DIGESTS_FIELD, insightDigestSubscribed } from '@aglyn/aglyn/app-utils/notifications'\nimport { AppLink } from '@aglyn/shared-ui-jsx'\nimport { authorizedFetch } from '@aglyn/shared-util-http/authorized-token'\nimport { useFirestore } from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n CircularProgress,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n FormControlLabel,\n Stack,\n Switch,\n TextField,\n Typography,\n} from '@mui/material'\nimport { doc, getDoc, setDoc } from 'firebase/firestore'\nimport { useCallback, useEffect, useRef, useState } from 'react'\nimport {\n AI_INSIGHT_DEFAULT_DAYS,\n AI_INSIGHT_QUESTION_MAX_CHARS,\n AI_INSIGHT_WINDOWS,\n aiInsightRowText,\n aiInsightSourceHref,\n type AiInsightAnswerWire,\n type AiInsightSurface,\n} from '../model/ai-insight'\nimport { AI_JOB_TERMINAL_STATUSES, type AiJobSummary } from '../model/ai-jobs.types'\nimport { readEventFrames } from './ai-job-events'\nimport { openAiJobs, publishAiJob } from './ai-jobs-store'\n\n/**\n * \"Ask about your numbers\" (AGL-2915): the Assist panel's question box on a\n * site's Analytics, Data and CRM Reports pages and the workspace's Data page,\n * and the answer an `insight` job wrote.\n *\n * A question starts a job, which reads the figures and answers on the jobs\n * beat; the dialog watches the job and then reads the answer through\n * `/api/ai/insights/{jobId}`, the only door to it. Every insight shows the\n * rows it was traced to and links the console page they come from, so a\n * person can check a number before acting on it. Nothing here writes anything\n * but the person's own weekly-insights switch.\n */\n\n/** Placeholder questions by surface, in the words a person would use. */\nconst PLACEHOLDERS: Record<Exclude<AiInsightSurface, 'digest'>, string> = {\n analytics: 'Which pages brought in the most visitors, and did anything change?',\n datasets: 'What is the average order total by state?',\n 'crm-reports': 'Which campaign or form brought in the most people this month?',\n marketing: 'Which campaign brought in the most form submissions, and what did it earn?',\n automations: 'Which automations failed most this month?',\n bookings: 'Which services were booked most, and how many were canceled?',\n workspace: 'How much is in the pipeline, and what closed this month?',\n}\n\nexport interface AiInsightDialogProps {\n open: boolean\n onClose: () => void\n orgId: string\n /** The org's slug, for the links to the pages a table's figures come from. */\n orgSlug: string\n /** The site the question is about; `null` on the workspace's Data page. */\n hostId: string | null\n /** The site's subdomain, which a console link names a site by. */\n host: string | null\n surface: Exclude<AiInsightSurface, 'digest'>\n user: Parameters<typeof authorizedFetch>[0]\n /** The signed-in person, whose weekly-insights switch the dialog shows. */\n uid: string | null\n /** An answered job to show rather than a question to ask. */\n jobId?: string | null\n /**\n * A question to start from, put in the box each time the dialog opens to\n * ask (AGL-3603): the page that opened it knows what it is about. The\n * person may change it before asking; absent, the box keeps what it held.\n */\n question?: string\n}\n\ntype Phase = 'ask' | 'working' | 'answer'\n\nexport function AiInsightDialog(props: AiInsightDialogProps) {\n const { open, onClose, orgId, orgSlug, hostId, host, surface, jobId, question: startFrom } = props\n // Held in a ref: a request reads who is signed in, and nothing keys on the\n // identity of the object that says so.\n const userRef = useRef(props.user)\n userRef.current = props.user\n const uid = props.uid\n const firestore = useFirestore()\n const [phase, setPhase] = useState<Phase>('ask')\n const [question, setQuestion] = useState('')\n const [days, setDays] = useState(AI_INSIGHT_DEFAULT_DAYS)\n const [notice, setNotice] = useState<string | null>(null)\n const [answer, setAnswer] = useState<AiInsightAnswerWire | null>(null)\n const [openCitations, setOpenCitations] = useState<number | null>(null)\n const [subscribed, setSubscribed] = useState<boolean | null>(null)\n /**\n * The job whose answer was still being written when the dialog stopped\n * waiting (AGL-3593): its notice offers to open AI jobs on it.\n */\n const [stillWriting, setStillWriting] = useState<string | null>(null)\n const watchRef = useRef<AbortController | null>(null)\n\n const readAnswer = useCallback(\n async (id: string) => {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/insights/${encodeURIComponent(id)}?orgId=${encodeURIComponent(orgId)}`,\n )\n const payload = await response.json().catch(() => null)\n if (!response.ok || !payload?.answer) {\n setNotice(String(payload?.error ?? 'The answer could not be loaded. Try again.'))\n setPhase('ask')\n return\n }\n setAnswer(payload.answer as AiInsightAnswerWire)\n setPhase('answer')\n },\n [orgId],\n )\n\n /** Follows a job until it settles, then reads its answer or says why there is none. */\n const follow = useCallback(\n async (job: AiJobSummary) => {\n const settle = async (settled: AiJobSummary) => {\n if (settled.status === 'done') await readAnswer(settled.id)\n else {\n setNotice(settled.error ?? 'The question could not be answered. Try again.')\n setPhase('ask')\n }\n }\n if (AI_JOB_TERMINAL_STATUSES.includes(job.status)) return settle(job)\n setPhase('working')\n const controller = new AbortController()\n watchRef.current?.abort()\n watchRef.current = controller\n let again = true\n while (again && !controller.signal.aborted) {\n again = false\n let settled: AiJobSummary | null = null\n try {\n const response = await authorizedFetch(\n userRef.current,\n `/api/ai/jobs/${encodeURIComponent(job.id)}/events?orgId=${encodeURIComponent(orgId)}`,\n { signal: controller.signal },\n )\n if (!response.ok || !response.body) break\n await readEventFrames(response.body, (event) => {\n if (event['type'] === 'reconnect') again = true\n if (event['type'] !== 'state') return\n const next = event['job'] as AiJobSummary\n publishAiJob(next)\n if (next.status === 'needs_input') {\n setNotice(next.error ?? 'This workspace cannot run AI jobs right now.')\n }\n if (AI_JOB_TERMINAL_STATUSES.includes(next.status)) settled = next\n })\n } catch {\n if (controller.signal.aborted) return\n break\n }\n if (settled) return settle(settled)\n }\n if (!controller.signal.aborted) {\n setNotice('The answer is still being written. Find it in AI jobs in a minute.')\n setStillWriting(job.id)\n setPhase('ask')\n }\n },\n [orgId, readAnswer],\n )\n\n useEffect(() => {\n if (!open) {\n watchRef.current?.abort()\n return\n }\n setNotice(null)\n setStillWriting(null)\n setOpenCitations(null)\n if (jobId) {\n setPhase('working')\n void readAnswer(jobId)\n } else {\n setAnswer(null)\n setPhase('ask')\n if (startFrom) setQuestion(startFrom.slice(0, AI_INSIGHT_QUESTION_MAX_CHARS))\n }\n }, [open, jobId, readAnswer, startFrom])\n\n // The person's own weekly-insights switch, read when the dialog opens.\n useEffect(() => {\n if (!open || !uid || !orgId) return\n let active = true\n void getDoc(doc(firestore, 'users', uid))\n .then((snapshot) => {\n if (active) {\n setSubscribed(\n insightDigestSubscribed(snapshot.get(INSIGHT_DIGESTS_FIELD) as Record<string, boolean>, orgId),\n )\n }\n })\n .catch(() => undefined)\n return () => {\n active = false\n }\n }, [open, uid, orgId, firestore])\n\n const toggleDigest = useCallback(async () => {\n if (!uid || subscribed === null) return\n const next = !subscribed\n setSubscribed(next)\n try {\n await setDoc(doc(firestore, 'users', uid), { [INSIGHT_DIGESTS_FIELD]: { [orgId]: next } }, { merge: true })\n } catch {\n setSubscribed(!next)\n setNotice('Your weekly insights setting could not be saved. Try again.')\n }\n }, [uid, subscribed, firestore, orgId])\n\n const ask = useCallback(async () => {\n const text = question.trim()\n if (!text) return\n setNotice(null)\n setStillWriting(null)\n setPhase('working')\n try {\n const response = await authorizedFetch(userRef.current, '/api/ai/jobs', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n orgId,\n hostId,\n kind: 'insight',\n brief: text,\n inputs: { surface, days },\n }),\n })\n const payload = await response.json().catch(() => null)\n if (!response.ok || !payload?.job) {\n const locked = parseLockdownRefusal(response.status, payload)\n setNotice(locked ? lockdownRefusalText(locked) : String(payload?.error ?? 'The question could not be asked. Try again.'))\n setPhase('ask')\n return\n }\n publishAiJob(payload.job as AiJobSummary)\n await follow(payload.job as AiJobSummary)\n } catch {\n setNotice('The question could not be asked. Try again.')\n setPhase('ask')\n }\n }, [question, orgId, hostId, surface, days, follow])\n\n const tableByRef = (ref: string) => answer?.tables.find((table) => table.ref === ref) ?? null\n\n return (\n <Dialog open={open} onClose={onClose} fullWidth maxWidth=\"sm\" aria-labelledby=\"ai-insight-title\">\n <DialogTitle id=\"ai-insight-title\">{'Ask about your numbers'}</DialogTitle>\n <DialogContent>\n <Stack spacing={2} sx={{ pt: 1 }}>\n {phase === 'ask' ? (\n <>\n <TextField\n label=\"What do you want to know?\"\n placeholder={PLACEHOLDERS[surface]}\n multiline\n minRows={2}\n value={question}\n onChange={(event) => setQuestion(event.target.value.slice(0, AI_INSIGHT_QUESTION_MAX_CHARS))}\n autoFocus\n />\n {surface !== 'datasets' ? (\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\" id=\"ai-insight-window-label\">\n {'Over the last'}\n </Typography>\n <Stack direction=\"row\" useFlexGap role=\"group\" aria-labelledby=\"ai-insight-window-label\" sx={{ flexWrap: 'wrap', gap: 1 }}>\n {AI_INSIGHT_WINDOWS.map((window) => (\n <Chip\n key={window}\n label={`${window} days`}\n color={days === window ? 'primary' : 'default'}\n variant={days === window ? 'filled' : 'outlined'}\n onClick={() => setDays(window)}\n aria-pressed={days === window}\n />\n ))}\n </Stack>\n </Stack>\n ) : null}\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'The answer is read from your own figures, and every number in it links to where it comes from. '}\n {'Nothing is changed or sent.'}\n </Typography>\n </>\n ) : null}\n {phase === 'working' ? (\n <Stack direction=\"row\" spacing={1.5} sx={{ alignItems: 'center' }} role=\"status\">\n <CircularProgress size={18} />\n <Typography variant=\"body2\">{'Reading your figures. This usually takes a minute.'}</Typography>\n </Stack>\n ) : null}\n {phase === 'answer' && answer ? (\n <Stack spacing={1.5}>\n <Typography variant=\"subtitle2\">{answer.question}</Typography>\n {answer.insights.length ? (\n <Stack component=\"ol\" spacing={1.5} sx={{ pl: 2.5, m: 0 }}>\n {answer.insights.map((insight, index) => (\n <Box component=\"li\" key={index}>\n <Typography variant=\"body2\">{insight.text}</Typography>\n <Button\n size=\"small\"\n onClick={() => setOpenCitations((current) => (current === index ? null : index))}\n aria-expanded={openCitations === index}\n >\n {openCitations === index ? 'Hide the figures' : 'Show the figures'}\n </Button>\n {openCitations === index ? (\n <Stack spacing={1} sx={{ pl: 1, borderLeft: 2, borderColor: 'divider' }}>\n {insight.cites.map((cite) => {\n const table = tableByRef(cite.table)\n if (!table) return null\n const href = aiInsightSourceHref(table, { orgSlug, host })\n return (\n <Box key={cite.table}>\n <Typography variant=\"caption\" color=\"text.secondary\" component=\"div\">\n {table.period\n ? `${table.title} · ${table.period.from} to ${table.period.to}`\n : table.title}\n {href ? (\n <>\n {' · '}\n <AppLink componentVariant=\"naked\" href={href}>\n {`Open ${table.source.label}`}\n </AppLink>\n </>\n ) : null}\n </Typography>\n {cite.rows.map((row) => (\n <Typography key={row} variant=\"caption\" component=\"div\">\n {aiInsightRowText(table, row)}\n </Typography>\n ))}\n </Box>\n )\n })}\n </Stack>\n ) : null}\n </Box>\n ))}\n </Stack>\n ) : (\n <Alert severity=\"info\">{'The figures read here did not answer this question.'}</Alert>\n )}\n {answer.gap ? <Alert severity=\"info\">{answer.gap}</Alert> : null}\n {answer.left > 0 ? (\n <Typography variant=\"caption\" color=\"text.secondary\">\n {`${answer.left} ${answer.left === 1 ? 'insight was' : 'insights were'} left out because ${\n answer.left === 1 ? 'its numbers' : 'their numbers'\n } could not be traced to the figures.`}\n </Typography>\n ) : null}\n </Stack>\n ) : null}\n {notice ? (\n <Alert\n severity=\"warning\"\n action={\n stillWriting ? (\n <Button\n color=\"inherit\"\n size=\"small\"\n variant=\"outlined\"\n onClick={() => {\n openAiJobs({ jobId: stillWriting })\n onClose()\n }}\n >\n {'Open AI jobs'}\n </Button>\n ) : undefined\n }\n >\n {notice}\n </Alert>\n ) : null}\n {surface !== 'datasets' && subscribed !== null ? (\n <FormControlLabel\n control={<Switch checked={subscribed} onChange={() => void toggleDigest()} />}\n label=\"Send me weekly insights for this workspace every Monday\"\n />\n ) : null}\n </Stack>\n </DialogContent>\n <DialogActions>\n {phase === 'answer' && !jobId ? (\n <Button\n onClick={() => {\n setAnswer(null)\n setQuestion('')\n setPhase('ask')\n }}\n >\n {'Ask another'}\n </Button>\n ) : null}\n <Button onClick={onClose}>{'Close'}</Button>\n {phase === 'ask' ? (\n <Button variant=\"contained\" onClick={() => void ask()} disabled={!question.trim()}>\n {'Ask'}\n </Button>\n ) : null}\n </DialogActions>\n </Dialog>\n )\n}\n\nexport default AiInsightDialog\n"],"names":["lockdownRefusalText","parseLockdownRefusal","INSIGHT_DIGESTS_FIELD","insightDigestSubscribed","AppLink","authorizedFetch","useFirestore","Alert","Box","Button","Chip","CircularProgress","Dialog","DialogActions","DialogContent","DialogTitle","FormControlLabel","Stack","Switch","TextField","Typography","doc","getDoc","setDoc","useCallback","useEffect","useRef","useState","AI_INSIGHT_DEFAULT_DAYS","AI_INSIGHT_QUESTION_MAX_CHARS","AI_INSIGHT_WINDOWS","aiInsightRowText","aiInsightSourceHref","AI_JOB_TERMINAL_STATUSES","readEventFrames","openAiJobs","publishAiJob","PLACEHOLDERS","analytics","datasets","marketing","automations","bookings","workspace","AiInsightDialog","props","open","onClose","orgId","orgSlug","hostId","host","surface","jobId","question","startFrom","userRef","user","current","uid","firestore","phase","setPhase","setQuestion","days","setDays","notice","setNotice","answer","setAnswer","openCitations","setOpenCitations","subscribed","setSubscribed","stillWriting","setStillWriting","watchRef","readAnswer","id","response","encodeURIComponent","payload","json","catch","ok","String","error","follow","job","settle","settled","status","includes","controller","AbortController","abort","again","signal","aborted","body","event","next","slice","active","then","snapshot","get","undefined","toggleDigest","merge","ask","text","trim","method","headers","JSON","stringify","kind","brief","inputs","locked","tableByRef","ref","tables","find","table","fullWidth","maxWidth","aria-labelledby","spacing","sx","pt","label","placeholder","multiline","minRows","value","onChange","target","autoFocus","variant","color","direction","useFlexGap","role","flexWrap","gap","map","window","onClick","aria-pressed","alignItems","size","insights","length","component","pl","m","insight","index","aria-expanded","borderLeft","borderColor","cites","cite","href","period","title","from","to","componentVariant","source","rows","row","severity","left","action","control","checked","disabled"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,mBAAmB,EAAEC,oBAAoB,QAAQ,eAAc;AACxE,SAASC,qBAAqB,EAAEC,uBAAuB,QAAQ,uCAAsC;AACrG,SAASC,OAAO,QAAQ,uBAAsB;AAC9C,SAASC,eAAe,QAAQ,2CAA0C;AAC1E,SAASC,YAAY,QAAQ,iCAAgC;AAC7D,SACEC,KAAK,EACLC,GAAG,EACHC,MAAM,EACNC,IAAI,EACJC,gBAAgB,EAChBC,MAAM,EACNC,aAAa,EACbC,aAAa,EACbC,WAAW,EACXC,gBAAgB,EAChBC,KAAK,EACLC,MAAM,EACNC,SAAS,EACTC,UAAU,QACL,gBAAe;AACtB,SAASC,GAAG,EAAEC,MAAM,EAAEC,MAAM,QAAQ,qBAAoB;AACxD,SAASC,WAAW,EAAEC,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAO;AAChE,SACEC,uBAAuB,EACvBC,6BAA6B,EAC7BC,kBAAkB,EAClBC,gBAAgB,EAChBC,mBAAmB,QAGd,yBAAqB;AAC5B,SAASC,wBAAwB,QAA2B,4BAAwB;AACpF,SAASC,eAAe,QAAQ,qBAAiB;AACjD,SAASC,UAAU,EAAEC,YAAY,QAAQ,qBAAiB;AAE1D;;;;;;;;;;;CAWC,GAED,uEAAuE,GACvE,MAAMC,eAAoE;IACxEC,WAAW;IACXC,UAAU;IACV,eAAe;IACfC,WAAW;IACXC,aAAa;IACbC,UAAU;IACVC,WAAW;AACb;AA4BA,OAAO,SAASC,gBAAgBC,KAA2B;IACzD,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,KAAK,EAAEC,OAAO,EAAEC,MAAM,EAAEC,IAAI,EAAEC,OAAO,EAAEC,KAAK,EAAEC,UAAUC,SAAS,EAAE,GAAGV;IAC7F,2EAA2E;IAC3E,uCAAuC;IACvC,MAAMW,UAAU9B,OAAOmB,MAAMY,IAAI;IACjCD,QAAQE,OAAO,GAAGb,MAAMY,IAAI;IAC5B,MAAME,MAAMd,MAAMc,GAAG;IACrB,MAAMC,YAAYtD;IAClB,MAAM,CAACuD,OAAOC,SAAS,GAAGnC,SAAgB;IAC1C,MAAM,CAAC2B,UAAUS,YAAY,GAAGpC,SAAS;IACzC,MAAM,CAACqC,MAAMC,QAAQ,GAAGtC,SAASC;IACjC,MAAM,CAACsC,QAAQC,UAAU,GAAGxC,SAAwB;IACpD,MAAM,CAACyC,QAAQC,UAAU,GAAG1C,SAAqC;IACjE,MAAM,CAAC2C,eAAeC,iBAAiB,GAAG5C,SAAwB;IAClE,MAAM,CAAC6C,YAAYC,cAAc,GAAG9C,SAAyB;IAC7D;;;GAGC,GACD,MAAM,CAAC+C,cAAcC,gBAAgB,GAAGhD,SAAwB;IAChE,MAAMiD,WAAWlD,OAA+B;IAEhD,MAAMmD,aAAarD,YACjB,OAAOsD;QACL,MAAMC,WAAW,MAAM1E,gBACrBmD,QAAQE,OAAO,EACf,CAAC,iBAAiB,EAAEsB,mBAAmBF,IAAI,OAAO,EAAEE,mBAAmBhC,QAAQ;QAEjF,MAAMiC,UAAU,MAAMF,SAASG,IAAI,GAAGC,KAAK,CAAC,IAAM;QAClD,IAAI,CAACJ,SAASK,EAAE,IAAI,EAACH,2BAAAA,QAASb,MAAM,GAAE;;YACpCD,UAAUkB,eAAOJ,2BAAAA,QAASK,KAAK,mBAAI;YACnCxB,SAAS;YACT;QACF;QACAO,UAAUY,QAAQb,MAAM;QACxBN,SAAS;IACX,GACA;QAACd;KAAM;IAGT,qFAAqF,GACrF,MAAMuC,SAAS/D,YACb,OAAOgE;YAWLZ;QAVA,MAAMa,SAAS,OAAOC;YACpB,IAAIA,QAAQC,MAAM,KAAK,QAAQ,MAAMd,WAAWa,QAAQZ,EAAE;iBACrD;oBACOY;gBAAVvB,WAAUuB,iBAAAA,QAAQJ,KAAK,YAAbI,iBAAiB;gBAC3B5B,SAAS;YACX;QACF;QACA,IAAI7B,yBAAyB2D,QAAQ,CAACJ,IAAIG,MAAM,GAAG,OAAOF,OAAOD;QACjE1B,SAAS;QACT,MAAM+B,aAAa,IAAIC;SACvBlB,oBAAAA,SAASlB,OAAO,qBAAhBkB,kBAAkBmB,KAAK;QACvBnB,SAASlB,OAAO,GAAGmC;QACnB,IAAIG,QAAQ;QACZ,MAAOA,SAAS,CAACH,WAAWI,MAAM,CAACC,OAAO,CAAE;YAC1CF,QAAQ;YACR,IAAIN,UAA+B;YACnC,IAAI;gBACF,MAAMX,WAAW,MAAM1E,gBACrBmD,QAAQE,OAAO,EACf,CAAC,aAAa,EAAEsB,mBAAmBQ,IAAIV,EAAE,EAAE,cAAc,EAAEE,mBAAmBhC,QAAQ,EACtF;oBAAEiD,QAAQJ,WAAWI,MAAM;gBAAC;gBAE9B,IAAI,CAAClB,SAASK,EAAE,IAAI,CAACL,SAASoB,IAAI,EAAE;gBACpC,MAAMjE,gBAAgB6C,SAASoB,IAAI,EAAE,CAACC;oBACpC,IAAIA,KAAK,CAAC,OAAO,KAAK,aAAaJ,QAAQ;oBAC3C,IAAII,KAAK,CAAC,OAAO,KAAK,SAAS;oBAC/B,MAAMC,OAAOD,KAAK,CAAC,MAAM;oBACzBhE,aAAaiE;oBACb,IAAIA,KAAKV,MAAM,KAAK,eAAe;4BACvBU;wBAAVlC,WAAUkC,cAAAA,KAAKf,KAAK,YAAVe,cAAc;oBAC1B;oBACA,IAAIpE,yBAAyB2D,QAAQ,CAACS,KAAKV,MAAM,GAAGD,UAAUW;gBAChE;YACF,EAAE,eAAM;gBACN,IAAIR,WAAWI,MAAM,CAACC,OAAO,EAAE;gBAC/B;YACF;YACA,IAAIR,SAAS,OAAOD,OAAOC;QAC7B;QACA,IAAI,CAACG,WAAWI,MAAM,CAACC,OAAO,EAAE;YAC9B/B,UAAU;YACVQ,gBAAgBa,IAAIV,EAAE;YACtBhB,SAAS;QACX;IACF,GACA;QAACd;QAAO6B;KAAW;IAGrBpD,UAAU;QACR,IAAI,CAACqB,MAAM;gBACT8B;aAAAA,oBAAAA,SAASlB,OAAO,qBAAhBkB,kBAAkBmB,KAAK;YACvB;QACF;QACA5B,UAAU;QACVQ,gBAAgB;QAChBJ,iBAAiB;QACjB,IAAIlB,OAAO;YACTS,SAAS;YACT,KAAKe,WAAWxB;QAClB,OAAO;YACLgB,UAAU;YACVP,SAAS;YACT,IAAIP,WAAWQ,YAAYR,UAAU+C,KAAK,CAAC,GAAGzE;QAChD;IACF,GAAG;QAACiB;QAAMO;QAAOwB;QAAYtB;KAAU;IAEvC,uEAAuE;IACvE9B,UAAU;QACR,IAAI,CAACqB,QAAQ,CAACa,OAAO,CAACX,OAAO;QAC7B,IAAIuD,SAAS;QACb,KAAKjF,OAAOD,IAAIuC,WAAW,SAASD,MACjC6C,IAAI,CAAC,CAACC;YACL,IAAIF,QAAQ;gBACV9B,cACEtE,wBAAwBsG,SAASC,GAAG,CAACxG,wBAAmD8C;YAE5F;QACF,GACCmC,KAAK,CAAC,IAAMwB;QACf,OAAO;YACLJ,SAAS;QACX;IACF,GAAG;QAACzD;QAAMa;QAAKX;QAAOY;KAAU;IAEhC,MAAMgD,eAAepF,YAAY;QAC/B,IAAI,CAACmC,OAAOa,eAAe,MAAM;QACjC,MAAM6B,OAAO,CAAC7B;QACdC,cAAc4B;QACd,IAAI;YACF,MAAM9E,OAAOF,IAAIuC,WAAW,SAASD,MAAM;gBAAE,CAACzD,sBAAsB,EAAE;oBAAE,CAAC8C,MAAM,EAAEqD;gBAAK;YAAE,GAAG;gBAAEQ,OAAO;YAAK;QAC3G,EAAE,eAAM;YACNpC,cAAc,CAAC4B;YACflC,UAAU;QACZ;IACF,GAAG;QAACR;QAAKa;QAAYZ;QAAWZ;KAAM;IAEtC,MAAM8D,MAAMtF,YAAY;QACtB,MAAMuF,OAAOzD,SAAS0D,IAAI;QAC1B,IAAI,CAACD,MAAM;QACX5C,UAAU;QACVQ,gBAAgB;QAChBb,SAAS;QACT,IAAI;YACF,MAAMiB,WAAW,MAAM1E,gBAAgBmD,QAAQE,OAAO,EAAE,gBAAgB;gBACtEuD,QAAQ;gBACRC,SAAS;oBAAE,gBAAgB;gBAAmB;gBAC9Cf,MAAMgB,KAAKC,SAAS,CAAC;oBACnBpE;oBACAE;oBACAmE,MAAM;oBACNC,OAAOP;oBACPQ,QAAQ;wBAAEnE;wBAASY;oBAAK;gBAC1B;YACF;YACA,MAAMiB,UAAU,MAAMF,SAASG,IAAI,GAAGC,KAAK,CAAC,IAAM;YAClD,IAAI,CAACJ,SAASK,EAAE,IAAI,EAACH,2BAAAA,QAASO,GAAG,GAAE;;gBACjC,MAAMgC,SAASvH,qBAAqB8E,SAASY,MAAM,EAAEV;gBACrDd,UAAUqD,SAASxH,oBAAoBwH,UAAUnC,eAAOJ,2BAAAA,QAASK,KAAK,mBAAI;gBAC1ExB,SAAS;gBACT;YACF;YACA1B,aAAa6C,QAAQO,GAAG;YACxB,MAAMD,OAAON,QAAQO,GAAG;QAC1B,EAAE,eAAM;YACNrB,UAAU;YACVL,SAAS;QACX;IACF,GAAG;QAACR;QAAUN;QAAOE;QAAQE;QAASY;QAAMuB;KAAO;IAEnD,MAAMkC,aAAa,CAACC;;uBAAgBtD,0BAAAA,OAAQuD,MAAM,CAACC,IAAI,CAAC,CAACC,QAAUA,MAAMH,GAAG,KAAKA,uBAAQ;;IAEzF,qBACE,MAAC9G;QAAOkC,MAAMA;QAAMC,SAASA;QAAS+E,SAAS;QAACC,UAAS;QAAKC,mBAAgB;;0BAC5E,KAACjH;gBAAY+D,IAAG;0BAAoB;;0BACpC,KAAChE;0BACC,cAAA,MAACG;oBAAMgH,SAAS;oBAAGC,IAAI;wBAAEC,IAAI;oBAAE;;wBAC5BtE,UAAU,sBACT;;8CACE,KAAC1C;oCACCiH,OAAM;oCACNC,aAAahG,YAAY,CAACe,QAAQ;oCAClCkF,SAAS;oCACTC,SAAS;oCACTC,OAAOlF;oCACPmF,UAAU,CAACrC,QAAUrC,YAAYqC,MAAMsC,MAAM,CAACF,KAAK,CAAClC,KAAK,CAAC,GAAGzE;oCAC7D8G,SAAS;;gCAEVvF,YAAY,2BACX,MAACnC;oCAAMgH,SAAS;;sDACd,KAAC7G;4CAAWwH,SAAQ;4CAAQC,OAAM;4CAAiB/D,IAAG;sDACnD;;sDAEH,KAAC7D;4CAAM6H,WAAU;4CAAMC,UAAU;4CAACC,MAAK;4CAAQhB,mBAAgB;4CAA0BE,IAAI;gDAAEe,UAAU;gDAAQC,KAAK;4CAAE;sDACrHpH,mBAAmBqH,GAAG,CAAC,CAACC,uBACvB,KAAC1I;oDAEC0H,OAAO,GAAGgB,OAAO,KAAK,CAAC;oDACvBP,OAAO7E,SAASoF,SAAS,YAAY;oDACrCR,SAAS5E,SAASoF,SAAS,WAAW;oDACtCC,SAAS,IAAMpF,QAAQmF;oDACvBE,gBAActF,SAASoF;mDALlBA;;;qCAUX;8CACJ,MAAChI;oCAAWwH,SAAQ;oCAAQC,OAAM;;wCAC/B;wCACA;;;;6BAGH;wBACHhF,UAAU,0BACT,MAAC5C;4BAAM6H,WAAU;4BAAMb,SAAS;4BAAKC,IAAI;gCAAEqB,YAAY;4BAAS;4BAAGP,MAAK;;8CACtE,KAACrI;oCAAiB6I,MAAM;;8CACxB,KAACpI;oCAAWwH,SAAQ;8CAAS;;;6BAE7B;wBACH/E,UAAU,YAAYO,uBACrB,MAACnD;4BAAMgH,SAAS;;8CACd,KAAC7G;oCAAWwH,SAAQ;8CAAaxE,OAAOd,QAAQ;;gCAC/Cc,OAAOqF,QAAQ,CAACC,MAAM,iBACrB,KAACzI;oCAAM0I,WAAU;oCAAK1B,SAAS;oCAAKC,IAAI;wCAAE0B,IAAI;wCAAKC,GAAG;oCAAE;8CACrDzF,OAAOqF,QAAQ,CAACN,GAAG,CAAC,CAACW,SAASC,sBAC7B,MAACvJ;4CAAImJ,WAAU;;8DACb,KAACvI;oDAAWwH,SAAQ;8DAASkB,QAAQ/C,IAAI;;8DACzC,KAACtG;oDACC+I,MAAK;oDACLH,SAAS,IAAM9E,iBAAiB,CAACb,UAAaA,YAAYqG,QAAQ,OAAOA;oDACzEC,iBAAe1F,kBAAkByF;8DAEhCzF,kBAAkByF,QAAQ,qBAAqB;;gDAEjDzF,kBAAkByF,sBACjB,KAAC9I;oDAAMgH,SAAS;oDAAGC,IAAI;wDAAE0B,IAAI;wDAAGK,YAAY;wDAAGC,aAAa;oDAAU;8DACnEJ,QAAQK,KAAK,CAAChB,GAAG,CAAC,CAACiB;wDAClB,MAAMvC,QAAQJ,WAAW2C,KAAKvC,KAAK;wDACnC,IAAI,CAACA,OAAO,OAAO;wDACnB,MAAMwC,OAAOrI,oBAAoB6F,OAAO;4DAAE5E;4DAASE;wDAAK;wDACxD,qBACE,MAAC3C;;8EACC,MAACY;oEAAWwH,SAAQ;oEAAUC,OAAM;oEAAiBc,WAAU;;wEAC5D9B,MAAMyC,MAAM,GACT,GAAGzC,MAAM0C,KAAK,CAAC,GAAG,EAAE1C,MAAMyC,MAAM,CAACE,IAAI,CAAC,IAAI,EAAE3C,MAAMyC,MAAM,CAACG,EAAE,EAAE,GAC7D5C,MAAM0C,KAAK;wEACdF,qBACC;;gFACG;8FACD,KAACjK;oFAAQsK,kBAAiB;oFAAQL,MAAMA;8FACrC,CAAC,KAAK,EAAExC,MAAM8C,MAAM,CAACvC,KAAK,EAAE;;;6EAG/B;;;gEAELgC,KAAKQ,IAAI,CAACzB,GAAG,CAAC,CAAC0B,oBACd,KAACzJ;wEAAqBwH,SAAQ;wEAAUe,WAAU;kFAC/C5H,iBAAiB8F,OAAOgD;uEADVA;;2DAfXT,KAAKvC,KAAK;oDAqBxB;qDAEA;;2CAvCmBkC;mDA4C7B,KAACxJ;oCAAMuK,UAAS;8CAAQ;;gCAEzB1G,OAAO8E,GAAG,iBAAG,KAAC3I;oCAAMuK,UAAS;8CAAQ1G,OAAO8E,GAAG;qCAAY;gCAC3D9E,OAAO2G,IAAI,GAAG,kBACb,KAAC3J;oCAAWwH,SAAQ;oCAAUC,OAAM;8CACjC,GAAGzE,OAAO2G,IAAI,CAAC,CAAC,EAAE3G,OAAO2G,IAAI,KAAK,IAAI,gBAAgB,gBAAgB,kBAAkB,EACvF3G,OAAO2G,IAAI,KAAK,IAAI,gBAAgB,gBACrC,oCAAoC,CAAC;qCAEtC;;6BAEJ;wBACH7G,uBACC,KAAC3D;4BACCuK,UAAS;4BACTE,QACEtG,6BACE,KAACjE;gCACCoI,OAAM;gCACNW,MAAK;gCACLZ,SAAQ;gCACRS,SAAS;oCACPlH,WAAW;wCAAEkB,OAAOqB;oCAAa;oCACjC3B;gCACF;0CAEC;iCAED4D;sCAGLzC;6BAED;wBACHd,YAAY,cAAcoB,eAAe,qBACxC,KAACxD;4BACCiK,uBAAS,KAAC/J;gCAAOgK,SAAS1G;gCAAYiE,UAAU,IAAM,KAAK7B;;4BAC3DwB,OAAM;6BAEN;;;;0BAGR,MAACvH;;oBACEgD,UAAU,YAAY,CAACR,sBACtB,KAAC5C;wBACC4I,SAAS;4BACPhF,UAAU;4BACVN,YAAY;4BACZD,SAAS;wBACX;kCAEC;yBAED;kCACJ,KAACrD;wBAAO4I,SAAStG;kCAAU;;oBAC1Bc,UAAU,sBACT,KAACpD;wBAAOmI,SAAQ;wBAAYS,SAAS,IAAM,KAAKvC;wBAAOqE,UAAU,CAAC7H,SAAS0D,IAAI;kCAC5E;yBAED;;;;;AAIZ;AAEA,eAAepE,gBAAe"}
|
|
@@ -27,8 +27,13 @@ import { AiSiteStarterFallback } from './ai-site-starter-fallback.component';
|
|
|
27
27
|
*/
|
|
28
28
|
export interface AiJobPlanProps {
|
|
29
29
|
job: AiJobSummary;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Confirms the plan, or tries the refused step again. A build whose request
|
|
32
|
+
* asked to publish confirms with `publish` when its box is ticked (AGL-3616).
|
|
33
|
+
*/
|
|
34
|
+
onResume: (job: AiJobSummary, options?: {
|
|
35
|
+
publish?: boolean;
|
|
36
|
+
}) => void;
|
|
32
37
|
/** A resume for this job is in flight. */
|
|
33
38
|
busy?: boolean;
|
|
34
39
|
/**
|