@aglyn/plugins-workflows 1.0.0-beta.231 → 1.0.0-beta.233

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/staff-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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiEyeOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useFirestore, usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n documentId,\n type Firestore,\n limit,\n orderBy,\n query,\n type Query,\n where,\n} from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\n\n/*\n * AUTOMATIONS, AS STAFF SEE THEM (AGL-3379).\n *\n * The staff site and organization pages are about a customer the staffer is\n * not a member of, so nothing here edits, runs or pauses: each table is a\n * paged walk of one collection in document-id order, and a row opens a\n * read-only view of the trigger and the steps. The rules admit staff reads on\n * every site and organization subcollection.\n */\n\ntype AutomationRow = { $id: string } & Record<string, any>\n\nconst nameOf = (row: AutomationRow): string => String(row['name'] || row['displayName'] || row.$id)\n\nconst whenOf = (value: any): string =>\n typeof value?.toDate === 'function' ? value.toDate().toLocaleString() : '—'\n\n/** The trigger and the steps of one automation, read-only. */\nfunction StepsDialog(props: { row: AutomationRow | null; onClose: () => void }) {\n const { row, onClose } = props\n const steps: unknown[] = Array.isArray(row?.['steps']) ? row['steps'] : []\n return (\n <Dialog open={Boolean(row)} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>{row ? nameOf(row) : ''}</DialogTitle>\n <DialogContent>\n <Stack spacing={2}>\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Trigger'}\n </Typography>\n <Typography variant=\"body2\">\n {row?.['trigger']?.event ?? 'Manual — runs only when called'}\n </Typography>\n {row?.['trigger']?.filter ? (\n <Typography variant=\"caption\" sx={{ fontFamily: 'monospace' }}>\n {`when ${row['trigger'].filter}`}\n </Typography>\n ) : null}\n </Box>\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Steps (${steps.length})`}\n </Typography>\n <Box\n component=\"pre\"\n sx={{\n m: 0,\n p: 1.5,\n borderRadius: 1,\n bgcolor: 'action.hover',\n fontSize: 12,\n overflow: 'auto',\n maxHeight: 420,\n }}\n >\n {JSON.stringify(steps, null, 2)}\n </Box>\n </Box>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Close'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\n\n/** One paged, read-only table of automations. */\nfunction AutomationTable(props: {\n title: string\n buildQuery: (firestore: Firestore, pageLimit: number) => Query | null\n deps: readonly unknown[]\n /** The site the table is read for, to mark what it has paused. */\n hostId?: string\n emptyLabel: string\n onOpen: (row: AutomationRow) => void\n}) {\n const { title, buildQuery, deps, hostId, emptyLabel, onOpen } = props\n const firestore = useFirestore()\n const paged = usePagedCollection<AutomationRow>(\n (pageLimit) => buildQuery(firestore, pageLimit),\n [firestore, ...deps],\n { idField: '$id' },\n )\n const byId = useMemo(() => new Map(paged.rows.map((row) => [row.$id, row])), [paged.rows])\n const columns: GridColDef[] = useMemo(\n () => [\n {\n field: 'name',\n headerName: 'Automation',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: AutomationRow) => nameOf(row),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 1.2,\n minWidth: 200,\n valueGetter: (_value, row: AutomationRow) => String(row['trigger']?.event ?? 'manual'),\n renderCell: ({ row }: { row: AutomationRow }) => {\n const paused: string[] = Array.isArray(row['pausedHostIds']) ? row['pausedHostIds'] : []\n return (\n <Stack\n direction=\"row\"\n spacing={0.5}\n useFlexGap\n sx={{ alignItems: 'center', height: '100%', flexWrap: 'wrap' }}\n >\n <Chip size=\"small\" variant=\"outlined\" label={row['trigger']?.event ?? 'manual'} />\n {row['enabled'] === false ? (\n <Chip size=\"small\" color=\"warning\" label=\"off\" />\n ) : (\n <Chip size=\"small\" color=\"success\" label=\"on\" />\n )}\n {hostId && paused.includes(hostId) ? (\n <Chip size=\"small\" color=\"warning\" label=\"paused here\" />\n ) : !hostId && paused.length ? (\n <Chip size=\"small\" color=\"warning\" label={`paused on ${paused.length}`} />\n ) : null}\n {row['deletedAt'] ? <Chip size=\"small\" color=\"error\" label=\"deleted\" /> : null}\n </Stack>\n )\n },\n },\n {\n field: 'updatedAt',\n headerName: 'Updated',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: AutomationRow) => whenOf(row['updatedAt']),\n },\n listActionsColumn(\n (row: AutomationRow) => (\n <ListRowActions\n label={nameOf(row)}\n quick={{ icon: mdiEyeOutline.path, label: 'View steps', onClick: () => onOpen(row) }}\n items={[]}\n />\n ),\n { width: 110 },\n ),\n ],\n [hostId, onOpen],\n )\n return (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{title}</Typography>\n <ListTable\n rows={paged.rows}\n columns={columns}\n loading={paged.status === 'loading'}\n disableColumnSorting\n noRowsLabel={emptyLabel}\n onOpen={(id) => {\n const row = byId.get(String(id))\n if (row) onOpen(row)\n }}\n hideFooter\n rowHeight={TABLE_ROW_HEIGHT}\n />\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n )\n}\n\nconst inIdOrder = (source: Query, pageLimit: number) =>\n query(source, orderBy(documentId()), limit(pageLimit))\n\n/**\n * The staff site page's automations (`staffSite` zone): the site's own\n * automations, the organization automations that run on it, and its\n * workflows.\n */\nexport function StaffSiteAutomationsCard(props: { hostId: string; orgId?: string }) {\n const { hostId, orgId } = props\n const [open, setOpen] = useState<AutomationRow | null>(null)\n return (\n <>\n <CardDisplay\n header={'Automations'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-automations',\n excerpt:\n \"The site's automations, the organization automations that run on it, and its workflows — read-only, each opening its trigger and steps.\",\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={3}>\n <AutomationTable\n title=\"Site automations\"\n buildQuery={(firestore, pageLimit) =>\n hostId ? inIdOrder(collection(firestore, 'hosts', hostId, 'actions'), pageLimit) : null\n }\n deps={[hostId]}\n hostId={hostId}\n emptyLabel=\"No automations on this site\"\n onOpen={setOpen}\n />\n <AutomationTable\n title=\"Organization automations that run here\"\n buildQuery={(firestore, pageLimit) =>\n orgId && hostId\n ? inIdOrder(\n query(\n collection(firestore, 'orgs', orgId, 'automations'),\n where('visibleTo', 'array-contains-any', ['org', `host:${hostId}`]),\n ),\n pageLimit,\n )\n : null\n }\n deps={[orgId, hostId]}\n hostId={hostId}\n emptyLabel=\"No organization automation runs on this site\"\n onOpen={setOpen}\n />\n <AutomationTable\n title=\"Workflows\"\n buildQuery={(firestore, pageLimit) =>\n hostId ? inIdOrder(collection(firestore, 'hosts', hostId, 'workflows'), pageLimit) : null\n }\n deps={[hostId]}\n emptyLabel=\"No workflows on this site\"\n onOpen={setOpen}\n />\n </Stack>\n </CardDisplay>\n <StepsDialog row={open} onClose={() => setOpen(null)} />\n </>\n )\n}\n\n/** The staff organization page's automations (`adminOrgDetail` zone). */\nexport function StaffOrgAutomationsCard(props: { orgId: string }) {\n const { orgId } = props\n const [open, setOpen] = useState<AutomationRow | null>(null)\n return (\n <>\n <CardDisplay\n header={'Organization automations'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-automations',\n excerpt:\n \"The organization's automations, whether each is on and where it is paused — read-only, each opening its trigger and steps.\",\n })}\n contentGutterX\n contentGutterY\n >\n <AutomationTable\n title=\"Automations\"\n buildQuery={(firestore, pageLimit) =>\n orgId ? inIdOrder(collection(firestore, 'orgs', orgId, 'automations'), pageLimit) : null\n }\n deps={[orgId]}\n emptyLabel=\"This organization has no automations\"\n onOpen={setOpen}\n />\n </CardDisplay>\n <StepsDialog row={open} onClose={() => setOpen(null)} />\n </>\n )\n}\n"],"names":["pluginDocsHelp","mdiEyeOutline","CardDisplay","ListPagination","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","useFirestore","usePagedCollection","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Typography","collection","documentId","limit","orderBy","query","where","useMemo","useState","nameOf","row","String","$id","whenOf","value","toDate","toLocaleString","StepsDialog","props","onClose","steps","Array","isArray","open","Boolean","maxWidth","fullWidth","spacing","variant","color","event","filter","sx","fontFamily","length","component","m","p","borderRadius","bgcolor","fontSize","overflow","maxHeight","JSON","stringify","onClick","AutomationTable","title","buildQuery","deps","hostId","emptyLabel","onOpen","firestore","paged","pageLimit","idField","byId","Map","rows","map","columns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","paused","direction","useFlexGap","alignItems","height","flexWrap","size","label","includes","quick","icon","path","items","width","loading","status","disableColumnSorting","noRowsLabel","id","get","hideFooter","rowHeight","page","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","inIdOrder","source","StaffSiteAutomationsCard","orgId","setOpen","header","help","anchor","excerpt","contentGutterX","contentGutterY","StaffOrgAutomationsCard"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/workflows/src/lib/components/staff-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 { pluginDocsHelp } from '@aglyn/aglyn/app-utils/docs-help'\nimport { mdiEyeOutline } from '@aglyn/shared-data-mdi'\nimport { CardDisplay } from '@aglyn/shared-ui-jsx'\nimport { ListPagination } from '@aglyn/shared-ui-jsx/components/list-pagination.component'\nimport ListQueryNotices from '@aglyn/shared-ui-jsx/components/list-query-notices.component'\nimport {\n ListRowActions,\n ListTable,\n listActionsColumn,\n} from '@aglyn/shared-ui-jsx/components/list-table.component'\nimport { TABLE_ROW_HEIGHT } from '@aglyn/shared-ui-jsx/const/table-pagination'\nimport { useListColumnSort } from '@aglyn/shared-ui-jsx/hooks/use-list-column-sort'\nimport { useFirestore, usePagedCollection } from '@aglyn/tenant-feature-instance'\nimport {\n Box,\n Button,\n Chip,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n Stack,\n Typography,\n} from '@mui/material'\nimport type { GridColDef } from '@mui/x-data-grid'\nimport {\n collection,\n documentId,\n type Firestore,\n limit,\n orderBy,\n query,\n type Query,\n where,\n} from 'firebase/firestore'\nimport { useMemo, useState } from 'react'\n\n/*\n * AUTOMATIONS, AS STAFF SEE THEM (AGL-3379).\n *\n * The staff site and organization pages are about a customer the staffer is\n * not a member of, so nothing here edits, runs or pauses: each table is a\n * paged walk of one collection in document-id order, and a row opens a\n * read-only view of the trigger and the steps. The rules admit staff reads on\n * every site and organization subcollection.\n */\n\ntype AutomationRow = { $id: string } & Record<string, any>\n\nconst nameOf = (row: AutomationRow): string => String(row['name'] || row['displayName'] || row.$id)\n\nconst whenOf = (value: any): string =>\n typeof value?.toDate === 'function' ? value.toDate().toLocaleString() : '—'\n\n/** The trigger and the steps of one automation, read-only. */\nfunction StepsDialog(props: { row: AutomationRow | null; onClose: () => void }) {\n const { row, onClose } = props\n const steps: unknown[] = Array.isArray(row?.['steps']) ? row['steps'] : []\n return (\n <Dialog open={Boolean(row)} onClose={onClose} maxWidth=\"md\" fullWidth>\n <DialogTitle>{row ? nameOf(row) : ''}</DialogTitle>\n <DialogContent>\n <Stack spacing={2}>\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {'Trigger'}\n </Typography>\n <Typography variant=\"body2\">\n {row?.['trigger']?.event ?? 'Manual — runs only when called'}\n </Typography>\n {row?.['trigger']?.filter ? (\n <Typography variant=\"caption\" sx={{ fontFamily: 'monospace' }}>\n {`when ${row['trigger'].filter}`}\n </Typography>\n ) : null}\n </Box>\n <Box>\n <Typography variant=\"overline\" color=\"text.secondary\">\n {`Steps (${steps.length})`}\n </Typography>\n <Box\n component=\"pre\"\n sx={{\n m: 0,\n p: 1.5,\n borderRadius: 1,\n bgcolor: 'action.hover',\n fontSize: 12,\n overflow: 'auto',\n maxHeight: 420,\n }}\n >\n {JSON.stringify(steps, null, 2)}\n </Box>\n </Box>\n </Stack>\n </DialogContent>\n <DialogActions>\n <Button onClick={onClose}>{'Close'}</Button>\n </DialogActions>\n </Dialog>\n )\n}\n\n/** One paged, read-only table of automations. */\nconst millisOf = (value: any): number | null =>\n typeof value?.toMillis === 'function' ? value.toMillis() : null\n\n/*\n * EVERY HEADER SORTS, OVER THE PAGE (AGL-3680). These read-only staff tables\n * walk each collection by document id. Their name falls back across fields\n * that differ by collection (`name`, `displayName`, the id), their status is\n * drawn from the trigger and the pause lists, and `updatedAt` is not stamped\n * by every writer (an imported automation carries none) — so no stored field\n * orders all three, and each header sorts the page on screen and says so.\n */\nconst AUTOMATION_PAGE_SORTS = {\n name: (row: AutomationRow) => nameOf(row),\n status: (row: AutomationRow) => String(row['trigger']?.event ?? 'manual'),\n updatedAt: (row: AutomationRow) => millisOf(row['updatedAt']),\n}\nconst AUTOMATION_PAGE_SORT_HEADERS = { name: 'Automation', status: 'Status', updatedAt: 'Updated' }\nconst NO_QUERY_SORTS = [] as const\n\nfunction AutomationTable(props: {\n title: string\n buildQuery: (firestore: Firestore, pageLimit: number) => Query | null\n deps: readonly unknown[]\n /** The site the table is read for, to mark what it has paused. */\n hostId?: string\n emptyLabel: string\n onOpen: (row: AutomationRow) => void\n}) {\n const { title, buildQuery, deps, hostId, emptyLabel, onOpen } = props\n const firestore = useFirestore()\n const paged = usePagedCollection<AutomationRow>(\n (pageLimit) => buildQuery(firestore, pageLimit),\n [firestore, ...deps],\n { idField: '$id' },\n )\n const byId = useMemo(() => new Map(paged.rows.map((row) => [row.$id, row])), [paged.rows])\n const columnSort = useListColumnSort<AutomationRow>({\n sorts: NO_QUERY_SORTS,\n rows: paged.rows,\n pageSorts: AUTOMATION_PAGE_SORTS,\n headers: AUTOMATION_PAGE_SORT_HEADERS,\n })\n const columns: GridColDef[] = useMemo(\n () => [\n {\n field: 'name',\n headerName: 'Automation',\n flex: 1.4,\n minWidth: 200,\n valueGetter: (_value, row: AutomationRow) => nameOf(row),\n },\n {\n field: 'status',\n headerName: 'Status',\n flex: 1.2,\n minWidth: 200,\n valueGetter: (_value, row: AutomationRow) => String(row['trigger']?.event ?? 'manual'),\n renderCell: ({ row }: { row: AutomationRow }) => {\n const paused: string[] = Array.isArray(row['pausedHostIds']) ? row['pausedHostIds'] : []\n return (\n <Stack\n direction=\"row\"\n spacing={0.5}\n useFlexGap\n sx={{ alignItems: 'center', height: '100%', flexWrap: 'wrap' }}\n >\n <Chip size=\"small\" variant=\"outlined\" label={row['trigger']?.event ?? 'manual'} />\n {row['enabled'] === false ? (\n <Chip size=\"small\" color=\"warning\" label=\"off\" />\n ) : (\n <Chip size=\"small\" color=\"success\" label=\"on\" />\n )}\n {hostId && paused.includes(hostId) ? (\n <Chip size=\"small\" color=\"warning\" label=\"paused here\" />\n ) : !hostId && paused.length ? (\n <Chip size=\"small\" color=\"warning\" label={`paused on ${paused.length}`} />\n ) : null}\n {row['deletedAt'] ? <Chip size=\"small\" color=\"error\" label=\"deleted\" /> : null}\n </Stack>\n )\n },\n },\n {\n field: 'updatedAt',\n headerName: 'Updated',\n flex: 0.9,\n minWidth: 160,\n valueGetter: (_value, row: AutomationRow) => whenOf(row['updatedAt']),\n },\n listActionsColumn(\n (row: AutomationRow) => (\n <ListRowActions\n label={nameOf(row)}\n quick={{ icon: mdiEyeOutline.path, label: 'View steps', onClick: () => onOpen(row) }}\n items={[]}\n />\n ),\n { width: 110 },\n ),\n ],\n [hostId, onOpen],\n )\n return (\n <Stack spacing={1}>\n <Typography variant=\"subtitle2\">{title}</Typography>\n <ListQueryNotices refused={[]} notices={columnSort.notices} />\n <ListTable\n rows={columnSort.rows}\n columns={columns}\n loading={paged.status === 'loading'}\n columnSort={columnSort}\n noRowsLabel={emptyLabel}\n onOpen={(id) => {\n const row = byId.get(String(id))\n if (row) onOpen(row)\n }}\n hideFooter\n rowHeight={TABLE_ROW_HEIGHT}\n />\n <ListPagination\n page={paged.page}\n pageSize={paged.pageSize}\n rowCount={paged.rows.length}\n hasMore={paged.hasMore}\n onPageChange={paged.setPage}\n onPageSizeChange={paged.setPageSize}\n />\n </Stack>\n )\n}\n\nconst inIdOrder = (source: Query, pageLimit: number) =>\n query(source, orderBy(documentId()), limit(pageLimit))\n\n/**\n * The staff site page's automations (`staffSite` zone): the site's own\n * automations, the organization automations that run on it, and its\n * workflows.\n */\nexport function StaffSiteAutomationsCard(props: { hostId: string; orgId?: string }) {\n const { hostId, orgId } = props\n const [open, setOpen] = useState<AutomationRow | null>(null)\n return (\n <>\n <CardDisplay\n header={'Automations'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-automations',\n excerpt:\n \"The site's automations, the organization automations that run on it, and its workflows — read-only, each opening its trigger and steps.\",\n })}\n contentGutterX\n contentGutterY\n >\n <Stack spacing={3}>\n <AutomationTable\n title=\"Site automations\"\n buildQuery={(firestore, pageLimit) =>\n hostId ? inIdOrder(collection(firestore, 'hosts', hostId, 'actions'), pageLimit) : null\n }\n deps={[hostId]}\n hostId={hostId}\n emptyLabel=\"No automations on this site\"\n onOpen={setOpen}\n />\n <AutomationTable\n title=\"Organization automations that run here\"\n buildQuery={(firestore, pageLimit) =>\n orgId && hostId\n ? inIdOrder(\n query(\n collection(firestore, 'orgs', orgId, 'automations'),\n where('visibleTo', 'array-contains-any', ['org', `host:${hostId}`]),\n ),\n pageLimit,\n )\n : null\n }\n deps={[orgId, hostId]}\n hostId={hostId}\n emptyLabel=\"No organization automation runs on this site\"\n onOpen={setOpen}\n />\n <AutomationTable\n title=\"Workflows\"\n buildQuery={(firestore, pageLimit) =>\n hostId ? inIdOrder(collection(firestore, 'hosts', hostId, 'workflows'), pageLimit) : null\n }\n deps={[hostId]}\n emptyLabel=\"No workflows on this site\"\n onOpen={setOpen}\n />\n </Stack>\n </CardDisplay>\n <StepsDialog row={open} onClose={() => setOpen(null)} />\n </>\n )\n}\n\n/** The staff organization page's automations (`adminOrgDetail` zone). */\nexport function StaffOrgAutomationsCard(props: { orgId: string }) {\n const { orgId } = props\n const [open, setOpen] = useState<AutomationRow | null>(null)\n return (\n <>\n <CardDisplay\n header={'Organization automations'}\n help={pluginDocsHelp('staffConsole', {\n anchor: '#staff-automations',\n excerpt:\n \"The organization's automations, whether each is on and where it is paused — read-only, each opening its trigger and steps.\",\n })}\n contentGutterX\n contentGutterY\n >\n <AutomationTable\n title=\"Automations\"\n buildQuery={(firestore, pageLimit) =>\n orgId ? inIdOrder(collection(firestore, 'orgs', orgId, 'automations'), pageLimit) : null\n }\n deps={[orgId]}\n emptyLabel=\"This organization has no automations\"\n onOpen={setOpen}\n />\n </CardDisplay>\n <StepsDialog row={open} onClose={() => setOpen(null)} />\n </>\n )\n}\n"],"names":["pluginDocsHelp","mdiEyeOutline","CardDisplay","ListPagination","ListQueryNotices","ListRowActions","ListTable","listActionsColumn","TABLE_ROW_HEIGHT","useListColumnSort","useFirestore","usePagedCollection","Box","Button","Chip","Dialog","DialogActions","DialogContent","DialogTitle","Stack","Typography","collection","documentId","limit","orderBy","query","where","useMemo","useState","nameOf","row","String","$id","whenOf","value","toDate","toLocaleString","StepsDialog","props","onClose","steps","Array","isArray","open","Boolean","maxWidth","fullWidth","spacing","variant","color","event","filter","sx","fontFamily","length","component","m","p","borderRadius","bgcolor","fontSize","overflow","maxHeight","JSON","stringify","onClick","millisOf","toMillis","AUTOMATION_PAGE_SORTS","name","status","updatedAt","AUTOMATION_PAGE_SORT_HEADERS","NO_QUERY_SORTS","AutomationTable","title","buildQuery","deps","hostId","emptyLabel","onOpen","firestore","paged","pageLimit","idField","byId","Map","rows","map","columnSort","sorts","pageSorts","headers","columns","field","headerName","flex","minWidth","valueGetter","_value","renderCell","paused","direction","useFlexGap","alignItems","height","flexWrap","size","label","includes","quick","icon","path","items","width","refused","notices","loading","noRowsLabel","id","get","hideFooter","rowHeight","page","pageSize","rowCount","hasMore","onPageChange","setPage","onPageSizeChange","setPageSize","inIdOrder","source","StaffSiteAutomationsCard","orgId","setOpen","header","help","anchor","excerpt","contentGutterX","contentGutterY","StaffOrgAutomationsCard"],"mappings":"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"}
@@ -32,6 +32,7 @@ import { renderSiteTextEmail } from "@aglyn/tenant-data-admin/server/host-email-
32
32
  import { createHmac } from "crypto";
33
33
  import { FieldValue } from "firebase-admin/firestore";
34
34
  import { runSummaryFields } from "../model/run-history.js";
35
+ import { webhookSummaryText } from "../model/webhook-text.js";
35
36
  import { ACTION_MAX_EVENT_DEPTH, runWorkflow, WORKFLOW_MAX_STEPS } from "../model/workflows.js";
36
37
  import { describeStepOutcome } from "../model/step-outcomes.js";
37
38
  import { eventRunSuspension } from "./site-suspension.js";
@@ -299,9 +300,12 @@ export function makeWorkflowContextLoader(hostRef) {
299
300
  if (!hook || hookDoc.get('deletedAt') || hook.enabled === false || hook.direction !== 'outbound' || !hook.url) {
300
301
  return failed(`unknown webhook "${step.webhookName || step.webhookId}"`);
301
302
  }
303
+ // `text` is what a Slack, Google Chat or Teams incoming webhook posts
304
+ // (AGL-3684); without it Slack refuses the body with `400 no_text`.
302
305
  const body = JSON.stringify({
303
306
  event,
304
307
  payload,
308
+ text: webhookSummaryText(event, payload),
305
309
  sentAt: new Date().toISOString()
306
310
  });
307
311
  const signature = hook.secret ? createHmac('sha256', hook.secret).update(body).digest('hex') : '';