@elevasis/ui 2.71.0 → 2.72.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/api/index.js +3 -3
  2. package/dist/app/index.d.ts +2 -2
  3. package/dist/app/index.js +8 -8
  4. package/dist/auth/index.js +4 -4
  5. package/dist/charts/index.js +4 -4
  6. package/dist/{chunk-TVFSVTRG.js → chunk-2454MK7P.js} +7 -7
  7. package/dist/{chunk-26BTE3Z4.js → chunk-753MKWFV.js} +1 -1
  8. package/dist/{chunk-3BLYJT5L.js → chunk-7ESZKTQG.js} +3 -3
  9. package/dist/{chunk-DCMFNSJ6.js → chunk-A3JHFZDP.js} +1 -1
  10. package/dist/{chunk-VHPHWTX7.js → chunk-AFBFSXP3.js} +4 -4
  11. package/dist/{chunk-BM2WKRAQ.js → chunk-DJWIIPKW.js} +3 -3
  12. package/dist/{chunk-AIOV5NM7.js → chunk-DKVGO6RJ.js} +1 -1
  13. package/dist/{chunk-LL5H7J4N.js → chunk-IMZTRDY2.js} +2 -2
  14. package/dist/{chunk-VRRIHA3S.js → chunk-IXBRUIYI.js} +1 -1
  15. package/dist/{chunk-UZ7MSORW.js → chunk-KIDZXZM5.js} +49 -14
  16. package/dist/{chunk-TCQZ4AJI.js → chunk-LCHL5SM2.js} +1 -1
  17. package/dist/{chunk-H3SDGC5F.js → chunk-LEQGGX2U.js} +4 -4
  18. package/dist/{chunk-NM4A63KJ.js → chunk-Q3O3WY7Q.js} +23 -7
  19. package/dist/{chunk-DH2MO5DX.js → chunk-RRS2QR3W.js} +1 -1
  20. package/dist/{chunk-CPBT6Z2G.js → chunk-SV4M2BUI.js} +4 -4
  21. package/dist/{chunk-TB2PM2OU.js → chunk-UZ5JO5TK.js} +1 -1
  22. package/dist/{chunk-C75XWBYZ.js → chunk-XZIOCB5D.js} +1 -1
  23. package/dist/{chunk-VDX6DL7Z.js → chunk-Z2W5VNN3.js} +1 -1
  24. package/dist/{chunk-JTSNU6KA.js → chunk-ZBXCEWKF.js} +3 -3
  25. package/dist/components/index.d.ts +2 -2
  26. package/dist/components/index.js +23 -23
  27. package/dist/components/navigation/index.js +5 -5
  28. package/dist/features/auth/index.js +6 -6
  29. package/dist/features/clients/index.js +5 -5
  30. package/dist/features/content/index.d.ts +94 -37
  31. package/dist/features/content/index.js +144 -289
  32. package/dist/features/crm/index.d.ts +3 -2
  33. package/dist/features/crm/index.js +6 -6
  34. package/dist/features/dashboard/index.d.ts +1 -1
  35. package/dist/features/dashboard/index.js +6 -6
  36. package/dist/features/delivery/index.js +5 -5
  37. package/dist/features/knowledge/index.js +4 -4
  38. package/dist/features/lead-gen/index.d.ts +3 -2
  39. package/dist/features/lead-gen/index.js +6 -6
  40. package/dist/features/monitoring/index.js +7 -7
  41. package/dist/features/monitoring/requests/index.js +5 -5
  42. package/dist/features/operations/index.d.ts +2 -2
  43. package/dist/features/operations/index.js +9 -9
  44. package/dist/features/settings/index.js +6 -6
  45. package/dist/hooks/access/index.js +4 -4
  46. package/dist/hooks/delivery/index.js +2 -2
  47. package/dist/hooks/index.d.ts +11 -4
  48. package/dist/hooks/index.js +4 -4
  49. package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +1 -1
  50. package/dist/hooks/published.d.ts +11 -4
  51. package/dist/hooks/published.js +4 -4
  52. package/dist/knowledge/index.d.ts +2 -2
  53. package/dist/knowledge/index.js +18 -18
  54. package/dist/{knowledge-search-index-LP2WVSQX.js → knowledge-search-index-RZLIVWPJ.js} +5 -5
  55. package/dist/layout/index.js +7 -7
  56. package/dist/provider/index.d.ts +2 -2
  57. package/dist/provider/index.js +4 -4
  58. package/dist/provider/published.d.ts +2 -2
  59. package/dist/provider/published.js +4 -4
  60. package/dist/test-utils/index.js +3 -3
  61. package/dist/types/index.d.ts +1 -1
  62. package/dist/utils/index.d.ts +1 -1
  63. package/dist/utils/index.js +1 -1
  64. package/package.json +3 -3
@@ -1,14 +1,14 @@
1
1
  import { PageContainer } from '../../chunk-BZZCNLT6.js';
2
2
  import '../../chunk-TUMSNGTX.js';
3
3
  import { FilterBar } from '../../chunk-PDHTXPSF.js';
