@aglyn/plugins-workflows 1.0.0-beta.229 → 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.
@@ -18,6 +18,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
18
18
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
19
  import { checkEntitlement, hostEventLabel, pluginDocsHelp } from "@aglyn/aglyn";
20
20
  import { hostIdsFromScope, isOrgWideScope } from "@aglyn/aglyn/app-utils/scope-tokens";
21
+ import { useConsoleWidgetSlot } from "@aglyn/aglyn/app-utils/console-widget-slot-context";
21
22
  import { CardDisplay, useConfirmationContext } from "@aglyn/shared-ui-jsx";
22
23
  import { ListPagination } from "@aglyn/shared-ui-jsx/components/list-pagination.component";
23
24
  import { TABLE_PAGE_SIZE_DEFAULT } from "@aglyn/shared-ui-jsx/const/table-pagination";
@@ -26,8 +27,9 @@ import { useFirestore, useFirestoreCollection } from "@aglyn/tenant-feature-inst
26
27
  import { Alert, Button, Chip, Menu, MenuItem, Stack, Switch, Typography } from "@mui/material";
27
28
  import { collection, documentId, limit, orderBy, query, where } from "firebase/firestore";
28
29
  import { useCallback, useMemo, useState } from "react";
29
- import { ORG_AUTOMATIONS_COLLECTION, ORG_AUTOMATIONS_MAX, orgAutomationPausedHostIds, readOrgAutomation } from "../model/org-automations.js";
30
- import { newOrgAutomationDraft, OrgAutomationEditor, orgAutomationBody, orgAutomationDraft } from "./org-automation-editor.component.js";
30
+ import { ORG_AUTOMATION_STEP_TYPES, ORG_AUTOMATION_TRIGGER_EVENTS, ORG_AUTOMATIONS_COLLECTION, ORG_AUTOMATIONS_MAX, orgAutomationPausedHostIds, readOrgAutomation } from "../model/org-automations.js";
31
+ import { newOrgAutomationDraft, OrgAutomationEditor, orgAutomationBody, orgAutomationDraft, orgAutomationDraftFromProposal } from "./org-automation-editor.component.js";
32
+ import { ORG_AUTOMATIONS_ZONE } from "./workflow-zones.js";
31
33
  import { useOrgAutomationsApi } from "./use-org-automations-api.js";
32
34
  import { orgSiteName } from "./workflows-org-mount.js";
33
35
  import { HOST_ACTION_STEP_LABELS } from "../model/host-actions.js";
@@ -99,19 +101,47 @@ import { HOST_ACTION_STEP_LABELS } from "../model/host-actions.js";
99
101
  }), [
100
102
  enqueueSnackbar
101
103
  ]);
104
+ const refuseUnentitled = useCallback(()=>{
105
+ enqueueSnackbar('Org automations are built from the actions builder, which requires a Pro plan — see Billing to upgrade', {
106
+ variant: 'warning',
107
+ persist: false
108
+ });
109
+ }, [
110
+ enqueueSnackbar
111
+ ]);
102
112
  const handleAdd = useCallback(()=>{
113
+ if (!entitled) return refuseUnentitled();
114
+ setDraft(newOrgAutomationDraft());
115
+ }, [
116
+ entitled,
117
+ refuseUnentitled
118
+ ]);
119
+ /*
120
+ * For the `orgAutomations` zone (AGL-3603): an automation another plugin
121
+ * proposed opens in the editor as a new one, unsaved and switched off —
122
+ * the same door Add org automation opens, so the plan is asked first and
123
+ * Save is still the only write.
124
+ */ const propose = useCallback((proposal)=>{
103
125
  if (!entitled) {
104
- enqueueSnackbar('Org automations are built from the actions builder, which requires a Pro plan — see Billing to upgrade', {
105
- variant: 'warning',
106
- persist: false
107
- });
108
- return;
126
+ refuseUnentitled();
127
+ return false;
109
128
  }
110
- setDraft(newOrgAutomationDraft());
129
+ const opened = orgAutomationDraftFromProposal(proposal);
130
+ if (!opened) return false;
131
+ setDraft(opened);
132
+ return true;
111
133
  }, [
112
134
  entitled,
113
- enqueueSnackbar
135
+ refuseUnentitled
114
136
  ]);