4
- import { CustomModal, useContentItems, useContentDistributions, useContentConfig, SubshellContentContainer, PageTitleCaption, CenteredErrorState, EmptyState, StatCard, getContentItemIdentity, usePaginationState, useContentItem, useContentItemAttempts, useUpdateContentItem, useReviewContentItem, getOpenContentReviewGates, useContentDistribution, useUpdateContentDistribution, useContentPipelineSummary, deriveContentBoard, useExecutions } from '../../chunk-UZ7MSORW.js';
5
- export { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle, DEFAULT_CONTENT_VOCABULARY, MyReviewQueuePanel, contentManifest, deriveContentBoard, getContentItemIdentity, resolveContentStepResource, resolveContentVocabulary, useContentConfig } from '../../chunk-UZ7MSORW.js';
4
+ import { CustomModal, useContentItems, useContentDistributions, useContentConfig, SubshellContentContainer, PageTitleCaption, CenteredErrorState, EmptyState, StatCard, getContentItemIdentity, usePaginationState, useContentItem, useContentItemAttempts, useUpdateContentItem, useReviewContentItem, getOpenContentReviewGates, resolveContentWorkspaceRoute, useContentDistribution, useContentPipelineSummary, deriveContentBoard, useExecutions } from '../../chunk-KIDZXZM5.js';
5
+ export { CONTENT_ITEMS, ContentSidebar, ContentSidebarMiddle, DEFAULT_CONTENT_VOCABULARY, MyReviewQueuePanel, contentManifest, deriveContentBoard, getContentItemIdentity, resolveContentStepResource, resolveContentVocabulary, useContentConfig } from '../../chunk-KIDZXZM5.js';
6
6
  import '../../chunk-R6NMRXNO.js';
7
7
  import '../../chunk-RBGVNPA6.js';
8
8
  import '../../chunk-MKH2KOAO.js';
9
- import '../../chunk-VDX6DL7Z.js';
9
+ import '../../chunk-Z2W5VNN3.js';
10
10
  import '../../chunk-L7BZZ4SI.js';
11
- import { showSuccessNotification } from '../../chunk-C75XWBYZ.js';
11
+ import { showSuccessNotification } from '../../chunk-XZIOCB5D.js';
12
12
  import '../../chunk-MISHPEJX.js';
13
13
  import { CardHeader } from '../../chunk-62GVNH5U.js';
14
14
  import '../../chunk-P45GQ3ZW.js';
@@ -20,7 +20,7 @@ import '../../chunk-UD657ICS.js';
20
20
  import '../../chunk-DD3CCMCZ.js';
21
21
  import '../../chunk-RNP5R5I3.js';
22
22
  import '../../chunk-RT4KRGZT.js';
23
- import { formatTimeAgo, formatRelativeTime } from '../../chunk-NM4A63KJ.js';
23
+ import { formatTimeAgo, formatRelativeTime } from '../../chunk-Q3O3WY7Q.js';
24
24
  import '../../chunk-Y6I3IC45.js';
25
25
  import '../../chunk-6SSQGWK7.js';
26
26
  import '../../chunk-BI5VDO2K.js';
@@ -29,8 +29,8 @@ import '../../chunk-TVTSASST.js';
29
29
  import '../../chunk-BRJ3QZ4E.js';
30
30
  import '../../chunk-Q7DJKLEN.js';
31
31
  import '../../chunk-I2KLQ2HA.js';
32
- import { Stack, Group, Text, Badge, Button, Textarea, Box, Loader, Accordion, Tooltip, Card, Alert, List, Paper, Center, SimpleGrid, Table, TextInput, Select, Pagination, Title, Tabs, Anchor, Divider, Progress, Checkbox, TagsInput, Image, ActionIcon, UnstyledButton, ScrollArea } from '@mantine/core';
33
- import { IconAlertTriangle, IconShieldCheck, IconShieldX, IconArticle, IconArrowRight, IconClock, IconSend, IconSearch, IconFileText, IconListDetails, IconAlertCircle, IconLayoutGrid, IconShare, IconPlayerPlay, IconExternalLink, IconMaximize, IconCopy, IconDownload, IconTrash, IconArrowLeft, IconArrowsExchange, IconCheck, IconX, IconChecklist, IconRoute, IconInfoCircle, IconClockHour4 } from '@tabler/icons-react';
32
+ import { Stack, Group, Text, Badge, Button, Textarea, Box, Loader, Accordion, Tooltip, Card, Alert, List, Paper, Center, SimpleGrid, Table, TextInput, Select, Pagination, Title, Tabs, Anchor, Divider, Progress, Image, ActionIcon, UnstyledButton, ScrollArea } from '@mantine/core';
33
+ import { IconAlertTriangle, IconShieldCheck, IconShieldX, IconArticle, IconArrowRight, IconClock, IconSend, IconSearch, IconFileText, IconExternalLink, IconListDetails, IconAlertCircle, IconLayoutGrid, IconShare, IconPlayerPlay, IconCircleCheck, IconCircleDashed, IconMaximize, IconCopy, IconDownload, IconArrowLeft, IconArrowsExchange, IconCheck, IconX, IconChecklist, IconRoute, IconInfoCircle, IconClockHour4 } from '@tabler/icons-react';
34
34
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
35
35
  import { useState, useMemo, Fragment as Fragment$1, useCallback, useEffect } from 'react';
36
36
  import { useNavigate, useSearch } from '@tanstack/react-router';
@@ -425,7 +425,8 @@ function ContentReviewCard({
425
425
  processingSteps = [],
426
426
  actions,
427
427
  onAction,
428
- actionsPending = false
428
+ actionsPending = false,
429
+ readOnly = false
429
430
  }) {
430
431
  return /* @__PURE__ */ jsx(Card, { withBorder: true, radius: "md", padding: "xl", "data-testid": "content-review-card", children: /* @__PURE__ */ jsxs(Stack, { gap: "lg", children: [
431
432
  title || processingSteps.length > 0 ? /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "flex-start", wrap: "nowrap", children: [
@@ -466,10 +467,10 @@ function ContentReviewCard({
466
467
  onReset,
467
468
  payload,
468
469
  payloadFields,
469
- disabled: actionsPending
470
+ disabled: actionsPending || readOnly
470
471
  }
471
472
  ),
472
- /* @__PURE__ */ jsx(
473
+ readOnly ? null : /* @__PURE__ */ jsx(
473
474
  AlternatesPanel,
474
475
  {
475
476
  body,
@@ -708,6 +709,16 @@ function ContentItemsPage({ headerActions } = {}) {
708
709
  const [statusFilter, setStatusFilter] = useState("");
709
710
  const [pillarFilter, setPillarFilter] = useState("");
710
711
  const pagination = usePaginationState(PAGE_SIZE, [searchQuery, statusFilter, pillarFilter, reviewFilterActive]);
712
+ const openItem = (item) => {
713
+ const pipeline = contentConfig.pipelines.find((candidate) => candidate.id === item.pipelineId);
714
+ const openGate = getOpenContentReviewGates(item.processingState ?? {}, pipeline?.steps ?? [])[0];
715
+ const workspaceRoute = openGate ? resolveContentWorkspaceRoute(pipeline, openGate.step) : void 0;
716
+ if (workspaceRoute) {
717
+ void navigate({ to: workspaceRoute });
718
+ return;
719
+ }
720
+ void navigate({ to: "/content/items/$itemId", params: { itemId: item.id } });
721
+ };
711
722
  const itemsQuery = useContentItems({
712
723
  search: searchQuery.trim() || void 0,
713
724
  status: statusFilter || void 0,
@@ -789,31 +800,23 @@ function ContentItemsPage({ headerActions } = {}) {
789
800
  const statusEntry = contentConfig.stageCatalog[item.status];
790
801
  const pillarEntry = item.pillar ? contentConfig.stageCatalog[item.pillar] : void 0;
791
802
  const statusColors = getContentStageColor(statusEntry);
792
- return /* @__PURE__ */ jsxs(
793
- Table.Tr,
794
- {
795
- style: { cursor: "pointer" },
796
- onClick: () => navigate({ to: "/content/items/$itemId", params: { itemId: item.id } }),
797
- children: [
798
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: getContentItemIdentity(item) }) }),
799
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
800
- Badge,
801
- {
802
- size: "sm",
803
- variant: "light",
804
- styles: {
805
- root: { color: statusColors.accent, backgroundColor: statusColors.background }
806
- },
807
- children: statusEntry?.label ?? item.status
808
- }
809
- ) }),
810
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pillarEntry?.label ?? item.pillar ?? "\u2014" }) }),
811
- /* @__PURE__ */ jsx(Table.Td, { children: item.reviewedAt ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-success)", children: "Reviewed" }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-warning)", children: "Pending" }) }),
812
- /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatRelativeTime(item.updatedAt) }) })
813
- ]
814
- },
815
- item.id
816
- );
803
+ return /* @__PURE__ */ jsxs(Table.Tr, { style: { cursor: "pointer" }, onClick: () => openItem(item), children: [
804
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: getContentItemIdentity(item) }) }),
805
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(
806
+ Badge,
807
+ {
808
+ size: "sm",
809
+ variant: "light",
810
+ styles: {
811
+ root: { color: statusColors.accent, backgroundColor: statusColors.background }
812
+ },
813
+ children: statusEntry?.label ?? item.status
814
+ }
815
+ ) }),
816
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: pillarEntry?.label ?? item.pillar ?? "\u2014" }) }),
817
+ /* @__PURE__ */ jsx(Table.Td, { children: item.reviewedAt ? /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-success)", children: "Reviewed" }) : /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", color: "var(--color-warning)", children: "Pending" }) }),
818
+ /* @__PURE__ */ jsx(Table.Td, { children: /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: formatRelativeTime(item.updatedAt) }) })
819
+ ] }, item.id);
817
820
  }) })
818
821
  ] }),
819
822
  total > PAGE_SIZE && /* @__PURE__ */ jsx(Group, { justify: "center", children: /* @__PURE__ */ jsx(
@@ -953,9 +956,10 @@ function ContentItemReviewPage({ itemId, headerActions }) {
953
956
  if (!pipeline) return `Unknown pipeline "${item.pipelineId}" -- there is no review gate to clear.`;
954
957
  return "No open review gate on this item. Every queued step has already been reviewed, or the current step is still running.";
955
958
  }, [earliestOpenGate, item?.pipelineId, pipeline]);
959
+ const workspaceRoute = resolveContentWorkspaceRoute(pipeline, earliestOpenGate?.step);
956
960
  const reviewActions = useMemo(
957
- () => buildReviewActions(isTerminalPipeline, earliestOpenGate, reviewDisabledReason),
958
- [isTerminalPipeline, earliestOpenGate, reviewDisabledReason]
961
+ () => workspaceRoute ? [] : buildReviewActions(isTerminalPipeline, earliestOpenGate, reviewDisabledReason),
962
+ [workspaceRoute, isTerminalPipeline, earliestOpenGate, reviewDisabledReason]
959
963
  );
960
964
  const pipelinePayloadFields = useMemo(() => getPipelinePayloadFields(pipelineSteps), [pipelineSteps]);
961
965
  const primaryTextField = useMemo(() => getPrimaryPayloadTextField(pipelinePayloadFields), [pipelinePayloadFields]);
@@ -1099,7 +1103,7 @@ function ContentItemReviewPage({ itemId, headerActions }) {
1099
1103
  ] })