137
+ const ExtensionZone = useConsoleWidgetSlot();
138
+ const automationsZone = ExtensionZone && canEdit ? /*#__PURE__*/ _jsx(ExtensionZone, {
139
+ slot: ORG_AUTOMATIONS_ZONE.id,
140
+ orgId: mount.orgId,
141
+ triggers: ORG_AUTOMATION_TRIGGER_EVENTS,
142
+ steps: ORG_AUTOMATION_STEP_TYPES,
143
+ propose: propose
144
+ }) : null;
115
145
  const handleSave = useCallback(async ()=>{
116
146
  if (!draft) return;
117
147
  const body = orgAutomationBody(draft);
@@ -212,6 +242,8 @@ import { HOST_ACTION_STEP_LABELS } from "../model/host-actions.js";
212
242
  help: pluginDocsHelp('orgAutomations', {
213
243
  anchor: '#what-an-org-automation-is'
214
244
  }),
245
+ // Other ways to start an org automation (AGL-3603), in the header.
246
+ actions: automationsZone != null ? automationsZone : undefined,
215
247
  contentGutterX: true,
216
248
  contentGutterY: true,
217
249
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/org-automations-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 {\n type AglynOrgBilling,\n checkEntitlement,\n hostEventLabel,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n hostIdsFromScope,\n isOrgWideScope,\n} from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useFirestoreCollection,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Chip,\n Menu,\n MenuItem,\n Stack,\n Switch,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n documentId,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n ORG_AUTOMATIONS_COLLECTION,\n ORG_AUTOMATIONS_MAX,\n orgAutomationPausedHostIds,\n type OrgAutomationRow,\n readOrgAutomation,\n} from '../model/org-automations'\nimport {\n newOrgAutomationDraft,\n OrgAutomationEditor,\n orgAutomationBody,\n type OrgAutomationDraft,\n orgAutomationDraft,\n} from './org-automation-editor.component'\nimport { useOrgAutomationsApi } from './use-org-automations-api'\nimport { orgSiteName, type WorkflowsOrgMount } from './workflows-org-mount'\nimport { HOST_ACTION_STEP_LABELS, type HostActionStepType } from '../model/host-actions'\n\nexport interface OrgAutomationsCardProps {\n mount: WorkflowsOrgMount\n org?: Partial<AglynOrgBilling>\n /**\n * Whether the reader may write the organization's automations — an owner,\n * admin or editor. The route decides; this keeps a viewer from being\n * offered controls that are about to refuse them.\n */\n canEdit: boolean\n}\n\n/** The sites an automation runs on, by id: every org site for `['org']`. */\nfunction placedSiteIds(\n row: Pick<OrgAutomationRow, 'visibleTo'>,\n mount: WorkflowsOrgMount,\n): string[] {\n return isOrgWideScope(row.visibleTo)\n ? mount.hosts.map((host) => host.id)\n : hostIdsFromScope(row.visibleTo)\n}\n\n/** \"Runs on every site\" or \"Runs on Shop, Blog\". */\nfunction placementLine(\n row: Pick<OrgAutomationRow, 'visibleTo'>,\n mount: WorkflowsOrgMount,\n): string {\n if (isOrgWideScope(row.visibleTo)) return 'Runs on every site'\n const names = hostIdsFromScope(row.visibleTo).map((id) => orgSiteName(mount, id))\n return names.length ? `Runs on ${names.join(', ')}` : 'Runs on no site'\n}\n\n/**\n * THE ORGANIZATION'S AUTOMATIONS (AGL-3302): what the organization runs on\n * the sites it chooses.\n *\n * One ceilinged read of the live automations — the org hub admits only\n * org-wide members, who read every one — and every write through\n * `automations/manage` or `automations/pause`, because the collection is\n * closed to client writes. Each row says where it runs and where it is\n * paused; a paused site is resumed from its chip, and any placed site can be\n * paused from the row, which is the same control the site's own hub offers.\n */\nexport function OrgAutomationsCard(props: OrgAutomationsCardProps) {\n const { mount, org, canEdit } = props\n const firestore = useFirestore()\n const api = useOrgAutomationsApi()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n\n /*\n * Every LIVE automation, ceilinged at the organization's cap plus the probe\n * row. `deletedAt` is always written — `null` while live — so equality asks\n * for exactly the live ones and deleted documents never take a place in the\n * window. Ordered by document id, which the automatic single-field index\n * carries behind the equality, so the window is named rather than whatever\n * order Firestore returns; the list is sorted by name once it is in hand.\n */\n const { data: rows } = useFirestoreCollection<OrgAutomationRow>(\n () =>\n query(\n collection(firestore, 'orgs', mount.orgId, ORG_AUTOMATIONS_COLLECTION),\n where('deletedAt', '==', null),\n orderBy(documentId()),\n limit(ORG_AUTOMATIONS_MAX + 1),\n ),\n [firestore, mount.orgId],\n { idField: '$id' },\n )\n const automations = useMemo(\n () =>\n [...(rows ?? [])]\n .slice(0, ORG_AUTOMATIONS_MAX)\n .sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''))),\n [rows],\n )\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const shown = useMemo(\n () => automations.slice(page * pageSize, page * pageSize + pageSize),\n [automations, page, pageSize],\n )\n\n const [draft, setDraft] = useState<OrgAutomationDraft | null>(null)\n /**\n * The editor has been opened at least once — the latch the pickers key on,\n * so Cancel and the next Edit do not buy the option lists twice.\n */\n const [editorOpened, setEditorOpened] = useState(false)\n if (draft && !editorOpened) setEditorOpened(true)\n const [saving, setSaving] = useState(false)\n const [pauseMenu, setPauseMenu] = useState<{\n anchor: HTMLElement\n row: OrgAutomationRow\n } | null>(null)\n\n const entitled = checkEntitlement(org, 'actions')\n const fail = useCallback(\n (error: unknown) =>\n enqueueSnackbar((error as Error)?.message || 'The request could not be completed', {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const handleAdd = useCallback(() => {\n if (!entitled) {\n enqueueSnackbar(\n 'Org automations are built from the actions builder, which requires a Pro plan — see Billing to upgrade',\n { variant: 'warning', persist: false },\n )\n return\n }\n setDraft(newOrgAutomationDraft())\n }, [entitled, enqueueSnackbar])\n\n const handleSave = useCallback(async () => {\n if (!draft) return\n const body = orgAutomationBody(draft)\n // The route's own reader, asked first so a mistake reads the same here as\n // it would coming back from the server — and costs no round trip.\n const read = readOrgAutomation(body)\n if (read.ok === false) {\n return void enqueueSnackbar(read.problem, { variant: 'warning', persist: false })\n }\n setSaving(true)\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: draft.id ? 'update' : 'create',\n ...(draft.id ? { automationId: draft.id } : {}),\n automation: body,\n })\n setDraft(null)\n enqueueSnackbar('Org automation saved', { variant: 'success', persist: false })\n } catch (error) {\n fail(error)\n } finally {\n setSaving(false)\n }\n }, [draft, api, mount.orgId, enqueueSnackbar, fail])\n\n const handleToggle = useCallback(\n (row: OrgAutomationRow) => async (event: { target: { checked: boolean } }) => {\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: 'setEnabled',\n automationId: row.$id,\n enabled: event.target.checked,\n })\n } catch (error) {\n fail(error)\n }\n },\n [api, mount.orgId, fail],\n )\n\n const handleDelete = useCallback(\n (row: OrgAutomationRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this org automation?',\n description:\n `\"${row.name}\" stops running on every site it is placed on, and ` +\n 'anyone waiting inside it stops too.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: 'delete',\n automationId: row.$id,\n })\n } catch (error) {\n fail(error)\n }\n },\n [confirm, api, mount.orgId, fail],\n )\n\n const setPaused = useCallback(\n async (row: OrgAutomationRow, hostId: string, paused: boolean) => {\n setPauseMenu(null)\n try {\n await api('pause', { hostId, automationId: row.$id, paused })\n } catch (error) {\n fail(error)\n }\n },\n [api, fail],\n )\n\n return (\n <CardDisplay\n header={'Org automations'}\n help={pluginDocsHelp('orgAutomations', { anchor: '#what-an-org-automation-is' })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Write an automation once and run it on every site you choose. ' +\n 'Each run is that site’s own: its email goes from that site, it ' +\n 'counts on that site’s action runs, and the site can pause it ' +\n 'for itself. Pro plans and up.'}\n </Typography>\n {shown.map((row) => {\n const paused = orgAutomationPausedHostIds(row)\n const placed = placedSiteIds(row, mount)\n const pausable = placed.filter((hostId) => !paused.includes(hostId))\n return (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Switch\n size=\"small\"\n checked={row.enabled !== false}\n disabled={!canEdit}\n onChange={handleToggle(row)}\n slotProps={{ input: { 'aria-label': `Switch ${row.name} on or off` } }}\n />\n <Stack sx={{ flex: 1, minWidth: 0 }} spacing={0.25}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`on ${hostEventLabel(row.trigger?.event)} · ${(row.steps ?? [])\n .map(\n (step) =>\n HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ??\n step.type,\n )\n .join(' → ')}`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {placementLine(row, mount)}\n </Typography>\n {paused.length ? (\n <Stack direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap', gap: 0.5 }}>\n {paused.map((hostId) => (\n <Chip\n key={hostId}\n size=\"small\"\n variant=\"outlined\"\n color=\"warning\"\n label={`Paused on ${orgSiteName(mount, hostId)}`}\n {...(canEdit\n ? { onDelete: () => void setPaused(row, hostId, false) }\n : {})}\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n {canEdit ? (\n <>\n <Button size=\"small\" onClick={() => setDraft(orgAutomationDraft(row))}>\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n disabled={!pausable.length}\n aria-haspopup=\"menu\"\n onClick={(event) =>\n setPauseMenu({ anchor: event.currentTarget, row })\n }\n >\n {'Pause on…'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={handleDelete(row)}>\n {'Delete'}\n </Button>\n </>\n ) : null}\n </Stack>\n )\n })}\n {automations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No org automations yet.'}\n </Typography>\n ) : (\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={shown.length}\n count={automations.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )}\n {(rows?.length ?? 0) > ORG_AUTOMATIONS_MAX ? (\n <Alert severity=\"info\">\n {`Showing ${ORG_AUTOMATIONS_MAX} org automations, the most an ` +\n 'organization holds.'}\n </Alert>\n ) : null}\n {canEdit ? (\n <Button\n size=\"small\"\n color=\"primary\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={handleAdd}\n >\n {'Add org automation'}\n </Button>\n ) : null}\n </Stack>\n <Menu\n anchorEl={pauseMenu?.anchor ?? null}\n open={Boolean(pauseMenu)}\n onClose={() => setPauseMenu(null)}\n >\n {pauseMenu\n ? placedSiteIds(pauseMenu.row, mount)\n .filter(\n (hostId) =>\n !orgAutomationPausedHostIds(pauseMenu.row).includes(hostId),\n )\n .map((hostId) => (\n <MenuItem\n key={hostId}\n onClick={() => void setPaused(pauseMenu.row, hostId, true)}\n >\n {`Pause on ${orgSiteName(mount, hostId)}`}\n </MenuItem>\n ))\n : null}\n </Menu>\n <OrgAutomationEditor\n mount={mount}\n draft={draft}\n onDraft={(update) =>\n setDraft((previous) => (previous ? update(previous) : previous))\n }\n opened={editorOpened}\n saving={saving}\n onSave={() => void handleSave()}\n onClose={() => setDraft(null)}\n />\n </CardDisplay>\n )\n}\nOrgAutomationsCard.displayName = 'OrgAutomationsCard'\n\nexport default OrgAutomationsCard\n"],"names":["checkEntitlement","hostEventLabel","pluginDocsHelp","hostIdsFromScope","isOrgWideScope","CardDisplay","useConfirmationContext","ListPagination","TABLE_PAGE_SIZE_DEFAULT","useSnackbar","useFirestore","useFirestoreCollection","Alert","Button","Chip","Menu","MenuItem","Stack","Switch","Typography","collection","documentId","limit","orderBy","query","where","useCallback","useMemo","useState","ORG_AUTOMATIONS_COLLECTION","ORG_AUTOMATIONS_MAX","orgAutomationPausedHostIds","readOrgAutomation","newOrgAutomationDraft","OrgAutomationEditor","orgAutomationBody","orgAutomationDraft","useOrgAutomationsApi","orgSiteName","HOST_ACTION_STEP_LABELS","placedSiteIds","row","mount","visibleTo","hosts","map","host","id","placementLine","names","length","join","OrgAutomationsCard","props","org","canEdit","firestore","api","enqueueSnackbar","confirm","data","rows","orgId","idField","automations","slice","sort","a","b","String","name","localeCompare","page","setPage","pageSize","setPageSize","shown","draft","setDraft","editorOpened","setEditorOpened","saving","setSaving","pauseMenu","setPauseMenu","entitled","fail","error","message","variant","persist","handleAdd","handleSave","body","read","ok","problem","action","automationId","automation","handleToggle","event","$id","enabled","target","checked","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","setPaused","hostId","paused","header","help","anchor","contentGutterX","contentGutterY","spacing","placed","pausable","filter","includes","direction","sx","alignItems","size","disabled","onChange","slotProps","input","flex","minWidth","noWrap","trigger","steps","step","type","flexWrap","gap","label","onDelete","onClick","aria-haspopup","currentTarget","rowCount","count","onPageChange","onPageSizeChange","severity","alignSelf","anchorEl","open","Boolean","onClose","onDraft","update","previous","opened","onSave","displayName"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/org-automations-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 {\n type AglynOrgBilling,\n checkEntitlement,\n hostEventLabel,\n pluginDocsHelp,\n} from '@aglyn/aglyn'\nimport {\n hostIdsFromScope,\n isOrgWideScope,\n} from '@aglyn/aglyn/app-utils/scope-tokens'\nimport { useConsoleWidgetSlot } from '@aglyn/aglyn/app-utils/console-widget-slot-context'\nimport { CardDisplay, useConfirmationContext } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport { TABLE_PAGE_SIZE_DEFAULT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useSnackbar } from '@aglyn/shared-ui-snackstack'\nimport {\n useFirestore,\n useFirestoreCollection,\n} from '@aglyn/tenant-feature-instance'\nimport {\n Alert,\n Button,\n Chip,\n Menu,\n MenuItem,\n Stack,\n Switch,\n Typography,\n} from '@mui/material'\nimport {\n collection,\n documentId,\n limit,\n orderBy,\n query,\n where,\n} from 'firebase/firestore'\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n ORG_AUTOMATION_STEP_TYPES,\n ORG_AUTOMATION_TRIGGER_EVENTS,\n ORG_AUTOMATIONS_COLLECTION,\n ORG_AUTOMATIONS_MAX,\n orgAutomationPausedHostIds,\n type OrgAutomationRow,\n readOrgAutomation,\n} from '../model/org-automations'\nimport {\n newOrgAutomationDraft,\n OrgAutomationEditor,\n orgAutomationBody,\n type OrgAutomationDraft,\n orgAutomationDraft,\n orgAutomationDraftFromProposal,\n} from './org-automation-editor.component'\nimport { ORG_AUTOMATIONS_ZONE, type OrgAutomationProposal } from './workflow-zones'\nimport { useOrgAutomationsApi } from './use-org-automations-api'\nimport { orgSiteName, type WorkflowsOrgMount } from './workflows-org-mount'\nimport { HOST_ACTION_STEP_LABELS, type HostActionStepType } from '../model/host-actions'\n\nexport interface OrgAutomationsCardProps {\n mount: WorkflowsOrgMount\n org?: Partial<AglynOrgBilling>\n /**\n * Whether the reader may write the organization's automations — an owner,\n * admin or editor. The route decides; this keeps a viewer from being\n * offered controls that are about to refuse them.\n */\n canEdit: boolean\n}\n\n/** The sites an automation runs on, by id: every org site for `['org']`. */\nfunction placedSiteIds(\n row: Pick<OrgAutomationRow, 'visibleTo'>,\n mount: WorkflowsOrgMount,\n): string[] {\n return isOrgWideScope(row.visibleTo)\n ? mount.hosts.map((host) => host.id)\n : hostIdsFromScope(row.visibleTo)\n}\n\n/** \"Runs on every site\" or \"Runs on Shop, Blog\". */\nfunction placementLine(\n row: Pick<OrgAutomationRow, 'visibleTo'>,\n mount: WorkflowsOrgMount,\n): string {\n if (isOrgWideScope(row.visibleTo)) return 'Runs on every site'\n const names = hostIdsFromScope(row.visibleTo).map((id) => orgSiteName(mount, id))\n return names.length ? `Runs on ${names.join(', ')}` : 'Runs on no site'\n}\n\n/**\n * THE ORGANIZATION'S AUTOMATIONS (AGL-3302): what the organization runs on\n * the sites it chooses.\n *\n * One ceilinged read of the live automations — the org hub admits only\n * org-wide members, who read every one — and every write through\n * `automations/manage` or `automations/pause`, because the collection is\n * closed to client writes. Each row says where it runs and where it is\n * paused; a paused site is resumed from its chip, and any placed site can be\n * paused from the row, which is the same control the site's own hub offers.\n */\nexport function OrgAutomationsCard(props: OrgAutomationsCardProps) {\n const { mount, org, canEdit } = props\n const firestore = useFirestore()\n const api = useOrgAutomationsApi()\n const { enqueueSnackbar } = useSnackbar()\n const { confirm } = useConfirmationContext()\n\n /*\n * Every LIVE automation, ceilinged at the organization's cap plus the probe\n * row. `deletedAt` is always written — `null` while live — so equality asks\n * for exactly the live ones and deleted documents never take a place in the\n * window. Ordered by document id, which the automatic single-field index\n * carries behind the equality, so the window is named rather than whatever\n * order Firestore returns; the list is sorted by name once it is in hand.\n */\n const { data: rows } = useFirestoreCollection<OrgAutomationRow>(\n () =>\n query(\n collection(firestore, 'orgs', mount.orgId, ORG_AUTOMATIONS_COLLECTION),\n where('deletedAt', '==', null),\n orderBy(documentId()),\n limit(ORG_AUTOMATIONS_MAX + 1),\n ),\n [firestore, mount.orgId],\n { idField: '$id' },\n )\n const automations = useMemo(\n () =>\n [...(rows ?? [])]\n .slice(0, ORG_AUTOMATIONS_MAX)\n .sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''))),\n [rows],\n )\n const [page, setPage] = useState(0)\n const [pageSize, setPageSize] = useState(TABLE_PAGE_SIZE_DEFAULT)\n const shown = useMemo(\n () => automations.slice(page * pageSize, page * pageSize + pageSize),\n [automations, page, pageSize],\n )\n\n const [draft, setDraft] = useState<OrgAutomationDraft | null>(null)\n /**\n * The editor has been opened at least once — the latch the pickers key on,\n * so Cancel and the next Edit do not buy the option lists twice.\n */\n const [editorOpened, setEditorOpened] = useState(false)\n if (draft && !editorOpened) setEditorOpened(true)\n const [saving, setSaving] = useState(false)\n const [pauseMenu, setPauseMenu] = useState<{\n anchor: HTMLElement\n row: OrgAutomationRow\n } | null>(null)\n\n const entitled = checkEntitlement(org, 'actions')\n const fail = useCallback(\n (error: unknown) =>\n enqueueSnackbar((error as Error)?.message || 'The request could not be completed', {\n variant: 'warning',\n persist: false,\n }),\n [enqueueSnackbar],\n )\n\n const refuseUnentitled = useCallback(() => {\n enqueueSnackbar(\n 'Org automations are built from the actions builder, which requires a Pro plan — see Billing to upgrade',\n { variant: 'warning', persist: false },\n )\n }, [enqueueSnackbar])\n\n const handleAdd = useCallback(() => {\n if (!entitled) return refuseUnentitled()\n setDraft(newOrgAutomationDraft())\n }, [entitled, refuseUnentitled])\n\n /*\n * For the `orgAutomations` zone (AGL-3603): an automation another plugin\n * proposed opens in the editor as a new one, unsaved and switched off —\n * the same door Add org automation opens, so the plan is asked first and\n * Save is still the only write.\n */\n const propose = useCallback(\n (proposal: OrgAutomationProposal) => {\n if (!entitled) {\n refuseUnentitled()\n return false\n }\n const opened = orgAutomationDraftFromProposal(proposal)\n if (!opened) return false\n setDraft(opened)\n return true\n },\n [entitled, refuseUnentitled],\n )\n const ExtensionZone = useConsoleWidgetSlot()\n const automationsZone =\n ExtensionZone && canEdit ? (\n <ExtensionZone\n slot={ORG_AUTOMATIONS_ZONE.id}\n orgId={mount.orgId}\n triggers={ORG_AUTOMATION_TRIGGER_EVENTS}\n steps={ORG_AUTOMATION_STEP_TYPES}\n propose={propose}\n />\n ) : null\n\n const handleSave = useCallback(async () => {\n if (!draft) return\n const body = orgAutomationBody(draft)\n // The route's own reader, asked first so a mistake reads the same here as\n // it would coming back from the server — and costs no round trip.\n const read = readOrgAutomation(body)\n if (read.ok === false) {\n return void enqueueSnackbar(read.problem, { variant: 'warning', persist: false })\n }\n setSaving(true)\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: draft.id ? 'update' : 'create',\n ...(draft.id ? { automationId: draft.id } : {}),\n automation: body,\n })\n setDraft(null)\n enqueueSnackbar('Org automation saved', { variant: 'success', persist: false })\n } catch (error) {\n fail(error)\n } finally {\n setSaving(false)\n }\n }, [draft, api, mount.orgId, enqueueSnackbar, fail])\n\n const handleToggle = useCallback(\n (row: OrgAutomationRow) => async (event: { target: { checked: boolean } }) => {\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: 'setEnabled',\n automationId: row.$id,\n enabled: event.target.checked,\n })\n } catch (error) {\n fail(error)\n }\n },\n [api, mount.orgId, fail],\n )\n\n const handleDelete = useCallback(\n (row: OrgAutomationRow) => async () => {\n const confirmed = await confirm({\n title: 'Delete this org automation?',\n description:\n `\"${row.name}\" stops running on every site it is placed on, and ` +\n 'anyone waiting inside it stops too.',\n confirmationText: 'Delete',\n confirmationButtonProps: { color: 'error' },\n })\n .then(() => true)\n .catch(() => false)\n if (!confirmed) return\n try {\n await api('manage', {\n orgId: mount.orgId,\n action: 'delete',\n automationId: row.$id,\n })\n } catch (error) {\n fail(error)\n }\n },\n [confirm, api, mount.orgId, fail],\n )\n\n const setPaused = useCallback(\n async (row: OrgAutomationRow, hostId: string, paused: boolean) => {\n setPauseMenu(null)\n try {\n await api('pause', { hostId, automationId: row.$id, paused })\n } catch (error) {\n fail(error)\n }\n },\n [api, fail],\n )\n\n return (\n <CardDisplay\n header={'Org automations'}\n help={pluginDocsHelp('orgAutomations', { anchor: '#what-an-org-automation-is' })}\n // Other ways to start an org automation (AGL-3603), in the header.\n actions={automationsZone ?? undefined}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={1}>\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'Write an automation once and run it on every site you choose. ' +\n 'Each run is that site’s own: its email goes from that site, it ' +\n 'counts on that site’s action runs, and the site can pause it ' +\n 'for itself. Pro plans and up.'}\n </Typography>\n {shown.map((row) => {\n const paused = orgAutomationPausedHostIds(row)\n const placed = placedSiteIds(row, mount)\n const pausable = placed.filter((hostId) => !paused.includes(hostId))\n return (\n <Stack\n key={row.$id}\n direction=\"row\"\n spacing={1}\n sx={{ alignItems: 'center' }}\n >\n <Switch\n size=\"small\"\n checked={row.enabled !== false}\n disabled={!canEdit}\n onChange={handleToggle(row)}\n slotProps={{ input: { 'aria-label': `Switch ${row.name} on or off` } }}\n />\n <Stack sx={{ flex: 1, minWidth: 0 }} spacing={0.25}>\n <Typography variant=\"body2\" noWrap>\n {row.name}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {`on ${hostEventLabel(row.trigger?.event)} · ${(row.steps ?? [])\n .map(\n (step) =>\n HOST_ACTION_STEP_LABELS[step.type as HostActionStepType] ??\n step.type,\n )\n .join(' → ')}`}\n </Typography>\n <Typography variant=\"caption\" color=\"text.secondary\" noWrap>\n {placementLine(row, mount)}\n </Typography>\n {paused.length ? (\n <Stack direction=\"row\" spacing={0.5} sx={{ flexWrap: 'wrap', gap: 0.5 }}>\n {paused.map((hostId) => (\n <Chip\n key={hostId}\n size=\"small\"\n variant=\"outlined\"\n color=\"warning\"\n label={`Paused on ${orgSiteName(mount, hostId)}`}\n {...(canEdit\n ? { onDelete: () => void setPaused(row, hostId, false) }\n : {})}\n />\n ))}\n </Stack>\n ) : null}\n </Stack>\n {canEdit ? (\n <>\n <Button size=\"small\" onClick={() => setDraft(orgAutomationDraft(row))}>\n {'Edit'}\n </Button>\n <Button\n size=\"small\"\n disabled={!pausable.length}\n aria-haspopup=\"menu\"\n onClick={(event) =>\n setPauseMenu({ anchor: event.currentTarget, row })\n }\n >\n {'Pause on…'}\n </Button>\n <Button size=\"small\" color=\"error\" onClick={handleDelete(row)}>\n {'Delete'}\n </Button>\n </>\n ) : null}\n </Stack>\n )\n })}\n {automations.length === 0 ? (\n <Typography variant=\"body2\" color=\"text.secondary\">\n {'No org automations yet.'}\n </Typography>\n ) : (\n <ListPagination\n page={page}\n pageSize={pageSize}\n rowCount={shown.length}\n count={automations.length}\n onPageChange={setPage}\n onPageSizeChange={setPageSize}\n />\n )}\n {(rows?.length ?? 0) > ORG_AUTOMATIONS_MAX ? (\n <Alert severity=\"info\">\n {`Showing ${ORG_AUTOMATIONS_MAX} org automations, the most an ` +\n 'organization holds.'}\n </Alert>\n ) : null}\n {canEdit ? (\n <Button\n size=\"small\"\n color=\"primary\"\n sx={{ alignSelf: 'flex-start' }}\n onClick={handleAdd}\n >\n {'Add org automation'}\n </Button>\n ) : null}\n </Stack>\n <Menu\n anchorEl={pauseMenu?.anchor ?? null}\n open={Boolean(pauseMenu)}\n onClose={() => setPauseMenu(null)}\n >\n {pauseMenu\n ? placedSiteIds(pauseMenu.row, mount)\n .filter(\n (hostId) =>\n !orgAutomationPausedHostIds(pauseMenu.row).includes(hostId),\n )\n .map((hostId) => (\n <MenuItem\n key={hostId}\n onClick={() => void setPaused(pauseMenu.row, hostId, true)}\n >\n {`Pause on ${orgSiteName(mount, hostId)}`}\n </MenuItem>\n ))\n : null}\n </Menu>\n <OrgAutomationEditor\n mount={mount}\n draft={draft}\n onDraft={(update) =>\n setDraft((previous) => (previous ? update(previous) : previous))\n }\n opened={editorOpened}\n saving={saving}\n onSave={() => void handleSave()}\n onClose={() => setDraft(null)}\n />\n </CardDisplay>\n )\n}\nOrgAutomationsCard.displayName = 'OrgAutomationsCard'\n\nexport default OrgAutomationsCard\n"],"names":["checkEntitlement","hostEventLabel","pluginDocsHelp","hostIdsFromScope","isOrgWideScope","useConsoleWidgetSlot","CardDisplay","useConfirmationContext","ListPagination","TABLE_PAGE_SIZE_DEFAULT","useSnackbar","useFirestore","useFirestoreCollection","Alert","Button","Chip","Menu","MenuItem","Stack","Switch","Typography","collection","documentId","limit","orderBy","query","where","useCallback","useMemo","useState","ORG_AUTOMATION_STEP_TYPES","ORG_AUTOMATION_TRIGGER_EVENTS","ORG_AUTOMATIONS_COLLECTION","ORG_AUTOMATIONS_MAX","orgAutomationPausedHostIds","readOrgAutomation","newOrgAutomationDraft","OrgAutomationEditor","orgAutomationBody","orgAutomationDraft","orgAutomationDraftFromProposal","ORG_AUTOMATIONS_ZONE","useOrgAutomationsApi","orgSiteName","HOST_ACTION_STEP_LABELS","placedSiteIds","row","mount","visibleTo","hosts","map","host","id","placementLine","names","length","join","OrgAutomationsCard","props","org","canEdit","firestore","api","enqueueSnackbar","confirm","data","rows","orgId","idField","automations","slice","sort","a","b","String","name","localeCompare","page","setPage","pageSize","setPageSize","shown","draft","setDraft","editorOpened","setEditorOpened","saving","setSaving","pauseMenu","setPauseMenu","entitled","fail","error","message","variant","persist","refuseUnentitled","handleAdd","propose","proposal","opened","ExtensionZone","automationsZone","slot","triggers","steps","handleSave","body","read","ok","problem","action","automationId","automation","handleToggle","event","$id","enabled","target","checked","handleDelete","confirmed","title","description","confirmationText","confirmationButtonProps","color","then","catch","setPaused","hostId","paused","header","help","anchor","actions","undefined","contentGutterX","contentGutterY","spacing","placed","pausable","filter","includes","direction","sx","alignItems","size","disabled","onChange","slotProps","input","flex","minWidth","noWrap","trigger","step","type","flexWrap","gap","label","onDelete","onClick","aria-haspopup","currentTarget","rowCount","count","onPageChange","onPageSizeChange","severity","alignSelf","anchorEl","open","Boolean","onClose","onDraft","update","previous","onSave","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;;AAEA,SAEEA,gBAAgB,EAChBC,cAAc,EACdC,cAAc,QACT,eAAc;AACrB,SACEC,gBAAgB,EAChBC,cAAc,QACT,sCAAqC;AAC5C,SAASC,oBAAoB,QAAQ,qDAAoD;AACzF,SAASC,WAAW,EAAEC,sBAAsB,QAAQ,uBAAsB;AAC1E,SAASC,cAAc,QAAQ,4DAA2D;AAC1F,SAASC,uBAAuB,QAAQ,8CAA6C;AACrF,SAASC,WAAW,QAAQ,8BAA6B;AACzD,SACEC,YAAY,EACZC,sBAAsB,QACjB,iCAAgC;AACvC,SACEC,KAAK,EACLC,MAAM,EACNC,IAAI,EACJC,IAAI,EACJC,QAAQ,EACRC,KAAK,EACLC,MAAM,EACNC,UAAU,QACL,gBAAe;AACtB,SACEC,UAAU,EACVC,UAAU,EACVC,KAAK,EACLC,OAAO,EACPC,KAAK,EACLC,KAAK,QACA,qBAAoB;AAC3B,SAASC,WAAW,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAO;AACtD,SACEC,yBAAyB,EACzBC,6BAA6B,EAC7BC,0BAA0B,EAC1BC,mBAAmB,EACnBC,0BAA0B,EAE1BC,iBAAiB,QACZ,8BAA0B;AACjC,SACEC,qBAAqB,EACrBC,mBAAmB,EACnBC,iBAAiB,EAEjBC,kBAAkB,EAClBC,8BAA8B,QACzB,uCAAmC;AAC1C,SAASC,oBAAoB,QAAoC,sBAAkB;AACnF,SAASC,oBAAoB,QAAQ,+BAA2B;AAChE,SAASC,WAAW,QAAgC,2BAAuB;AAC3E,SAASC,uBAAuB,QAAiC,2BAAuB;AAaxF,0EAA0E,GAC1E,SAASC,cACPC,GAAwC,EACxCC,KAAwB;IAExB,OAAO3C,eAAe0C,IAAIE,SAAS,IAC/BD,MAAME,KAAK,CAACC,GAAG,CAAC,CAACC,OAASA,KAAKC,EAAE,IACjCjD,iBAAiB2C,IAAIE,SAAS;AACpC;AAEA,kDAAkD,GAClD,SAASK,cACPP,GAAwC,EACxCC,KAAwB;IAExB,IAAI3C,eAAe0C,IAAIE,SAAS,GAAG,OAAO;IAC1C,MAAMM,QAAQnD,iBAAiB2C,IAAIE,SAAS,EAAEE,GAAG,CAAC,CAACE,KAAOT,YAAYI,OAAOK;IAC7E,OAAOE,MAAMC,MAAM,GAAG,CAAC,QAAQ,EAAED,MAAME,IAAI,CAAC,OAAO,GAAG;AACxD;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASC,mBAAmBC,KAA8B;;IAC/D,MAAM,EAAEX,KAAK,EAAEY,GAAG,EAAEC,OAAO,EAAE,GAAGF;IAChC,MAAMG,YAAYlD;IAClB,MAAMmD,MAAMpB;IACZ,MAAM,EAAEqB,eAAe,EAAE,GAAGrD;IAC5B,MAAM,EAAEsD,OAAO,EAAE,GAAGzD;IAEpB;;;;;;;GAOC,GACD,MAAM,EAAE0D,MAAMC,IAAI,EAAE,GAAGtD,uBACrB,IACEa,MACEJ,WAAWwC,WAAW,QAAQd,MAAMoB,KAAK,EAAEnC,6BAC3CN,MAAM,aAAa,MAAM,OACzBF,QAAQF,eACRC,MAAMU,sBAAsB,KAEhC;QAAC4B;QAAWd,MAAMoB,KAAK;KAAC,EACxB;QAAEC,SAAS;IAAM;IAEnB,MAAMC,cAAczC,QAClB,IACE;eAAKsC,eAAAA,OAAQ,EAAE;SAAE,CACdI,KAAK,CAAC,GAAGrC,qBACTsC,IAAI,CAAC,CAACC,GAAGC;gBAAaD,SAAmCC;mBAA1CC,QAAOF,UAAAA,EAAEG,IAAI,YAANH,UAAU,IAAII,aAAa,CAACF,QAAOD,UAAAA,EAAEE,IAAI,YAANF,UAAU;YACxE;QAACP;KAAK;IAER,MAAM,CAACW,MAAMC,QAAQ,GAAGjD,SAAS;IACjC,MAAM,CAACkD,UAAUC,YAAY,GAAGnD,SAASpB;IACzC,MAAMwE,QAAQrD,QACZ,IAAMyC,YAAYC,KAAK,CAACO,OAAOE,UAAUF,OAAOE,WAAWA,WAC3D;QAACV;QAAaQ;QAAME;KAAS;IAG/B,MAAM,CAACG,OAAOC,SAAS,GAAGtD,SAAoC;IAC9D;;;GAGC,GACD,MAAM,CAACuD,cAAcC,gBAAgB,GAAGxD,SAAS;IACjD,IAAIqD,SAAS,CAACE,cAAcC,gBAAgB;IAC5C,MAAM,CAACC,QAAQC,UAAU,GAAG1D,SAAS;IACrC,MAAM,CAAC2D,WAAWC,aAAa,GAAG5D,SAGxB;IAEV,MAAM6D,WAAW1F,iBAAiB2D,KAAK;IACvC,MAAMgC,OAAOhE,YACX,CAACiE,QACC7B,gBAAgB,CAAC6B,yBAAD,AAACA,MAAiBC,OAAO,KAAI,sCAAsC;YACjFC,SAAS;YACTC,SAAS;QACX,IACF;QAAChC;KAAgB;IAGnB,MAAMiC,mBAAmBrE,YAAY;QACnCoC,gBACE,0GACA;YAAE+B,SAAS;YAAWC,SAAS;QAAM;IAEzC,GAAG;QAAChC;KAAgB;IAEpB,MAAMkC,YAAYtE,YAAY;QAC5B,IAAI,CAAC+D,UAAU,OAAOM;QACtBb,SAAS/C;IACX,GAAG;QAACsD;QAAUM;KAAiB;IAE/B;;;;;GAKC,GACD,MAAME,UAAUvE,YACd,CAACwE;QACC,IAAI,CAACT,UAAU;YACbM;YACA,OAAO;QACT;QACA,MAAMI,SAAS5D,+BAA+B2D;QAC9C,IAAI,CAACC,QAAQ,OAAO;QACpBjB,SAASiB;QACT,OAAO;IACT,GACA;QAACV;QAAUM;KAAiB;IAE9B,MAAMK,gBAAgBhG;IACtB,MAAMiG,kBACJD,iBAAiBzC,wBACf,KAACyC;QACCE,MAAM9D,qBAAqBW,EAAE;QAC7Be,OAAOpB,MAAMoB,KAAK;QAClBqC,UAAUzE;QACV0E,OAAO3E;QACPoE,SAASA;SAET;IAEN,MAAMQ,aAAa/E,YAAY;QAC7B,IAAI,CAACuD,OAAO;QACZ,MAAMyB,OAAOrE,kBAAkB4C;QAC/B,0EAA0E;QAC1E,kEAAkE;QAClE,MAAM0B,OAAOzE,kBAAkBwE;QAC/B,IAAIC,KAAKC,EAAE,KAAK,OAAO;YACrB,OAAO,KAAK9C,gBAAgB6C,KAAKE,OAAO,EAAE;gBAAEhB,SAAS;gBAAWC,SAAS;YAAM;QACjF;QACAR,UAAU;QACV,IAAI;YACF,MAAMzB,IAAI,UAAU;gBAClBK,OAAOpB,MAAMoB,KAAK;gBAClB4C,QAAQ7B,MAAM9B,EAAE,GAAG,WAAW;eAC1B8B,MAAM9B,EAAE,GAAG;gBAAE4D,cAAc9B,MAAM9B,EAAE;YAAC,IAAI,CAAC;gBAC7C6D,YAAYN;;YAEdxB,SAAS;YACTpB,gBAAgB,wBAAwB;gBAAE+B,SAAS;gBAAWC,SAAS;YAAM;QAC/E,EAAE,OAAOH,OAAO;YACdD,KAAKC;QACP,SAAU;YACRL,UAAU;QACZ;IACF,GAAG;QAACL;QAAOpB;QAAKf,MAAMoB,KAAK;QAAEJ;QAAiB4B;KAAK;IAEnD,MAAMuB,eAAevF,YACnB,CAACmB,MAA0B,OAAOqE;YAChC,IAAI;gBACF,MAAMrD,IAAI,UAAU;oBAClBK,OAAOpB,MAAMoB,KAAK;oBAClB4C,QAAQ;oBACRC,cAAclE,IAAIsE,GAAG;oBACrBC,SAASF,MAAMG,MAAM,CAACC,OAAO;gBAC/B;YACF,EAAE,OAAO3B,OAAO;gBACdD,KAAKC;YACP;QACF,GACA;QAAC9B;QAAKf,MAAMoB,KAAK;QAAEwB;KAAK;IAG1B,MAAM6B,eAAe7F,YACnB,CAACmB,MAA0B;YACzB,MAAM2E,YAAY,MAAMzD,QAAQ;gBAC9B0D,OAAO;gBACPC,aACE,CAAC,CAAC,EAAE7E,IAAI6B,IAAI,CAAC,mDAAmD,CAAC,GACjE;gBACFiD,kBAAkB;gBAClBC,yBAAyB;oBAAEC,OAAO;gBAAQ;YAC5C,GACGC,IAAI,CAAC,IAAM,MACXC,KAAK,CAAC,IAAM;YACf,IAAI,CAACP,WAAW;YAChB,IAAI;gBACF,MAAM3D,IAAI,UAAU;oBAClBK,OAAOpB,MAAMoB,KAAK;oBAClB4C,QAAQ;oBACRC,cAAclE,IAAIsE,GAAG;gBACvB;YACF,EAAE,OAAOxB,OAAO;gBACdD,KAAKC;YACP;QACF,GACA;QAAC5B;QAASF;QAAKf,MAAMoB,KAAK;QAAEwB;KAAK;IAGnC,MAAMsC,YAAYtG,YAChB,OAAOmB,KAAuBoF,QAAgBC;QAC5C1C,aAAa;QACb,IAAI;YACF,MAAM3B,IAAI,SAAS;gBAAEoE;gBAAQlB,cAAclE,IAAIsE,GAAG;gBAAEe;YAAO;QAC7D,EAAE,OAAOvC,OAAO;YACdD,KAAKC;QACP;IACF,GACA;QAAC9B;QAAK6B;KAAK;IAGb,qBACE,MAACrF;QACC8H,QAAQ;QACRC,MAAMnI,eAAe,kBAAkB;YAAEoI,QAAQ;QAA6B;QAC9E,mEAAmE;QACnEC,OAAO,EAAEjC,0BAAAA,kBAAmBkC;QAC5BC,cAAc;QACdC,cAAc;;0BAEd,MAACxH;gBAAMyH,SAAS;;kCACd,KAACvH;wBAAW0E,SAAQ;wBAAQgC,OAAM;kCAC/B,mEACC,oEACA,kEACA;;oBAEH7C,MAAM/B,GAAG,CAAC,CAACJ;4BAuB8CA;4BAA1BA;wBAtB9B,MAAMqF,SAASjG,2BAA2BY;wBAC1C,MAAM8F,SAAS/F,cAAcC,KAAKC;wBAClC,MAAM8F,WAAWD,OAAOE,MAAM,CAAC,CAACZ,SAAW,CAACC,OAAOY,QAAQ,CAACb;wBAC5D,qBACE,MAAChH;4BAEC8H,WAAU;4BACVL,SAAS;4BACTM,IAAI;gCAAEC,YAAY;4BAAS;;8CAE3B,KAAC/H;oCACCgI,MAAK;oCACL5B,SAASzE,IAAIuE,OAAO,KAAK;oCACzB+B,UAAU,CAACxF;oCACXyF,UAAUnC,aAAapE;oCACvBwG,WAAW;wCAAEC,OAAO;4CAAE,cAAc,CAAC,OAAO,EAAEzG,IAAI6B,IAAI,CAAC,UAAU,CAAC;wCAAC;oCAAE;;8CAEvE,MAACzD;oCAAM+H,IAAI;wCAAEO,MAAM;wCAAGC,UAAU;oCAAE;oCAAGd,SAAS;;sDAC5C,KAACvH;4CAAW0E,SAAQ;4CAAQ4D,MAAM;sDAC/B5G,IAAI6B,IAAI;;sDAEX,KAACvD;4CAAW0E,SAAQ;4CAAUgC,OAAM;4CAAiB4B,MAAM;sDACxD,CAAC,GAAG,EAAEzJ,gBAAe6C,eAAAA,IAAI6G,OAAO,qBAAX7G,aAAaqE,KAAK,EAAE,GAAG,EAAE,EAACrE,aAAAA,IAAI2D,KAAK,YAAT3D,aAAa,EAAE,EAC5DI,GAAG,CACF,CAAC0G;oDACChH;wDAAAA,qCAAAA,uBAAuB,CAACgH,KAAKC,IAAI,CAAuB,YAAxDjH,qCACAgH,KAAKC,IAAI;+CAEZrG,IAAI,CAAC,QAAQ;;sDAElB,KAACpC;4CAAW0E,SAAQ;4CAAUgC,OAAM;4CAAiB4B,MAAM;sDACxDrG,cAAcP,KAAKC;;wCAErBoF,OAAO5E,MAAM,iBACZ,KAACrC;4CAAM8H,WAAU;4CAAML,SAAS;4CAAKM,IAAI;gDAAEa,UAAU;gDAAQC,KAAK;4CAAI;sDACnE5B,OAAOjF,GAAG,CAAC,CAACgF,uBACX,KAACnH;oDAECoI,MAAK;oDACLrD,SAAQ;oDACRgC,OAAM;oDACNkC,OAAO,CAAC,UAAU,EAAErH,YAAYI,OAAOmF,SAAS;mDAC3CtE,UACD;oDAAEqG,UAAU,IAAM,KAAKhC,UAAUnF,KAAKoF,QAAQ;gDAAO,IACrD,CAAC,IAPAA;6CAWT;;;gCAELtE,wBACC;;sDACE,KAAC9C;4CAAOqI,MAAK;4CAAQe,SAAS,IAAM/E,SAAS5C,mBAAmBO;sDAC7D;;sDAEH,KAAChC;4CACCqI,MAAK;4CACLC,UAAU,CAACP,SAAStF,MAAM;4CAC1B4G,iBAAc;4CACdD,SAAS,CAAC/C,QACR1B,aAAa;oDAAE6C,QAAQnB,MAAMiD,aAAa;oDAAEtH;gDAAI;sDAGjD;;sDAEH,KAAChC;4CAAOqI,MAAK;4CAAQrB,OAAM;4CAAQoC,SAAS1C,aAAa1E;sDACtD;;;qCAGH;;2BAhECA,IAAIsE,GAAG;oBAmElB;oBACC/C,YAAYd,MAAM,KAAK,kBACtB,KAACnC;wBAAW0E,SAAQ;wBAAQgC,OAAM;kCAC/B;uCAGH,KAACtH;wBACCqE,MAAMA;wBACNE,UAAUA;wBACVsF,UAAUpF,MAAM1B,MAAM;wBACtB+G,OAAOjG,YAAYd,MAAM;wBACzBgH,cAAczF;wBACd0F,kBAAkBxF;;oBAGrB,SAACd,wBAAAA,KAAMX,MAAM,mBAAI,KAAKtB,oCACrB,KAACpB;wBAAM4J,UAAS;kCACb,CAAC,QAAQ,EAAExI,oBAAoB,8BAA8B,CAAC,GAC7D;yBAEF;oBACH2B,wBACC,KAAC9C;wBACCqI,MAAK;wBACLrB,OAAM;wBACNmB,IAAI;4BAAEyB,WAAW;wBAAa;wBAC9BR,SAASjE;kCAER;yBAED;;;0BAEN,KAACjF;gBACC2J,QAAQ,WAAEnF,6BAAAA,UAAW8C,MAAM,oBAAI;gBAC/BsC,MAAMC,QAAQrF;gBACdsF,SAAS,IAAMrF,aAAa;0BAE3BD,YACG3C,cAAc2C,UAAU1C,GAAG,EAAEC,OAC1B+F,MAAM,CACL,CAACZ,SACC,CAAChG,2BAA2BsD,UAAU1C,GAAG,EAAEiG,QAAQ,CAACb,SAEvDhF,GAAG,CAAC,CAACgF,uBACJ,KAACjH;wBAECiJ,SAAS,IAAM,KAAKjC,UAAUzC,UAAU1C,GAAG,EAAEoF,QAAQ;kCAEpD,CAAC,SAAS,EAAEvF,YAAYI,OAAOmF,SAAS;uBAHpCA,WAMX;;0BAEN,KAAC7F;gBACCU,OAAOA;gBACPmC,OAAOA;gBACP6F,SAAS,CAACC,SACR7F,SAAS,CAAC8F,WAAcA,WAAWD,OAAOC,YAAYA;gBAExD7E,QAAQhB;gBACRE,QAAQA;gBACR4F,QAAQ,IAAM,KAAKxE;gBACnBoE,SAAS,IAAM3F,SAAS;;;;AAIhC;AACA1B,mBAAmB0H,WAAW,GAAG;AAEjC,eAAe1H,mBAAkB"}
@@ -57,7 +57,8 @@ export interface AutomationTarget {
57
57
  /**
58
58
  * The editor of one SAVED automation — an action or a workflow — on the
59
59
  * Automation page (AGL-2919). A widget here reads the automation as it is
60
- * stored and changes nothing in the editor.
60
+ * stored and changes nothing in the editor; what it drafts is a new
61
+ * automation, which `openAction` opens.
61
62
  */
62
63
  export interface AutomationEditorZoneProps {
63
64
  hostId: string;
@@ -65,6 +66,13 @@ export interface AutomationEditorZoneProps {
65
66
  orgId: string | undefined;
66
67
  /** The automation the editor has open, as it is stored. */
67
68
  target: AutomationTarget;
69
+ /**
70
+ * Opens a listed action in the Actions editor in place of the one open —
71
+ * a changed copy a widget drafted, say — and answers `false` when the id
72
+ * names no action the list has read yet (AGL-3603). The Actions editor
73
+ * hands it; a workflow's editor does not.
74
+ */
75
+ openAction?: (actionId: string) => boolean;
68
76
  }
69
77
  /**
70
78
  * One FAILED run in an automation's run history (AGL-2919), drawn once per
@@ -81,5 +89,49 @@ export interface AutomationRunZoneProps {
81
89
  runId: string;
82
90
  }
83
91
  export declare const HOST_AUTOMATIONS_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<HostAutomationsZoneProps>;
92
+ /**
93
+ * The workspace's Org automations section, in its card's header (AGL-3603):
94
+ * another way to start an org automation, drawn for a member who may write
95
+ * them. `triggers` and `steps` are what an org automation may start on and
96
+ * hold — this plugin's own lists, handed over so a widget never restates
97
+ * them. `propose` opens an automation in the section's editor as a NEW one,
98
+ * unsaved and switched off, and answers `false` when the editor cannot take
99
+ * it. A widget here writes nothing: the editor's Save, through the section's
100
+ * route, is the write.
101
+ */
102
+ export interface OrgAutomationsZoneProps {
103
+ orgId: string;
104
+ triggers: readonly string[];
105
+ steps: readonly string[];
106
+ propose: (automation: OrgAutomationProposal) => boolean;
107
+ }
108
+ /** An automation a widget proposes to the Org automations editor. */
109
+ export interface OrgAutomationProposal {
110
+ name: string;
111
+ trigger: {
112
+ event: string;
113
+ conditions?: ReadonlyArray<{
114
+ field: string;
115
+ op: string;
116
+ value?: string;
117
+ }>;
118
+ combinator?: 'and' | 'or';
119
+ };
120
+ steps: ReadonlyArray<Record<string, unknown> & {
121
+ type: string;
122
+ }>;
123
+ }
124
+ export declare const ORG_AUTOMATIONS_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<OrgAutomationsZoneProps>;
84
125
  export declare const AUTOMATION_EDITOR_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<AutomationEditorZoneProps>;
85
126
  export declare const AUTOMATION_RUN_ZONE: import("@aglyn/aglyn/plugin-manager/plugin-zones").PluginZone<AutomationRunZoneProps>;
127
+ /**
128
+ * The query parameter that asks the Actions section to open one of its
129
+ * actions in the editor once its list has read it (AGL-3603) — how the
130
+ * Workflows section's `openAction` hands over an action a widget drafted
131
+ * there, since the action lives in the other section.
132
+ */
133
+ export declare const OPEN_ACTION_PARAM = "action";
134
+ /** The Actions section's address, opening `actionId` in its editor. */
135
+ export declare function openActionHref(actionsHref: string, actionId: string): string;
136
+ /** The action the address asks the Actions section to open, or `null`. */
137
+ export declare function requestedActionId(search: string): string | null;
@@ -16,7 +16,21 @@
16
16
  */ import { definePluginZone } from "@aglyn/aglyn/plugin-manager/plugin-zones";
17
17
  export const WORKFLOW_USAGE_ZONE = definePluginZone('workflowUsage');
18
18
  export const HOST_AUTOMATIONS_ZONE = definePluginZone('hostAutomations');
19
+ export const ORG_AUTOMATIONS_ZONE = definePluginZone('orgAutomations');
19
20
  export const AUTOMATION_EDITOR_ZONE = definePluginZone('automationEditor');
20
21
  export const AUTOMATION_RUN_ZONE = definePluginZone('automationRun');
22
+ /**
23
+ * The query parameter that asks the Actions section to open one of its
24
+ * actions in the editor once its list has read it (AGL-3603) — how the
25
+ * Workflows section's `openAction` hands over an action a widget drafted
26
+ * there, since the action lives in the other section.
27
+ */ export const OPEN_ACTION_PARAM = 'action';
28
+ /** The Actions section's address, opening `actionId` in its editor. */ export function openActionHref(actionsHref, actionId) {
29
+ return `${actionsHref}?${OPEN_ACTION_PARAM}=${encodeURIComponent(actionId)}`;
30
+ }
31
+ /** The action the address asks the Actions section to open, or `null`. */ export function requestedActionId(search) {
32
+ const id = new URLSearchParams(search).get(OPEN_ACTION_PARAM);
33
+ return id && /^[A-Za-z0-9_-]{1,128}$/.test(id) ? id : null;
34
+ }
21
35
 
22
36
  //# sourceMappingURL=workflow-zones.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflow-zones.ts"],"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 type { WhereUsedResult } from '@aglyn/aglyn/app-utils/where-used'\nimport { definePluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\n\n/**\n * Where a workflow is used, shown by whichever plugin draws references.\n *\n * The Automation page asks the platform's where-used scan what a workflow\n * computes and holds the answer. Laying that answer out — the rows, the deep\n * links into a published version, the flag on a legacy name token — is the\n * same dialog a variable and a function open, and it belongs to the plugin\n * that owns those. This page hosts the zone and hands it the answer.\n */\nexport interface WorkflowUsageZoneProps {\n hostId: string\n /** The scan this page already ran; `null` closes whatever is drawn. */\n usage: { name: string; result: WhereUsedResult } | null\n onClose: () => void\n}\n\nexport const WORKFLOW_USAGE_ZONE =\n definePluginZone<WorkflowUsageZoneProps>('workflowUsage')\n\n/**\n * The Automation page's Actions, beside Add action and Recipes (AGL-2919):\n * another way to start an automation. `openAction` opens a listed action in\n * the Actions editor, and answers `false` when the id names no action the\n * list has read yet — which is how a widget whose draft was just written\n * tells a list that has not caught up from one that opened it. A widget here\n * writes nothing through the page.\n */\nexport interface HostAutomationsZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n openAction: (actionId: string) => boolean\n}\n\n/** One automation on the Automation page: an action, or a workflow (AGL-2919). */\nexport interface AutomationTarget {\n type: 'action' | 'workflow'\n id: string\n name: string\n}\n\n/**\n * The editor of one SAVED automation — an action or a workflow — on the\n * Automation page (AGL-2919). A widget here reads the automation as it is\n * stored and changes nothing in the editor.\n */\nexport interface AutomationEditorZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n /** The automation the editor has open, as it is stored. */\n target: AutomationTarget\n}\n\n/**\n * One FAILED run in an automation's run history (AGL-2919), drawn once per\n * failed row. A widget here reads the run as it was recorded and changes\n * nothing.\n */\nexport interface AutomationRunZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n /** The automation the run belongs to. */\n target: AutomationTarget\n /** The run's entry in the site's activity log. */\n runId: string\n}\n\nexport const HOST_AUTOMATIONS_ZONE =\n definePluginZone<HostAutomationsZoneProps>('hostAutomations')\n\nexport const AUTOMATION_EDITOR_ZONE =\n definePluginZone<AutomationEditorZoneProps>('automationEditor')\n\nexport const AUTOMATION_RUN_ZONE =\n definePluginZone<AutomationRunZoneProps>('automationRun')\n"],"names":["definePluginZone","WORKFLOW_USAGE_ZONE","HOST_AUTOMATIONS_ZONE","AUTOMATION_EDITOR_ZONE","AUTOMATION_RUN_ZONE"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,gBAAgB,QAAQ,2CAA0C;AAkB3E,OAAO,MAAMC,sBACXD,iBAAyC,iBAAgB;AAoD3D,OAAO,MAAME,wBACXF,iBAA2C,mBAAkB;AAE/D,OAAO,MAAMG,yBACXH,iBAA4C,oBAAmB;AAEjE,OAAO,MAAMI,sBACXJ,iBAAyC,iBAAgB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflow-zones.ts"],"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 type { WhereUsedResult } from '@aglyn/aglyn/app-utils/where-used'\nimport { definePluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\n\n/**\n * Where a workflow is used, shown by whichever plugin draws references.\n *\n * The Automation page asks the platform's where-used scan what a workflow\n * computes and holds the answer. Laying that answer out — the rows, the deep\n * links into a published version, the flag on a legacy name token — is the\n * same dialog a variable and a function open, and it belongs to the plugin\n * that owns those. This page hosts the zone and hands it the answer.\n */\nexport interface WorkflowUsageZoneProps {\n hostId: string\n /** The scan this page already ran; `null` closes whatever is drawn. */\n usage: { name: string; result: WhereUsedResult } | null\n onClose: () => void\n}\n\nexport const WORKFLOW_USAGE_ZONE =\n definePluginZone<WorkflowUsageZoneProps>('workflowUsage')\n\n/**\n * The Automation page's Actions, beside Add action and Recipes (AGL-2919):\n * another way to start an automation. `openAction` opens a listed action in\n * the Actions editor, and answers `false` when the id names no action the\n * list has read yet — which is how a widget whose draft was just written\n * tells a list that has not caught up from one that opened it. A widget here\n * writes nothing through the page.\n */\nexport interface HostAutomationsZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n openAction: (actionId: string) => boolean\n}\n\n/** One automation on the Automation page: an action, or a workflow (AGL-2919). */\nexport interface AutomationTarget {\n type: 'action' | 'workflow'\n id: string\n name: string\n}\n\n/**\n * The editor of one SAVED automation — an action or a workflow — on the\n * Automation page (AGL-2919). A widget here reads the automation as it is\n * stored and changes nothing in the editor; what it drafts is a new\n * automation, which `openAction` opens.\n */\nexport interface AutomationEditorZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n /** The automation the editor has open, as it is stored. */\n target: AutomationTarget\n /**\n * Opens a listed action in the Actions editor in place of the one open —\n * a changed copy a widget drafted, say — and answers `false` when the id\n * names no action the list has read yet (AGL-3603). The Actions editor\n * hands it; a workflow's editor does not.\n */\n openAction?: (actionId: string) => boolean\n}\n\n/**\n * One FAILED run in an automation's run history (AGL-2919), drawn once per\n * failed row. A widget here reads the run as it was recorded and changes\n * nothing.\n */\nexport interface AutomationRunZoneProps {\n hostId: string\n /** The org the page names; `undefined` while it resolves. */\n orgId: string | undefined\n /** The automation the run belongs to. */\n target: AutomationTarget\n /** The run's entry in the site's activity log. */\n runId: string\n}\n\nexport const HOST_AUTOMATIONS_ZONE =\n definePluginZone<HostAutomationsZoneProps>('hostAutomations')\n\n/**\n * The workspace's Org automations section, in its card's header (AGL-3603):\n * another way to start an org automation, drawn for a member who may write\n * them. `triggers` and `steps` are what an org automation may start on and\n * hold — this plugin's own lists, handed over so a widget never restates\n * them. `propose` opens an automation in the section's editor as a NEW one,\n * unsaved and switched off, and answers `false` when the editor cannot take\n * it. A widget here writes nothing: the editor's Save, through the section's\n * route, is the write.\n */\nexport interface OrgAutomationsZoneProps {\n orgId: string\n triggers: readonly string[]\n steps: readonly string[]\n propose: (automation: OrgAutomationProposal) => boolean\n}\n\n/** An automation a widget proposes to the Org automations editor. */\nexport interface OrgAutomationProposal {\n name: string\n trigger: {\n event: string\n conditions?: ReadonlyArray<{ field: string; op: string; value?: string }>\n combinator?: 'and' | 'or'\n }\n steps: ReadonlyArray<Record<string, unknown> & { type: string }>\n}\n\nexport const ORG_AUTOMATIONS_ZONE =\n definePluginZone<OrgAutomationsZoneProps>('orgAutomations')\n\nexport const AUTOMATION_EDITOR_ZONE =\n definePluginZone<AutomationEditorZoneProps>('automationEditor')\n\nexport const AUTOMATION_RUN_ZONE =\n definePluginZone<AutomationRunZoneProps>('automationRun')\n\n/**\n * The query parameter that asks the Actions section to open one of its\n * actions in the editor once its list has read it (AGL-3603) — how the\n * Workflows section's `openAction` hands over an action a widget drafted\n * there, since the action lives in the other section.\n */\nexport const OPEN_ACTION_PARAM = 'action'\n\n/** The Actions section's address, opening `actionId` in its editor. */\nexport function openActionHref(actionsHref: string, actionId: string): string {\n return `${actionsHref}?${OPEN_ACTION_PARAM}=${encodeURIComponent(actionId)}`\n}\n\n/** The action the address asks the Actions section to open, or `null`. */\nexport function requestedActionId(search: string): string | null {\n const id = new URLSearchParams(search).get(OPEN_ACTION_PARAM)\n return id && /^[A-Za-z0-9_-]{1,128}$/.test(id) ? id : null\n}\n"],"names":["definePluginZone","WORKFLOW_USAGE_ZONE","HOST_AUTOMATIONS_ZONE","ORG_AUTOMATIONS_ZONE","AUTOMATION_EDITOR_ZONE","AUTOMATION_RUN_ZONE","OPEN_ACTION_PARAM","openActionHref","actionsHref","actionId","encodeURIComponent","requestedActionId","search","id","URLSearchParams","get","test"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAGD,SAASA,gBAAgB,QAAQ,2CAA0C;AAkB3E,OAAO,MAAMC,sBACXD,iBAAyC,iBAAgB;AA4D3D,OAAO,MAAME,wBACXF,iBAA2C,mBAAkB;AA8B/D,OAAO,MAAMG,uBACXH,iBAA0C,kBAAiB;AAE7D,OAAO,MAAMI,yBACXJ,iBAA4C,oBAAmB;AAEjE,OAAO,MAAMK,sBACXL,iBAAyC,iBAAgB;AAE3D;;;;;CAKC,GACD,OAAO,MAAMM,oBAAoB,SAAQ;AAEzC,qEAAqE,GACrE,OAAO,SAASC,eAAeC,WAAmB,EAAEC,QAAgB;IAClE,OAAO,GAAGD,YAAY,CAAC,EAAEF,kBAAkB,CAAC,EAAEI,mBAAmBD,WAAW;AAC9E;AAEA,wEAAwE,GACxE,OAAO,SAASE,kBAAkBC,MAAc;IAC9C,MAAMC,KAAK,IAAIC,gBAAgBF,QAAQG,GAAG,CAACT;IAC3C,OAAOO,MAAM,yBAAyBG,IAAI,CAACH,MAAMA,KAAK;AACxD"}
@@ -34,7 +34,7 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
34
34
  * would CONSTRUCT all three every render, and each card opens its Firestore
35
35
  * listens on mount — which is the entire cost this page exists to stop paying.
36
36
  * Only the returned branch is ever built.
37
- */ function sectionBody(section, hostId, org) {
37
+ */ function sectionBody(section, hostId, org, /** The Actions section's address, where a drafted action is opened. */ actionsHref) {
38
38
  switch(section){
39
39
  case 'workflows':
40
40
  return(/*
@@ -52,7 +52,8 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
52
52
  }),
53
53
  /*#__PURE__*/ _jsx(HostWorkflowsCard, {
54
54
  hostId: hostId,
55
- org: org
55
+ org: org,
56
+ actionsHref: actionsHref
56
57
  })
57
58
  ]
58
59
  }));
@@ -148,6 +149,7 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
148
149
  * site and an ACTION belongs to the site too; an interaction belongs to the
149
150
  * document that holds it and is edited on the element, not here.
150
151
  */ export function WorkflowsConsolePage(props) {
152
+ var _sections_find;
151
153
  const { hostId, orgMount, org, permissions, section, sections, basePath } = props;
152
154
  /*
153
155
  * Mounted with no site: the organization's hub (AGL-3302). Memoised so the
@@ -176,7 +178,7 @@ import { workflowsOrgMount } from "./workflows-org-mount.js";
176
178
  }
177
179
  return /*#__PURE__*/ _jsx(HubSections, {
178
180
  sections: sections,
179
- children: sectionBody(section, hostId, org)
181
+ children: sectionBody(section, hostId, org, (_sections_find = sections.find((one)=>one.id === 'actions')) == null ? void 0 : _sections_find.href)
180
182
  });
181
183
  }
182
184
  WorkflowsConsolePage.displayName = 'WorkflowsConsolePage';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflows-console-page.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 type { AglynOrgBilling, ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport { Stack } from '@mui/material'\nimport { useMemo, type ReactNode } from 'react'\nimport HostActionsCard from './host-actions-card.component'\nimport HostWebhooksCard from './host-webhooks-card.component'\nimport HostWorkflowsCard from './host-workflows-card.component'\nimport OrgAutomationsCard from './org-automations-card.component'\nimport OrgSiteAutomationList from './org-site-automation-list.component'\nimport RunQuotaLine from './run-quota-line.component'\nimport SiteOrgAutomationsPanel from './site-org-automations-panel.component'\nimport type { WorkflowsConsoleSectionId } from './workflows-console-sections'\nimport {\n workflowsOrgMount,\n type WorkflowsOrgMount,\n} from './workflows-org-mount'\n\n/**\n * The body of one workflows section, built only when that section is the one\n * being read (AGL-2501).\n *\n * A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`\n * would CONSTRUCT all three every render, and each card opens its Firestore\n * listens on mount — which is the entire cost this page exists to stop paying.\n * Only the returned branch is ever built.\n */\nfunction sectionBody(\n section: WorkflowsConsoleSectionId,\n hostId: string,\n org: Partial<AglynOrgBilling> | undefined,\n): ReactNode {\n switch (section) {\n case 'workflows':\n return (\n /*\n * `N runs this month · M included` (AGL-2171), which the run-history\n * mockup puts opposite the heading. The quota silently stops\n * automations running once it is reached, and the only place it was\n * reported was the Billing page.\n */\n <Stack spacing={1}>\n <RunQuotaLine hostId={hostId} org={org} counter=\"workflowRuns\" />\n <HostWorkflowsCard hostId={hostId} org={org} />\n </Stack>\n )\n case 'actions':\n return (\n <Stack spacing={1}>\n {/* `actionRunsPerMonth` had NO customer-facing surface at all\n before this — staff panel and usage-alerts only. */}\n <RunQuotaLine hostId={hostId} org={org} counter=\"actionRuns\" />\n <HostActionsCard hostId={hostId} org={org} />\n {/* What the organization runs on this site beside the site's own\n actions, and the site's pause for each (AGL-3302). */}\n <SiteOrgAutomationsPanel hostId={hostId} />\n </Stack>\n )\n case 'webhooks':\n return <HostWebhooksCard hostId={hostId} org={org} />\n default:\n return null\n }\n}\n\n/**\n * The body of one ORGANIZATION-level section (AGL-3302): the org automations,\n * and every site's own workflows, actions and webhooks listed with the site\n * each belongs to.\n *\n * The run allowance line heads the workflows and actions lists here too: the\n * allowance is the workspace's (AGL-3472), every site's runs spend it, and\n * this is the page that lists them all.\n */\nfunction orgSectionBody(\n section: WorkflowsConsoleSectionId,\n mount: WorkflowsOrgMount,\n org: Partial<AglynOrgBilling> | undefined,\n canEdit: boolean,\n): ReactNode {\n switch (section) {\n case 'automations':\n return <OrgAutomationsCard mount={mount} org={org} canEdit={canEdit} />\n case 'workflows':\n case 'actions':\n return (\n <Stack spacing={1}>\n <RunQuotaLine\n hostId={null}\n orgId={mount.orgId}\n org={org}\n counter={section === 'workflows' ? 'workflowRuns' : 'actionRuns'}\n />\n <OrgSiteAutomationList mount={mount} kind={section} canRead />\n </Stack>\n )\n case 'webhooks':\n // A webhook holds its site's secret: the rules admit a site's admins\n // and editors, which every org-wide member but a viewer is.\n return (\n <OrgSiteAutomationList mount={mount} kind=\"webhooks\" canRead={canEdit} />\n )\n default:\n return null\n }\n}\n\n/**\n * Automation page (AGL-101/148/149 → AGL-395): the automation surface —\n * workflow builder, event-triggered actions, and webhooks — owned by the\n * workflows plugin and rendered by the shell's generic plugin route. Each card\n * runs its own entitlement check (workflows / actions / webhooks are distinct\n * plan flags), so the shell's resolved `org` doc flows into all three.\n *\n * Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so\n * this is not a read saving — `workflows-console-read-cost.spec.tsx` was\n * written BEFORE the conversion precisely to hold that line, and reports the\n * same counts after. What routing adds is that the URL names the section: it is\n * linkable, the back button walks sections, the breadcrumb says where you are,\n * and \"mount only what is open\" is structural rather than a `lazy` flag.\n *\n * The section ids are unchanged by the Automation rename: they are the\n * addresses people bookmark and the docs link to. A workflow belongs to the\n * site and an ACTION belongs to the site too; an interaction belongs to the\n * document that holds it and is edited on the element, not here.\n */\nexport function WorkflowsConsolePage(props: ConsolePluginPageProps) {\n const { hostId, orgMount, org, permissions, section, sections, basePath } =\n props\n /*\n * Mounted with no site: the organization's hub (AGL-3302). Memoised so the\n * cards below, which key their listeners on the mount's org, see one object.\n */\n const mount = useMemo(\n () => (hostId == null ? workflowsOrgMount(orgMount, basePath) : null),\n [hostId, orgMount, basePath],\n )\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare hub URL\n * to the landing section and holds a spinner while it does, so this state is\n * transient — and rendering a default section here instead would pay for its\n * listens on a URL that is already being replaced.\n */\n if (!section || !sections?.length || !basePath) return null\n\n if (hostId == null) {\n // No site and no org to stand in for it: nothing this page can scope.\n if (!mount) return null\n return (\n <HubSections sections={sections}>\n {orgSectionBody(\n section as WorkflowsConsoleSectionId,\n mount,\n org,\n // An org viewer's `editHosts` is false; every other org role's is\n // true. The server routes decide — this keeps a viewer from being\n // offered controls that are about to refuse them.\n permissions?.editHosts !== false,\n )}\n </HubSections>\n )\n }\n\n return (\n <HubSections sections={sections}>\n {sectionBody(section as WorkflowsConsoleSectionId, hostId, org)}\n </HubSections>\n )\n}\nWorkflowsConsolePage.displayName = 'WorkflowsConsolePage'\n\nexport default WorkflowsConsolePage\n"],"names":["HubSections","Stack","useMemo","HostActionsCard","HostWebhooksCard","HostWorkflowsCard","OrgAutomationsCard","OrgSiteAutomationList","RunQuotaLine","SiteOrgAutomationsPanel","workflowsOrgMount","sectionBody","section","hostId","org","spacing","counter","orgSectionBody","mount","canEdit","orgId","kind","canRead","WorkflowsConsolePage","props","orgMount","permissions","sections","basePath","length","editHosts","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD;;AAGA,SAASA,WAAW,QAAQ,wBAAuB;AACnD,SAASC,KAAK,QAAQ,gBAAe;AACrC,SAASC,OAAO,QAAwB,QAAO;AAC/C,OAAOC,qBAAqB,mCAA+B;AAC3D,OAAOC,sBAAsB,oCAAgC;AAC7D,OAAOC,uBAAuB,qCAAiC;AAC/D,OAAOC,wBAAwB,sCAAkC;AACjE,OAAOC,2BAA2B,0CAAsC;AACxE,OAAOC,kBAAkB,gCAA4B;AACrD,OAAOC,6BAA6B,4CAAwC;AAE5E,SACEC,iBAAiB,QAEZ,2BAAuB;AAE9B;;;;;;;;CAQC,GACD,SAASC,YACPC,OAAkC,EAClCC,MAAc,EACdC,GAAyC;IAEzC,OAAQF;QACN,KAAK;YACH,OACE;;;;;SAKC,iBACD,MAACX;gBAAMc,SAAS;;kCACd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACX;wBAAkBQ,QAAQA;wBAAQC,KAAKA;;;;QAG9C,KAAK;YACH,qBACE,MAACb;gBAAMc,SAAS;;kCAGd,KAACP;wBAAaK,QAAQA;wBAAQC,KAAKA;wBAAKE,SAAQ;;kCAChD,KAACb;wBAAgBU,QAAQA;wBAAQC,KAAKA;;kCAGtC,KAACL;wBAAwBI,QAAQA;;;;QAGvC,KAAK;YACH,qBAAO,KAACT;gBAAiBS,QAAQA;gBAAQC,KAAKA;;QAChD;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;CAQC,GACD,SAASG,eACPL,OAAkC,EAClCM,KAAwB,EACxBJ,GAAyC,EACzCK,OAAgB;IAEhB,OAAQP;QACN,KAAK;YACH,qBAAO,KAACN;gBAAmBY,OAAOA;gBAAOJ,KAAKA;gBAAKK,SAASA;;QAC9D,KAAK;QACL,KAAK;YACH,qBACE,MAAClB;gBAAMc,SAAS;;kCACd,KAACP;wBACCK,QAAQ;wBACRO,OAAOF,MAAME,KAAK;wBAClBN,KAAKA;wBACLE,SAASJ,YAAY,cAAc,iBAAiB;;kCAEtD,KAACL;wBAAsBW,OAAOA;wBAAOG,MAAMT;wBAASU,OAAO;;;;QAGjE,KAAK;YACH,qEAAqE;YACrE,4DAA4D;YAC5D,qBACE,KAACf;gBAAsBW,OAAOA;gBAAOG,MAAK;gBAAWC,SAASH;;QAElE;YACE,OAAO;IACX;AACF;AAEA;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASI,qBAAqBC,KAA6B;IAChE,MAAM,EAAEX,MAAM,EAAEY,QAAQ,EAAEX,GAAG,EAAEY,WAAW,EAAEd,OAAO,EAAEe,QAAQ,EAAEC,QAAQ,EAAE,GACvEJ;IACF;;;GAGC,GACD,MAAMN,QAAQhB,QACZ,IAAOW,UAAU,OAAOH,kBAAkBe,UAAUG,YAAY,MAChE;QAACf;QAAQY;QAAUG;KAAS;IAG9B;;;;;GAKC,GACD,IAAI,CAAChB,WAAW,EAACe,4BAAAA,SAAUE,MAAM,KAAI,CAACD,UAAU,OAAO;IAEvD,IAAIf,UAAU,MAAM;QAClB,sEAAsE;QACtE,IAAI,CAACK,OAAO,OAAO;QACnB,qBACE,KAAClB;YAAY2B,UAAUA;sBACpBV,eACCL,SACAM,OACAJ,KACA,kEAAkE;YAClE,kEAAkE;YAClE,kDAAkD;YAClDY,CAAAA,+BAAAA,YAAaI,SAAS,MAAK;;IAInC;IAEA,qBACE,KAAC9B;QAAY2B,UAAUA;kBACpBhB,YAAYC,SAAsCC,QAAQC;;AAGjE;AACAS,qBAAqBQ,WAAW,GAAG;AAEnC,eAAeR,qBAAoB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/workflows-console-page.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 type { AglynOrgBilling, ConsolePluginPageProps } from '@aglyn/aglyn'\nimport { HubSections } from '@aglyn/shared-ui-next'\nimport { Stack } from '@mui/material'\nimport { useMemo, type ReactNode } from 'react'\nimport HostActionsCard from './host-actions-card.component'\nimport HostWebhooksCard from './host-webhooks-card.component'\nimport HostWorkflowsCard from './host-workflows-card.component'\nimport OrgAutomationsCard from './org-automations-card.component'\nimport OrgSiteAutomationList from './org-site-automation-list.component'\nimport RunQuotaLine from './run-quota-line.component'\nimport SiteOrgAutomationsPanel from './site-org-automations-panel.component'\nimport type { WorkflowsConsoleSectionId } from './workflows-console-sections'\nimport {\n workflowsOrgMount,\n type WorkflowsOrgMount,\n} from './workflows-org-mount'\n\n/**\n * The body of one workflows section, built only when that section is the one\n * being read (AGL-2501).\n *\n * A function rather than a map of nodes on purpose: a `Record<id, ReactNode>`\n * would CONSTRUCT all three every render, and each card opens its Firestore\n * listens on mount — which is the entire cost this page exists to stop paying.\n * Only the returned branch is ever built.\n */\nfunction sectionBody(\n section: WorkflowsConsoleSectionId,\n hostId: string,\n org: Partial<AglynOrgBilling> | undefined,\n /** The Actions section's address, where a drafted action is opened. */\n actionsHref?: string,\n): ReactNode {\n switch (section) {\n case 'workflows':\n return (\n /*\n * `N runs this month · M included` (AGL-2171), which the run-history\n * mockup puts opposite the heading. The quota silently stops\n * automations running once it is reached, and the only place it was\n * reported was the Billing page.\n */\n <Stack spacing={1}>\n <RunQuotaLine hostId={hostId} org={org} counter=\"workflowRuns\" />\n <HostWorkflowsCard\n hostId={hostId}\n org={org}\n actionsHref={actionsHref}\n />\n </Stack>\n )\n case 'actions':\n return (\n <Stack spacing={1}>\n {/* `actionRunsPerMonth` had NO customer-facing surface at all\n before this — staff panel and usage-alerts only. */}\n <RunQuotaLine hostId={hostId} org={org} counter=\"actionRuns\" />\n <HostActionsCard hostId={hostId} org={org} />\n {/* What the organization runs on this site beside the site's own\n actions, and the site's pause for each (AGL-3302). */}\n <SiteOrgAutomationsPanel hostId={hostId} />\n </Stack>\n )\n case 'webhooks':\n return <HostWebhooksCard hostId={hostId} org={org} />\n default:\n return null\n }\n}\n\n/**\n * The body of one ORGANIZATION-level section (AGL-3302): the org automations,\n * and every site's own workflows, actions and webhooks listed with the site\n * each belongs to.\n *\n * The run allowance line heads the workflows and actions lists here too: the\n * allowance is the workspace's (AGL-3472), every site's runs spend it, and\n * this is the page that lists them all.\n */\nfunction orgSectionBody(\n section: WorkflowsConsoleSectionId,\n mount: WorkflowsOrgMount,\n org: Partial<AglynOrgBilling> | undefined,\n canEdit: boolean,\n): ReactNode {\n switch (section) {\n case 'automations':\n return <OrgAutomationsCard mount={mount} org={org} canEdit={canEdit} />\n case 'workflows':\n case 'actions':\n return (\n <Stack spacing={1}>\n <RunQuotaLine\n hostId={null}\n orgId={mount.orgId}\n org={org}\n counter={section === 'workflows' ? 'workflowRuns' : 'actionRuns'}\n />\n <OrgSiteAutomationList mount={mount} kind={section} canRead />\n </Stack>\n )\n case 'webhooks':\n // A webhook holds its site's secret: the rules admit a site's admins\n // and editors, which every org-wide member but a viewer is.\n return (\n <OrgSiteAutomationList mount={mount} kind=\"webhooks\" canRead={canEdit} />\n )\n default:\n return null\n }\n}\n\n/**\n * Automation page (AGL-101/148/149 → AGL-395): the automation surface —\n * workflow builder, event-triggered actions, and webhooks — owned by the\n * workflows plugin and rendered by the shell's generic plugin route. Each card\n * runs its own entitlement check (workflows / actions / webhooks are distinct\n * plan flags), so the shell's resolved `org` doc flows into all three.\n *\n * Sections are ROUTES (AGL-2501). `HubTabs lazy` already mounted one panel, so\n * this is not a read saving — `workflows-console-read-cost.spec.tsx` was\n * written BEFORE the conversion precisely to hold that line, and reports the\n * same counts after. What routing adds is that the URL names the section: it is\n * linkable, the back button walks sections, the breadcrumb says where you are,\n * and \"mount only what is open\" is structural rather than a `lazy` flag.\n *\n * The section ids are unchanged by the Automation rename: they are the\n * addresses people bookmark and the docs link to. A workflow belongs to the\n * site and an ACTION belongs to the site too; an interaction belongs to the\n * document that holds it and is edited on the element, not here.\n */\nexport function WorkflowsConsolePage(props: ConsolePluginPageProps) {\n const { hostId, orgMount, org, permissions, section, sections, basePath } =\n props\n /*\n * Mounted with no site: the organization's hub (AGL-3302). Memoised so the\n * cards below, which key their listeners on the mount's org, see one object.\n */\n const mount = useMemo(\n () => (hostId == null ? workflowsOrgMount(orgMount, basePath) : null),\n [hostId, orgMount, basePath],\n )\n\n /*\n * Nothing until the URL names a section. The shell redirects a bare hub URL\n * to the landing section and holds a spinner while it does, so this state is\n * transient — and rendering a default section here instead would pay for its\n * listens on a URL that is already being replaced.\n */\n if (!section || !sections?.length || !basePath) return null\n\n if (hostId == null) {\n // No site and no org to stand in for it: nothing this page can scope.\n if (!mount) return null\n return (\n <HubSections sections={sections}>\n {orgSectionBody(\n section as WorkflowsConsoleSectionId,\n mount,\n org,\n // An org viewer's `editHosts` is false; every other org role's is\n // true. The server routes decide — this keeps a viewer from being\n // offered controls that are about to refuse them.\n permissions?.editHosts !== false,\n )}\n </HubSections>\n )\n }\n\n return (\n <HubSections sections={sections}>\n {sectionBody(\n section as WorkflowsConsoleSectionId,\n hostId,\n org,\n sections.find((one) => one.id === 'actions')?.href,\n )}\n </HubSections>\n )\n}\nWorkflowsConsolePage.displayName = 'WorkflowsConsolePage'\n\nexport default WorkflowsConsolePage\n"],"names":["HubSections","Stack","useMemo","HostActionsCard","HostWebhooksCard","HostWorkflowsCard","OrgAutomationsCard","OrgSiteAutomationList","RunQuotaLine","SiteOrgAutomationsPanel","workflowsOrgMount","sectionBody","section","hostId","org","actionsHref","spacing","counter","orgSectionBody","mount","canEdit","orgId","kind","canRead","WorkflowsConsolePage","props","sections","orgMount","permissions","basePath","length","editHosts","find","one","id","href","displayName"],"mappings":"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"}
package/src/lib/plugin.js CHANGED
@@ -21,7 +21,7 @@ import { StaffOrgAutomationsCard, StaffSiteAutomationsCard } from "./components/
21
21
  import { WORKFLOWS_CONSOLE_SECTIONS, WORKFLOWS_ORG_CONSOLE_SECTIONS } from "./components/workflows-console-sections.js";
22
22
  import { registerPluginZone } from "@aglyn/aglyn/plugin-manager/plugin-zones";
23
23
  import { registerPluginTransferResourceUi } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
24
- import { AUTOMATION_EDITOR_ZONE, AUTOMATION_RUN_ZONE, HOST_AUTOMATIONS_ZONE, WORKFLOW_USAGE_ZONE } from "./components/workflow-zones.js";
24
+ import { AUTOMATION_EDITOR_ZONE, AUTOMATION_RUN_ZONE, HOST_AUTOMATIONS_ZONE, ORG_AUTOMATIONS_ZONE, WORKFLOW_USAGE_ZONE } from "./components/workflow-zones.js";
25
25
  import { BUNDLE_ID } from "./constants/bundle-common.js";
26
26
  import { registerWorkflowsRecordLists } from "./model/workflows-record-lists.js";
27
27
  import { registerWorkflowsRecordRoutes } from "./model/workflows-record-routes.js";
@@ -68,7 +68,16 @@ import { WORKFLOWS_SEARCH_SOURCES } from "./model/workflows-search-sources.js";
68
68
  label: 'Automation page actions',
69
69
  surface: 'console',
70
70
  layout: 'bare',
71
- description: 'On the Automation page, beside Add action and Recipes: another way to start an automation. `openAction` opens a listed action in the Actions editor; a widget here writes nothing through the page.'
71
+ description: 'On the Automation page, beside Add action and Recipes, and in the Workflows card’s header or empty state: another way to start an automation. `openAction` opens a listed action in the Actions editor; a widget here writes nothing through the page.'
72
+ }, {
73
+ pluginId: BUNDLE_ID
74
+ });
75
+ registerPluginZone({
76
+ zone: ORG_AUTOMATIONS_ZONE,
77
+ label: 'Org automations actions',
78
+ surface: 'console',
79
+ layout: 'bare',
80
+ description: 'In the header of the workspace’s Org automations card, for a member who may write them: another way to start an org automation. `triggers` and `steps` are what one may start on and hold; `propose` opens an automation in the section’s editor, unsaved and switched off. A widget here writes nothing.'
72
81
  }, {
73
82
  pluginId: BUNDLE_ID
74
83
  });
@@ -77,7 +86,7 @@ import { WORKFLOWS_SEARCH_SOURCES } from "./model/workflows-search-sources.js";
77
86
  label: 'An automation’s editor',
78
87
  surface: 'console',
79
88
  layout: 'bare',
80
- description: 'Inside the editor of one saved automation, an action or a workflow. A widget here reads the automation as it is stored and changes nothing in the editor.'
89
+ description: 'Inside the editor of one saved automation, an action or a workflow. A widget here reads the automation as it is stored and changes nothing in the editor; in an action’s editor, `openAction` opens another listed action, such as a copy the widget drafted, in its place.'
81
90
  }, {
82
91
  pluginId: BUNDLE_ID
83
92
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/plugin.ts"],"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 * as Aglyn from '@aglyn/aglyn'\nimport { mdiSitemap } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport { HostActivityCard } from './components/host-activity-card.component'\nimport {\n StaffOrgAutomationsCard,\n StaffSiteAutomationsCard,\n} from './components/staff-automations-card.component'\nimport {\n WORKFLOWS_CONSOLE_SECTIONS,\n WORKFLOWS_ORG_CONSOLE_SECTIONS,\n} from './components/workflows-console-sections'\nimport { registerPluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { registerPluginTransferResourceUi } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport {\n AUTOMATION_EDITOR_ZONE,\n AUTOMATION_RUN_ZONE,\n HOST_AUTOMATIONS_ZONE,\n WORKFLOW_USAGE_ZONE,\n} from './components/workflow-zones'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { registerWorkflowsRecordLists } from './model/workflows-record-lists'\nimport { registerWorkflowsRecordRoutes } from './model/workflows-record-routes'\nimport { WORKFLOWS_SEARCH_SOURCES } from './model/workflows-search-sources'\n\n/** Code-split: the Automation console page only loads when opened. */\nconst WorkflowsConsolePage = lazy(\n () => import('./components/workflows-console-page'),\n)\n\n/**\n * Workflows feature plugin (AGL-395). Console-only — workflows, actions,\n * and webhooks run server-side (event pipelines, `/api/hooks`), not through\n * a canvas element, so there is no UI bundle. The console half declares the\n * Automation nav + page through the ConsoleExtension registry, gated by the\n * `workflows` entitlement (the Actions/Webhooks tabs run their own per-plan\n * checks off the passed `org`). Where a workflow is used is asked of the\n * platform's scan and drawn in a zone this plugin hosts.\n *\n * `pluginId`, `featureFlag` and `navTabId` all still read `workflows`: they\n * are stored — an org's enabled-plugin ids, the plan's entitlement flags, the\n * Remote Config release flag — and a section's name is not a reason to orphan\n * them. Only what a reader sees carries the new name.\n */\nexport function registerWorkflowsConsole(): void {\n // How the Import & export hub names org automations in a workspace package (AGL-3535).\n registerPluginTransferResourceUi(\n 'workflows.org-automations',\n { label: 'Org automations', icon: { path: mdiSitemap.path } },\n { pluginId: BUNDLE_ID },\n )\n registerWorkflowsRecordRoutes()\n registerWorkflowsRecordLists()\n registerPluginZone(\n {\n zone: WORKFLOW_USAGE_ZONE,\n label: 'Where a workflow is used',\n surface: 'console',\n description:\n 'On the Automation page, after a reader asks where a workflow is used. A widget here lays out the scan the page already ran; it is handed the site, the answer and a way to close, and it writes nothing.',\n },\n // Named, because a spec calls this registrar without the loader.\n { pluginId: BUNDLE_ID },\n )\n // The zones the Automation page draws in its own layouts (AGL-2919): one\n // item in the Actions row, inside an open editor, on a failed run's row.\n registerPluginZone(\n {\n zone: HOST_AUTOMATIONS_ZONE,\n label: 'Automation page actions',\n surface: 'console',\n layout: 'bare',\n description:\n 'On the Automation page, beside Add action and Recipes: another way to start an automation. `openAction` opens a listed action in the Actions editor; a widget here writes nothing through the page.',\n },\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: AUTOMATION_EDITOR_ZONE,\n label: 'An automation’s editor',\n surface: 'console',\n layout: 'bare',\n description:\n 'Inside the editor of one saved automation, an action or a workflow. A widget here reads the automation as it is stored and changes nothing in the editor.',\n },\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: AUTOMATION_RUN_ZONE,\n label: 'A failed automation run',\n surface: 'console',\n layout: 'bare',\n description:\n 'On each failed run in an automation’s run history. A widget here reads the run as it was recorded and changes nothing.',\n },\n { pluginId: BUNDLE_ID },\n )\n Aglyn.registerConsoleExtension({\n // Host activity feed (AGL-419): rendered by the shell's\n // 'hostActivity' widget slot (dashboard + editor view page).\n widgets: [\n {\n slot: 'hostActivity',\n widgetId: 'workflows-host-activity',\n title: 'Recent Activity',\n Component: HostActivityCard,\n },\n // The staff site and organization pages' automations (AGL-3379), read\n // only: staff see what runs on a site without joining it.\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.staffSite,\n widgetId: 'workflows-staff-site-automations',\n title: 'Automations',\n Component: StaffSiteAutomationsCard,\n },\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.adminOrgDetail,\n widgetId: 'workflows-staff-org-automations',\n title: 'Organization automations',\n Component: StaffOrgAutomationsCard,\n },\n ],\n pluginId: BUNDLE_ID,\n displayName: 'Automation',\n featureFlag: 'workflows',\n searchSources: WORKFLOWS_SEARCH_SOURCES,\n navItems: [\n {\n label: 'Automation',\n // Workflows, Actions and Webhooks are the tabs INSIDE this section, so\n // the section is named for what they have in common. `/workflows` —\n // the address before the section had its own name — is redirected here\n // by this plugin's `consoleRedirects`.\n href: '/automation',\n // Sections as ROUTES (AGL-2501): each is a real URL the shell\n // resolves and gates, so the page mounts the one being read.\n sections: WORKFLOWS_CONSOLE_SECTIONS,\n // Reuse the existing release-flag nav-tab so staff-preview gating is\n // unchanged now that the tab comes from the plugin (AGL-395).\n navTabId: 'nav-tab-workflows',\n icon: { path: mdiSitemap.path },\n header: {\n title: 'Automation',\n icon: { path: mdiSitemap.path },\n docsTopic: 'workflows',\n },\n Component: WorkflowsConsolePage,\n },\n ],\n /*\n * The ORGANIZATION's Automation hub (AGL-3302), at `/[orgSlug]/automation`:\n * the org automations placed across the organization's sites, and every\n * site's own workflows, actions and webhooks side by side. The same page\n * component, mounted with no site; the site tab's release flag gates both\n * levels, so one flag rolls the surface out at once.\n */\n orgNavItems: [\n {\n label: 'Automation',\n href: '/automation',\n sections: WORKFLOWS_ORG_CONSOLE_SECTIONS,\n navTabId: 'nav-tab-workflows',\n icon: { path: mdiSitemap.path },\n header: {\n title: 'Automation',\n icon: { path: mdiSitemap.path },\n docsTopic: 'workflows',\n },\n Component: WorkflowsConsolePage,\n },\n ],\n })\n}\n\n// Activity feed consumed directly by the app (dashboard + screen view).\nexport { default as HostActivityCard } from './components/host-activity-card.component'\nexport type { HostActivityCardProps } from './components/host-activity-card.component'\n"],"names":["Aglyn","mdiSitemap","lazy","HostActivityCard","StaffOrgAutomationsCard","StaffSiteAutomationsCard","WORKFLOWS_CONSOLE_SECTIONS","WORKFLOWS_ORG_CONSOLE_SECTIONS","registerPluginZone","registerPluginTransferResourceUi","AUTOMATION_EDITOR_ZONE","AUTOMATION_RUN_ZONE","HOST_AUTOMATIONS_ZONE","WORKFLOW_USAGE_ZONE","BUNDLE_ID","registerWorkflowsRecordLists","registerWorkflowsRecordRoutes","WORKFLOWS_SEARCH_SOURCES","WorkflowsConsolePage","registerWorkflowsConsole","label","icon","path","pluginId","zone","surface","description","layout","registerConsoleExtension","widgets","slot","widgetId","title","Component","CONSOLE_WIDGET_SLOTS","staffSite","adminOrgDetail","displayName","featureFlag","searchSources","navItems","href","sections","navTabId","header","docsTopic","orgNavItems","default"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/plugins/workflows/src/lib/plugin.ts"],"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 * as Aglyn from '@aglyn/aglyn'\nimport { mdiSitemap } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport { HostActivityCard } from './components/host-activity-card.component'\nimport {\n StaffOrgAutomationsCard,\n StaffSiteAutomationsCard,\n} from './components/staff-automations-card.component'\nimport {\n WORKFLOWS_CONSOLE_SECTIONS,\n WORKFLOWS_ORG_CONSOLE_SECTIONS,\n} from './components/workflows-console-sections'\nimport { registerPluginZone } from '@aglyn/aglyn/plugin-manager/plugin-zones'\nimport { registerPluginTransferResourceUi } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport {\n AUTOMATION_EDITOR_ZONE,\n AUTOMATION_RUN_ZONE,\n HOST_AUTOMATIONS_ZONE,\n ORG_AUTOMATIONS_ZONE,\n WORKFLOW_USAGE_ZONE,\n} from './components/workflow-zones'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport { registerWorkflowsRecordLists } from './model/workflows-record-lists'\nimport { registerWorkflowsRecordRoutes } from './model/workflows-record-routes'\nimport { WORKFLOWS_SEARCH_SOURCES } from './model/workflows-search-sources'\n\n/** Code-split: the Automation console page only loads when opened. */\nconst WorkflowsConsolePage = lazy(\n () => import('./components/workflows-console-page'),\n)\n\n/**\n * Workflows feature plugin (AGL-395). Console-only — workflows, actions,\n * and webhooks run server-side (event pipelines, `/api/hooks`), not through\n * a canvas element, so there is no UI bundle. The console half declares the\n * Automation nav + page through the ConsoleExtension registry, gated by the\n * `workflows` entitlement (the Actions/Webhooks tabs run their own per-plan\n * checks off the passed `org`). Where a workflow is used is asked of the\n * platform's scan and drawn in a zone this plugin hosts.\n *\n * `pluginId`, `featureFlag` and `navTabId` all still read `workflows`: they\n * are stored — an org's enabled-plugin ids, the plan's entitlement flags, the\n * Remote Config release flag — and a section's name is not a reason to orphan\n * them. Only what a reader sees carries the new name.\n */\nexport function registerWorkflowsConsole(): void {\n // How the Import & export hub names org automations in a workspace package (AGL-3535).\n registerPluginTransferResourceUi(\n 'workflows.org-automations',\n { label: 'Org automations', icon: { path: mdiSitemap.path } },\n { pluginId: BUNDLE_ID },\n )\n registerWorkflowsRecordRoutes()\n registerWorkflowsRecordLists()\n registerPluginZone(\n {\n zone: WORKFLOW_USAGE_ZONE,\n label: 'Where a workflow is used',\n surface: 'console',\n description:\n 'On the Automation page, after a reader asks where a workflow is used. A widget here lays out the scan the page already ran; it is handed the site, the answer and a way to close, and it writes nothing.',\n },\n // Named, because a spec calls this registrar without the loader.\n { pluginId: BUNDLE_ID },\n )\n // The zones the Automation page draws in its own layouts (AGL-2919): one\n // item in the Actions row, inside an open editor, on a failed run's row.\n registerPluginZone(\n {\n zone: HOST_AUTOMATIONS_ZONE,\n label: 'Automation page actions',\n surface: 'console',\n layout: 'bare',\n description:\n 'On the Automation page, beside Add action and Recipes, and in the Workflows card’s header or empty state: another way to start an automation. `openAction` opens a listed action in the Actions editor; a widget here writes nothing through the page.',\n },\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: ORG_AUTOMATIONS_ZONE,\n label: 'Org automations actions',\n surface: 'console',\n layout: 'bare',\n description:\n 'In the header of the workspace’s Org automations card, for a member who may write them: another way to start an org automation. `triggers` and `steps` are what one may start on and hold; `propose` opens an automation in the section’s editor, unsaved and switched off. A widget here writes nothing.',\n },\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: AUTOMATION_EDITOR_ZONE,\n label: 'An automation’s editor',\n surface: 'console',\n layout: 'bare',\n description:\n 'Inside the editor of one saved automation, an action or a workflow. A widget here reads the automation as it is stored and changes nothing in the editor; in an action’s editor, `openAction` opens another listed action, such as a copy the widget drafted, in its place.',\n },\n { pluginId: BUNDLE_ID },\n )\n registerPluginZone(\n {\n zone: AUTOMATION_RUN_ZONE,\n label: 'A failed automation run',\n surface: 'console',\n layout: 'bare',\n description:\n 'On each failed run in an automation’s run history. A widget here reads the run as it was recorded and changes nothing.',\n },\n { pluginId: BUNDLE_ID },\n )\n Aglyn.registerConsoleExtension({\n // Host activity feed (AGL-419): rendered by the shell's\n // 'hostActivity' widget slot (dashboard + editor view page).\n widgets: [\n {\n slot: 'hostActivity',\n widgetId: 'workflows-host-activity',\n title: 'Recent Activity',\n Component: HostActivityCard,\n },\n // The staff site and organization pages' automations (AGL-3379), read\n // only: staff see what runs on a site without joining it.\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.staffSite,\n widgetId: 'workflows-staff-site-automations',\n title: 'Automations',\n Component: StaffSiteAutomationsCard,\n },\n {\n slot: Aglyn.CONSOLE_WIDGET_SLOTS.adminOrgDetail,\n widgetId: 'workflows-staff-org-automations',\n title: 'Organization automations',\n Component: StaffOrgAutomationsCard,\n },\n ],\n pluginId: BUNDLE_ID,\n displayName: 'Automation',\n featureFlag: 'workflows',\n searchSources: WORKFLOWS_SEARCH_SOURCES,\n navItems: [\n {\n label: 'Automation',\n // Workflows, Actions and Webhooks are the tabs INSIDE this section, so\n // the section is named for what they have in common. `/workflows` —\n // the address before the section had its own name — is redirected here\n // by this plugin's `consoleRedirects`.\n href: '/automation',\n // Sections as ROUTES (AGL-2501): each is a real URL the shell\n // resolves and gates, so the page mounts the one being read.\n sections: WORKFLOWS_CONSOLE_SECTIONS,\n // Reuse the existing release-flag nav-tab so staff-preview gating is\n // unchanged now that the tab comes from the plugin (AGL-395).\n navTabId: 'nav-tab-workflows',\n icon: { path: mdiSitemap.path },\n header: {\n title: 'Automation',\n icon: { path: mdiSitemap.path },\n docsTopic: 'workflows',\n },\n Component: WorkflowsConsolePage,\n },\n ],\n /*\n * The ORGANIZATION's Automation hub (AGL-3302), at `/[orgSlug]/automation`:\n * the org automations placed across the organization's sites, and every\n * site's own workflows, actions and webhooks side by side. The same page\n * component, mounted with no site; the site tab's release flag gates both\n * levels, so one flag rolls the surface out at once.\n */\n orgNavItems: [\n {\n label: 'Automation',\n href: '/automation',\n sections: WORKFLOWS_ORG_CONSOLE_SECTIONS,\n navTabId: 'nav-tab-workflows',\n icon: { path: mdiSitemap.path },\n header: {\n title: 'Automation',\n icon: { path: mdiSitemap.path },\n docsTopic: 'workflows',\n },\n Component: WorkflowsConsolePage,\n },\n ],\n })\n}\n\n// Activity feed consumed directly by the app (dashboard + screen view).\nexport { default as HostActivityCard } from './components/host-activity-card.component'\nexport type { HostActivityCardProps } from './components/host-activity-card.component'\n"],"names":["Aglyn","mdiSitemap","lazy","HostActivityCard","StaffOrgAutomationsCard","StaffSiteAutomationsCard","WORKFLOWS_CONSOLE_SECTIONS","WORKFLOWS_ORG_CONSOLE_SECTIONS","registerPluginZone","registerPluginTransferResourceUi","AUTOMATION_EDITOR_ZONE","AUTOMATION_RUN_ZONE","HOST_AUTOMATIONS_ZONE","ORG_AUTOMATIONS_ZONE","WORKFLOW_USAGE_ZONE","BUNDLE_ID","registerWorkflowsRecordLists","registerWorkflowsRecordRoutes","WORKFLOWS_SEARCH_SOURCES","WorkflowsConsolePage","registerWorkflowsConsole","label","icon","path","pluginId","zone","surface","description","layout","registerConsoleExtension","widgets","slot","widgetId","title","Component","CONSOLE_WIDGET_SLOTS","staffSite","adminOrgDetail","displayName","featureFlag","searchSources","navItems","href","sections","navTabId","header","docsTopic","orgNavItems","default"],"mappings":"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"}
@@ -0,0 +1,38 @@
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 PluginFigureReader } from '@aglyn/aglyn/plugin-manager/plugin-figures';
18
+ /**
19
+ * A site's automation runs as a figure table another plugin reads by id
20
+ * (AGL-3603) — the AI plugin's insights answer "which automations failed?"
21
+ * from it.
22
+ *
23
+ * Every run an action, a workflow or an organization automation makes is a
24
+ * row in the site's activity log carrying `result` (`succeeded` or `failed`)
25
+ * and `target` — the same rows the run history lists. The totals are COUNT
26
+ * aggregates over `result` in the window (the `activity` composite on
27
+ * `result` + `createdAt`), so they are exact however busy the site is; the
28
+ * per-automation split reads at most `AUTOMATION_RUN_FIGURES_WINDOW_CEILING`
29
+ * runs of each result, projected to `target`, and says so when that read
30
+ * low. A run's event payload names a person and is never read.
31
+ */
32
+ type Firestore = FirebaseFirestore.Firestore;
33
+ /** Runs of one result one window reads for the per-automation split. */
34
+ export declare const AUTOMATION_RUN_FIGURES_WINDOW_CEILING = 500;
35
+ export declare function automationRunFigureReader(firestore: () => Firestore): PluginFigureReader;
36
+ /** Registers the automation runs reader from the console surface, where insight jobs run. */
37
+ export declare function registerAutomationRunFigureReader(firestore: () => Firestore): void;
38
+ export {};