1100
1104
  ] }),
1101
1105
  /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
1102
- /* @__PURE__ */ jsx(
1106
+ workspaceRoute ? null : /* @__PURE__ */ jsx(
1103
1107
  Button,
1104
1108
  {
1105
1109
  size: "xs",
@@ -1124,6 +1128,27 @@ function ContentItemReviewPage({ itemId, headerActions }) {
1124
1128
  children: /* @__PURE__ */ jsx(Text, { size: "sm", children: reviewDisabledReason })
1125
1129
  }
1126
1130
  ),
1131
+ workspaceRoute && earliestOpenGate && /* @__PURE__ */ jsx(
1132
+ Alert,
1133
+ {
1134
+ icon: /* @__PURE__ */ jsx(IconExternalLink, { size: 16 }),
1135
+ variant: "light",
1136
+ title: "Reviewed in your workspace",
1137
+ "data-testid": "content-review-workspace-pointer",
1138
+ children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", align: "flex-start", children: [
1139
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: "This pipeline clears its gates on your own screen. Everything below is the piece as it stands." }),
1140
+ /* @__PURE__ */ jsx(
1141
+ Button,
1142
+ {
1143
+ size: "xs",
1144
+ rightSection: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 }),
1145
+ onClick: () => void navigate({ to: workspaceRoute }),
1146
+ children: "Open workspace"
1147
+ }
1148
+ )
1149
+ ] })
1150
+ }
1151
+ ),
1127
1152
  /* @__PURE__ */ jsx(
1128
1153
  ContentReviewCard,
1129
1154
  {
@@ -1137,7 +1162,8 @@ function ContentItemReviewPage({ itemId, headerActions }) {
1137
1162
  processingSteps: visibleProcessingSteps,
1138
1163
  actions: reviewActions,
1139
1164
  onAction: handleAction,
1140
- actionsPending: isSaving
1165
+ actionsPending: isSaving,
1166
+ readOnly: workspaceRoute !== void 0
1141
1167
  }
1142
1168
  ),
1143
1169
  item.reviewerFeedback && /* @__PURE__ */ jsx(Paper, { withBorder: true, p: "md", children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
@@ -1318,7 +1344,7 @@ function formatAge(ms) {
1318
1344
  return `${Math.floor(hours / 24)}d`;
1319
1345
  }
1320
1346
  function BoardItemCard({ card, onOpen }) {
1321
- return /* @__PURE__ */ jsx(Card, { withBorder: true, padding: "xs", onClick: () => onOpen(card.item.id), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1347
+ return /* @__PURE__ */ jsx(Card, { withBorder: true, padding: "xs", onClick: () => onOpen(card), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1322
1348
  /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, lineClamp: 2, children: getContentItemIdentity(card.item) }),
1323
1349
  /* @__PURE__ */ jsxs(Group, { gap: 6, justify: "space-between", wrap: "nowrap", children: [
1324
1350
  /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
@@ -1398,7 +1424,15 @@ function BoardTab({ pipeline }) {
1398
1424
  const itemsQuery = useContentItems({ pipelineId: pipeline.id, limit: BOARD_FETCH_LIMIT });
1399
1425
  const items = useMemo(() => itemsQuery.data?.data ?? [], [itemsQuery.data]);
1400
1426
  const board = useMemo(() => deriveContentBoard(items, pipeline, Date.now()), [items, pipeline]);
1401
- const openItem = (itemId) => navigate({ to: "/content/items/$itemId", params: { itemId } });
1427
+ const openItem = (card) => {
1428
+ const heldAtStep = pipeline.steps.find((step) => step.key === card.stepKey);
1429
+ const workspaceRoute = card.awaitingReview ? resolveContentWorkspaceRoute(pipeline, heldAtStep) : void 0;
1430
+ if (workspaceRoute) {
1431
+ void navigate({ to: workspaceRoute });
1432
+ return;
1433
+ }
1434
+ void navigate({ to: "/content/items/$itemId", params: { itemId: card.item.id } });
1435
+ };
1402
1436
  if (itemsQuery.isLoading) {
1403
1437
  return /* @__PURE__ */ jsx(Center, { p: "xl", children: /* @__PURE__ */ jsx(Loader, {}) });
1404
1438
  }
@@ -1613,21 +1647,21 @@ function RunsTab({ pipeline }) {
1613
1647
  const contentConfig = useContentConfig();
1614
1648
  return /* @__PURE__ */ jsx(Stack, { gap: "md", children: pipeline.steps.map((step) => /* @__PURE__ */ jsx(StepRunsSection, { step, resourceId: contentConfig.resolveStepResource(step)?.resourceId }, step.key)) });
1615
1649
  }
1616
- var PIPELINE_WORKSPACE_TABS = ["board", "distributions", "runs"];
1617
- function isPipelineWorkspaceTab(value) {
1618
- return typeof value === "string" && PIPELINE_WORKSPACE_TABS.includes(value);
1650
+ var PIPELINE_BOARD_TABS = ["board", "distributions", "runs"];
1651
+ function isPipelineBoardTab(value) {
1652
+ return typeof value === "string" && PIPELINE_BOARD_TABS.includes(value);
1619
1653
  }
1620
- function ContentPipelineWorkspacePage({
1654
+ function ContentPipelineBoardPage({
1621
1655
  pipelineId,
1622
1656
  headerActions
1623
1657
  }) {
1624
1658
  const navigate = useNavigate();
1625
1659
  const contentConfig = useContentConfig();
1626
1660
  const rawSearch = useSearch({ strict: false });
1627
- const activeTab = isPipelineWorkspaceTab(rawSearch.tab) ? rawSearch.tab : "board";
1661
+ const activeTab = isPipelineBoardTab(rawSearch.tab) ? rawSearch.tab : "board";
1628
1662
  const handleTabChange = useCallback(
1629
1663
  (value) => {
1630
- if (!isPipelineWorkspaceTab(value) || value === activeTab) return;
1664
+ if (!isPipelineBoardTab(value) || value === activeTab) return;
1631
1665
  void navigate({
1632
1666
  to: ".",
1633
1667
  search: ((current) => ({ ...current, tab: value })),
@@ -1848,6 +1882,33 @@ function ContentDistributionsPage({ headerActions } = {}) {
1848
1882
  ] }) })
1849
1883
  ] }) }) });
1850
1884
  }
1885
+ var COMMON_PLATFORM_FIELDS = [
1886
+ { key: "title", label: "Title" },
1887
+ { key: "description", label: "Description" },
1888
+ { key: "tags", label: "Tags" }
1889
+ ];
1890
+ var PLATFORM_SPECIFIC_FIELDS = {
1891
+ youtube: [
1892
+ { key: "timestamps", label: "Timestamps" },
1893
+ { key: "endScreenVideoId", label: "End Screen Video ID" },
1894
+ { key: "pinnedComment", label: "Pinned Comment" }
1895
+ ],
1896
+ linkedin: [
1897
+ { key: "hook", label: "Hook" },
1898
+ { key: "hashtags", label: "Hashtags" },
1899
+ { key: "firstComment", label: "First Comment" }
1900
+ ],
1901
+ instagram: [
1902
+ { key: "altText", label: "Alt Text" },
1903
+ { key: "hashtags", label: "Hashtags" },
1904
+ { key: "storyLink", label: "Story Link" }
1905
+ ],
1906
+ x: [
1907
+ { key: "hookTweet", label: "Hook Tweet" },
1908
+ { key: "threadTweets", label: "Thread Tweets" },
1909
+ { key: "replyThread", label: "Reply Thread" }
1910
+ ]
1911
+ };
1851
1912
  function formatDateTime3(value) {
1852
1913
  return value ? new Date(value).toLocaleString() : "\u2014";
1853
1914
  }
@@ -1862,6 +1923,17 @@ function getChecklistProgress(checklist) {
1862
1923
  const done = checklist.filter((item) => item.done).length;
1863
1924
  return { done, total: checklist.length, pct: Math.round(done / checklist.length * 100) };
1864
1925
  }
1926
+ function hasPlatformFieldValue(value) {
1927
+ if (Array.isArray(value)) return value.length > 0;
1928
+ return typeof value === "string" && value.trim() !== "";
1929
+ }
1930
+ function PlatformContentField({ label, value }) {
1931
+ if (!hasPlatformFieldValue(value)) return null;
1932
+ return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
1933
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: label }),
1934
+ Array.isArray(value) ? /* @__PURE__ */ jsx(Group, { gap: 4, children: value.map((entry, index) => /* @__PURE__ */ jsx(Badge, { size: "sm", variant: "light", children: String(entry) }, `${String(entry)}-${index}`)) }) : /* @__PURE__ */ jsx(Text, { size: "sm", style: { whiteSpace: "pre-wrap" }, children: String(value) })
1935
+ ] });
1936
+ }
1865
1937
  function MetricsDisplay({ metrics }) {
1866
1938
  if (!metrics || typeof metrics !== "object" || Object.keys(metrics).length === 0) {
1867
1939
  return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No metrics recorded yet." });
@@ -1885,17 +1957,8 @@ function ContentDistributionDetailPage({
1885
1957
  const distributionQuery = useContentDistribution(distributionId);
1886
1958
  const dist = distributionQuery.data;
1887
1959
  const itemQuery = useContentItem(dist?.contentItemId);
1888
- const updateDistribution = useUpdateContentDistribution();
1889
- const [platformContent, setPlatformContent] = useState({});
1890
- const [platformUrl, setPlatformUrl] = useState("");
1891
- const [status, setStatus] = useState("");
1892
1960
  const [mediaOpened, { open: openMedia, close: closeMedia }] = useDisclosure(false);
1893
1961
  const [previewUrl, setPreviewUrl] = useState(null);
1894
- useEffect(() => {
1895
- setPlatformUrl(dist?.platformUrl ?? "");
1896
- setStatus(dist?.status ?? "");
1897
- setPlatformContent(dist?.platformContent ?? {});
1898
- }, [dist?.id]);
1899
1962
  const backButton = /* @__PURE__ */ jsx(
1900
1963
  Button,
1901
1964
  {
@@ -1926,20 +1989,9 @@ function ContentDistributionDetailPage({
1926
1989
  const platformEntry = contentConfig.stageCatalog[dist.platform];
1927
1990
  const platformColors = getContentStageColor(platformEntry);
1928
1991
  const pillarEntry = contentItem?.pillar ? contentConfig.stageCatalog[contentItem.pillar] : void 0;
1929
- const isPlatformContentDirty = JSON.stringify(platformContent) !== JSON.stringify(dist.platformContent ?? {});
1930
- const patch = (updates) => {
1931
- updateDistribution.mutate({ distributionId, updates });
1932
- };
1933
- const updatePlatformField = (field, value) => {
1934
- setPlatformContent((prev) => ({ ...prev, [field]: value }));
1935
- };
1936
- const toggleChecklistItem = (key) => {
1937
- const nextChecklist = checklist.map((item) => item.key === key ? { ...item, done: !item.done } : item);
1938
- patch({ checklist: nextChecklist });
1939
- };
1940
- const deleteMedia = (url) => {
1941
- patch({ mediaUrls: mediaUrls.filter((existing) => existing !== url) });
1942
- };
1992
+ const platformContent = dist.platformContent ?? {};
1993
+ const platformFields = [...COMMON_PLATFORM_FIELDS, ...PLATFORM_SPECIFIC_FIELDS[dist.platform] ?? []];
1994
+ const hasPlatformContent = platformFields.some((field) => hasPlatformFieldValue(platformContent[field.key]));
1943
1995
  return /* @__PURE__ */ jsxs(SubshellContentContainer, { children: [
1944
1996
  /* @__PURE__ */ jsxs(PageContainer, { children: [
1945
1997
  /* @__PURE__ */ jsxs(Stack, { children: [
@@ -1997,30 +2049,9 @@ function ContentDistributionDetailPage({
1997
2049
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 140, children: "Format" }),
1998
2050
  /* @__PURE__ */ jsx(Text, { size: "sm", children: dist.format })
1999
2051
  ] }),
2000
- /* @__PURE__ */ jsxs(Group, { wrap: "nowrap", children: [
2052
+ /* @__PURE__ */ jsxs(Group, { children: [
2001
2053
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 140, children: "Status" }),
2002
- /* @__PURE__ */ jsx(
2003
- TextInput,
2004
- {
2005
- size: "xs",
2006
- value: status,
2007
- onChange: (event) => setStatus(event.currentTarget.value),
2008
- style: { flex: 1 },
2009
- onBlur: () => {
2010
- const value = status.trim();
2011
- if (value && value !== dist.status) {
2012
- const updates = { status: value };
2013
- if (value === "published" && !dist.publishedAt) {
2014
- updates.publishedAt = (/* @__PURE__ */ new Date()).toISOString();
2015
- }
2016
- patch(updates);
2017
- }
2018
- },
2019
- onKeyDown: (event) => {
2020
- if (event.key === "Enter") event.currentTarget.blur();
2021
- }
2022
- }
2023
- )
2054
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: dist.status })
2024
2055
  ] }),
2025
2056
  /* @__PURE__ */ jsxs(Group, { children: [
2026
2057
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 140, children: "Publish Method" }),
@@ -2044,26 +2075,20 @@ function ContentDistributionDetailPage({
2044
2075
  ] }),
2045
2076
  /* @__PURE__ */ jsxs(Group, { wrap: "nowrap", children: [
2046
2077
  /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", w: 140, children: "URL" }),
2047
- /* @__PURE__ */ jsx(
2048
- TextInput,
2078
+ dist.platformUrl ? /* @__PURE__ */ jsx(
2079
+ Anchor,
2049
2080
  {
2050
- size: "xs",
2051
- placeholder: "https://...",
2052
- value: platformUrl,
2053
- onChange: (event) => setPlatformUrl(event.currentTarget.value),
2054
- style: { flex: 1 },
2055
- onBlur: () => {
2056
- const value = platformUrl.trim();
2057
- if (value !== (dist.platformUrl ?? "")) {
2058
- patch({ platformUrl: value || null });
2059
- }
2060
- },
2061
- onKeyDown: (event) => {
2062
- if (event.key === "Enter") event.currentTarget.blur();
2063
- },
2064
- rightSection: dist.platformUrl ? /* @__PURE__ */ jsx(Anchor, { href: dist.platformUrl, target: "_blank", rel: "noopener noreferrer", children: /* @__PURE__ */ jsx(IconExternalLink, { size: 14 }) }) : void 0
2081
+ href: dist.platformUrl,
2082
+ target: "_blank",
2083
+ rel: "noopener noreferrer",
2084
+ size: "sm",
2085
+ style: { overflowWrap: "anywhere" },
2086
+ children: /* @__PURE__ */ jsxs(Group, { gap: 4, wrap: "nowrap", children: [
2087
+ dist.platformUrl,
2088
+ /* @__PURE__ */ jsx(IconExternalLink, { size: 14 })
2089
+ ] })
2065
2090
  }
2066
- )
2091
+ ) : /* @__PURE__ */ jsx(Text, { size: "sm", children: "\u2014" })
2067
2092
  ] })
2068
2093
  ] })
2069
2094
  ] }) }),
@@ -2094,174 +2119,15 @@ function ContentDistributionDetailPage({
2094
2119
  ] })
2095
2120
  ] }),
2096
2121
  /* @__PURE__ */ jsx(Divider, {}),
2097
- /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", verticalSpacing: "sm", children: checklist.map((item) => /* @__PURE__ */ jsx(
2098
- Checkbox,
2099
- {
2100
- label: item.label,
2101
- checked: item.done,
2102
- onChange: () => toggleChecklistItem(item.key),
2103
- disabled: updateDistribution.isPending,
2104
- size: "sm"
2105
- },
2106
- item.key
2107
- )) })
2122
+ /* @__PURE__ */ jsx(SimpleGrid, { cols: { base: 1, sm: 2 }, spacing: "sm", verticalSpacing: "sm", children: checklist.map((item) => /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", align: "flex-start", children: [
2123
+ item.done ? /* @__PURE__ */ jsx(IconCircleCheck, { size: 16, color: "var(--color-success)" }) : /* @__PURE__ */ jsx(IconCircleDashed, { size: 16, color: "var(--mantine-color-dimmed)" }),
2124
+ /* @__PURE__ */ jsx(Text, { size: "sm", c: item.done ? "dimmed" : void 0, children: item.label })
2125
+ ] }, item.key)) })
2108
2126
  ] }) }) : null,
2109
2127
  /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2110
- /* @__PURE__ */ jsxs(Group, { justify: "space-between", children: [
2111
- /* @__PURE__ */ jsx(Title, { order: 3, children: "Platform Content" }),
2112
- /* @__PURE__ */ jsx(
2113
- Button,
2114
- {
2115
- size: "xs",
2116
- disabled: !isPlatformContentDirty,
2117
- loading: updateDistribution.isPending,
2118
- onClick: () => patch({ platformContent }),
2119
- children: "Save"
2120
- }
2121
- )
2122
- ] }),
2128
+ /* @__PURE__ */ jsx(Title, { order: 3, children: "Platform Content" }),
2123
2129
  /* @__PURE__ */ jsx(Divider, {}),
2124
- /* @__PURE__ */ jsx(
2125
- TextInput,
2126
- {
2127
- label: "Title",
2128
- value: platformContent.title ?? "",
2129
- onChange: (event) => updatePlatformField("title", event.currentTarget.value)
2130
- }
2131
- ),
2132
- /* @__PURE__ */ jsx(
2133
- Textarea,
2134
- {
2135
- label: "Description",
2136
- autosize: true,
2137
- minRows: 2,
2138
- value: platformContent.description ?? "",
2139
- onChange: (event) => updatePlatformField("description", event.currentTarget.value)
2140
- }
2141
- ),
2142
- /* @__PURE__ */ jsx(
2143
- TagsInput,
2144
- {
2145
- label: "Tags",
2146
- value: platformContent.tags ?? [],
2147
- onChange: (value) => updatePlatformField("tags", value)
2148
- }
2149
- ),
2150
- dist.platform === "youtube" && /* @__PURE__ */ jsxs(Fragment, { children: [
2151
- /* @__PURE__ */ jsx(
2152
- TagsInput,
2153
- {
2154
- label: "Timestamps",
2155
- placeholder: "0:00 Intro",
2156
- value: platformContent.timestamps ?? [],
2157
- onChange: (value) => updatePlatformField("timestamps", value)
2158
- }
2159
- ),
2160
- /* @__PURE__ */ jsx(
2161
- TextInput,
2162
- {
2163
- label: "End Screen Video ID",
2164
- value: platformContent.endScreenVideoId ?? "",
2165
- onChange: (event) => updatePlatformField("endScreenVideoId", event.currentTarget.value)
2166
- }
2167
- ),
2168
- /* @__PURE__ */ jsx(
2169
- Textarea,
2170
- {
2171
- label: "Pinned Comment",
2172
- autosize: true,
2173
- minRows: 2,
2174
- value: platformContent.pinnedComment ?? "",
2175
- onChange: (event) => updatePlatformField("pinnedComment", event.currentTarget.value)
2176
- }
2177
- )
2178
- ] }),
2179
- dist.platform === "linkedin" && /* @__PURE__ */ jsxs(Fragment, { children: [
2180
- /* @__PURE__ */ jsx(
2181
- Textarea,
2182
- {
2183
- label: "Hook",
2184
- autosize: true,
2185
- minRows: 2,
2186
- value: platformContent.hook ?? "",
2187
- onChange: (event) => updatePlatformField("hook", event.currentTarget.value)
2188
- }
2189
- ),
2190
- /* @__PURE__ */ jsx(
2191
- TagsInput,
2192
- {
2193
- label: "Hashtags",
2194
- value: platformContent.hashtags ?? [],
2195
- onChange: (value) => updatePlatformField("hashtags", value)
2196
- }
2197
- ),
2198
- /* @__PURE__ */ jsx(
2199
- Textarea,
2200
- {
2201
- label: "First Comment",
2202
- autosize: true,
2203
- minRows: 2,
2204
- value: platformContent.firstComment ?? "",
2205
- onChange: (event) => updatePlatformField("firstComment", event.currentTarget.value)
2206
- }
2207
- )
2208
- ] }),
2209
- dist.platform === "instagram" && /* @__PURE__ */ jsxs(Fragment, { children: [
2210
- /* @__PURE__ */ jsx(
2211
- Textarea,
2212
- {
2213
- label: "Alt Text",
2214
- autosize: true,
2215
- minRows: 2,
2216
- value: platformContent.altText ?? "",
2217
- onChange: (event) => updatePlatformField("altText", event.currentTarget.value)
2218
- }
2219
- ),
2220
- /* @__PURE__ */ jsx(
2221
- TagsInput,
2222
- {
2223
- label: "Hashtags",
2224
- value: platformContent.hashtags ?? [],
2225
- onChange: (value) => updatePlatformField("hashtags", value)
2226
- }
2227
- ),
2228
- /* @__PURE__ */ jsx(
2229
- TextInput,
2230
- {
2231
- label: "Story Link",
2232
- value: platformContent.storyLink ?? "",
2233
- onChange: (event) => updatePlatformField("storyLink", event.currentTarget.value)
2234
- }
2235
- )
2236
- ] }),
2237
- dist.platform === "x" && /* @__PURE__ */ jsxs(Fragment, { children: [
2238
- /* @__PURE__ */ jsx(
2239
- Textarea,
2240
- {
2241
- label: "Hook Tweet",
2242
- autosize: true,
2243
- minRows: 2,
2244
- value: platformContent.hookTweet ?? "",
2245
- onChange: (event) => updatePlatformField("hookTweet", event.currentTarget.value)
2246
- }
2247
- ),
2248
- /* @__PURE__ */ jsx(
2249
- TagsInput,
2250
- {
2251
- label: "Thread Tweets",
2252
- value: platformContent.threadTweets ?? [],
2253
- onChange: (value) => updatePlatformField("threadTweets", value)
2254
- }
2255
- ),
2256
- /* @__PURE__ */ jsx(
2257
- TextInput,
2258
- {
2259
- label: "Reply Thread",
2260
- value: platformContent.replyThread ?? "",
2261
- onChange: (event) => updatePlatformField("replyThread", event.currentTarget.value)
2262
- }
2263
- )
2264
- ] })
2130
+ hasPlatformContent ? platformFields.map((field) => /* @__PURE__ */ jsx(PlatformContentField, { label: field.label, value: platformContent[field.key] }, field.key)) : /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", children: "No platform content recorded yet." })
2265
2131
  ] }) }),
2266
2132
  dist.adaptedBody ? /* @__PURE__ */ jsx(Card, { withBorder: true, children: /* @__PURE__ */ jsxs(Stack, { gap: "sm", children: [
2267
2133
  /* @__PURE__ */ jsx(Title, { order: 3, children: "Adapted Content" }),
@@ -2331,18 +2197,7 @@ function ContentDistributionDetailPage({
2331
2197
  }
2332
2198
  ) }),
2333
2199
  /* @__PURE__ */ jsx(Tooltip, { label: "Copy URL", children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", onClick: () => navigator.clipboard.writeText(url), children: /* @__PURE__ */ jsx(IconCopy, { size: 16 }) }) }),
2334
- /* @__PURE__ */ jsx(Tooltip, { label: "Download", children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", component: "a", href: url, download: true, target: "_blank", children: /* @__PURE__ */ jsx(IconDownload, { size: 16 }) }) }),
2335
- /* @__PURE__ */ jsx(Tooltip, { label: "Delete", children: /* @__PURE__ */ jsx(
2336
- ActionIcon,
2337
- {
2338
- variant: "subtle",
2339
- color: "var(--color-error)",
2340
- size: "sm",
2341
- loading: updateDistribution.isPending,
2342
- onClick: () => deleteMedia(url),
2343
- children: /* @__PURE__ */ jsx(IconTrash, { size: 16 })
2344
- }
2345
- ) })
2200
+ /* @__PURE__ */ jsx(Tooltip, { label: "Download", children: /* @__PURE__ */ jsx(ActionIcon, { variant: "subtle", size: "sm", component: "a", href: url, download: true, target: "_blank", children: /* @__PURE__ */ jsx(IconDownload, { size: 16 }) }) })
2346
2201
  ] })
2347
2202
  ] }, url)) })
2348
2203
  ] }) })
@@ -2351,4 +2206,4 @@ function ContentDistributionDetailPage({
2351
2206
  ] });
2352
2207
  }
2353
2208
 
2354
- export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard, PayloadBody, ProcessingStateStrip, ReviewActionBar, firstLine, formatElapsed, lastLine, swapFirstLine, swapLastLine };
2209
+ export { AlternatesPanel, ContentDistributionDetailPage, ContentDistributionsPage, ContentItemReviewPage, ContentItemsPage, ContentOverviewPage, ContentPipelineBoardPage, ContentPipelineBoardPage as ContentPipelineWorkspacePage, ContentPipelinesPage, ContentReviewCard, PayloadBody, ProcessingStateStrip, ReviewActionBar, firstLine, formatElapsed, lastLine, swapFirstLine, swapLastLine };
@@ -3965,7 +3965,7 @@ declare const OntologyScopeSchema: z.ZodDefault<z.ZodObject<{
3965
3965
  aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
3966
3966
  route: z.ZodOptional<z.ZodString>;
3967
3967
  }, z.core.$loose>>>>;
3968
- }, z.core.$strip>>;
3968
+ }, z.core.$strict>>;
3969
3969
  type OntologyScope = z.infer<typeof OntologyScopeSchema>;
3970
3970
 
3971
3971
  declare const SystemApiInterfaceSchema: z.ZodObject<{
@@ -4627,7 +4627,7 @@ declare const OrganizationModelSchema: z.ZodObject<{
4627
4627
  aliases: z.ZodOptional<z.ZodArray<z.ZodString>>;
4628
4628
  route: z.ZodOptional<z.ZodString>;
4629
4629
  }, z.core.$loose>>>>;
4630
- }, z.core.$strip>>>;
4630
+ }, z.core.$strict>>>;
4631
4631
  resources: z.ZodDefault<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
4632
4632
  id: z.ZodString;
4633
4633
  order: z.ZodDefault<z.ZodNumber>;
@@ -5085,6 +5085,7 @@ declare const CRM_API_INTERFACE: {
5085
5085
  readonly systemPath: "sales.crm";
5086
5086
  readonly interfaceKey: "api";
5087
5087
  readonly readinessProfile: "sales.crm.api";
5088
+ readonly validation: "built-in";
5088
5089
  };
5089
5090
 
5090
5091
  /**