@manta-eu/react 0.1.3 → 0.1.5

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 (139) hide show
  1. package/README.md +6 -9
  2. package/dist/components/audience-phase-badge/index.d.ts +1 -1
  3. package/dist/components/audience-phase-badge/index.d.ts.map +1 -1
  4. package/dist/components/audience-phase-badge/index.js +3 -0
  5. package/dist/components/authenticated-chat/index.d.ts.map +1 -1
  6. package/dist/components/authenticated-chat/index.js +0 -5
  7. package/dist/components/chat/chat-input/index.d.ts.map +1 -1
  8. package/dist/components/chat/chat-input/index.js +7 -0
  9. package/dist/components/chat-view/Chat.d.ts +8 -1
  10. package/dist/components/chat-view/Chat.d.ts.map +1 -1
  11. package/dist/components/chat-view/Chat.js +9 -7
  12. package/dist/components/chat-view/ChatHeaders.d.ts.map +1 -1
  13. package/dist/components/chat-view/ChatHeaders.js +3 -2
  14. package/dist/components/embedded-chat/EmbeddedDatastorePicker.d.ts +1 -0
  15. package/dist/components/embedded-chat/EmbeddedDatastorePicker.d.ts.map +1 -1
  16. package/dist/components/embedded-chat/EmbeddedDatastorePicker.js +9 -3
  17. package/dist/components/embedded-chat/index.d.ts +1 -0
  18. package/dist/components/embedded-chat/index.d.ts.map +1 -1
  19. package/dist/components/embedded-chat/index.js +2 -9
  20. package/dist/components/layouts/Sidebar.d.ts.map +1 -1
  21. package/dist/components/layouts/Sidebar.js +11 -4
  22. package/dist/components/planning/ActioningView.d.ts.map +1 -1
  23. package/dist/components/planning/ActioningView.js +11 -6
  24. package/dist/components/planning/ExecutionSectionItem.d.ts +2 -3
  25. package/dist/components/planning/ExecutionSectionItem.d.ts.map +1 -1
  26. package/dist/components/planning/ExecutionSectionItem.js +3 -6
  27. package/dist/components/planning/ExecutionSections.d.ts +2 -3
  28. package/dist/components/planning/ExecutionSections.d.ts.map +1 -1
  29. package/dist/components/planning/ExecutionSections.js +2 -2
  30. package/dist/components/planning/ManualActionCard.d.ts +3 -4
  31. package/dist/components/planning/ManualActionCard.d.ts.map +1 -1
  32. package/dist/components/planning/ManualActionCard.js +24 -6
  33. package/dist/components/rdf-insights/CampaignChannelMix.js +1 -1
  34. package/dist/components/rdf-insights/CampaignGrid.d.ts.map +1 -1
  35. package/dist/components/rdf-insights/CampaignGrid.js +2 -2
  36. package/dist/components/rdf-insights/DailyPerformance.js +2 -2
  37. package/dist/components/rdf-insights/KpiDashboard.d.ts +2 -2
  38. package/dist/components/rdf-insights/KpiDashboard.d.ts.map +1 -1
  39. package/dist/components/rdf-insights/KpiDashboard.js +33 -53
  40. package/dist/components/rdf-insights/MetaDailyInsights.js +2 -2
  41. package/dist/components/rdf-insights/ProductCard.d.ts.map +1 -1
  42. package/dist/components/rdf-insights/ProductCard.js +1 -1
  43. package/dist/components/rdf-insights/ProductVariantsTable.js +1 -1
  44. package/dist/components/rdf-insights/useExtractedInsights.d.ts +1 -1
  45. package/dist/components/rdf-insights/useExtractedInsights.js +3 -3
  46. package/dist/datastore-picker/datastore-picker-rows.d.ts +1 -0
  47. package/dist/datastore-picker/datastore-picker-rows.d.ts.map +1 -1
  48. package/dist/datastore-picker/datastore-picker-rows.js +6 -2
  49. package/dist/graph/nodes/GoogleCampaignView.js +2 -2
  50. package/dist/hooks/use-action-plans-list.js +1 -1
  51. package/dist/hooks/use-composer-draft.d.ts +10 -0
  52. package/dist/hooks/use-composer-draft.d.ts.map +1 -0
  53. package/dist/hooks/use-composer-draft.js +20 -0
  54. package/dist/hooks/use-manual-actions.d.ts +11 -1
  55. package/dist/hooks/use-manual-actions.d.ts.map +1 -1
  56. package/dist/hooks/use-manual-actions.js +47 -100
  57. package/dist/hooks/useAuth.d.ts +47 -0
  58. package/dist/hooks/useAuth.d.ts.map +1 -1
  59. package/dist/hooks/useAuth.js +50 -10
  60. package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
  61. package/dist/ldo-ui.d.ts +1 -1
  62. package/dist/ldo-ui.d.ts.map +1 -1
  63. package/dist/lib/chat-types.d.ts +5 -0
  64. package/dist/lib/chat-types.d.ts.map +1 -1
  65. package/dist/lib/manual-actions.js +1 -1
  66. package/dist/lib/rdf/index.d.ts +4 -2
  67. package/dist/lib/rdf/index.d.ts.map +1 -1
  68. package/dist/lib/rdf/index.js +48 -18
  69. package/dist/lib/rdf/insights.d.ts +6 -1
  70. package/dist/lib/rdf/insights.d.ts.map +1 -1
  71. package/dist/lib/rdf/insights.js +15 -3
  72. package/dist/lib/rdf/types.d.ts +21 -8
  73. package/dist/lib/rdf/types.d.ts.map +1 -1
  74. package/dist/providers/AuthContext.d.ts +1 -1
  75. package/dist/providers/AuthContext.d.ts.map +1 -1
  76. package/dist/providers/index.d.ts +4 -1
  77. package/dist/providers/index.d.ts.map +1 -1
  78. package/dist/providers/index.js +2 -1
  79. package/dist/routes/chat/index.d.ts +2 -1
  80. package/dist/routes/chat/index.d.ts.map +1 -1
  81. package/dist/routes/chat/index.js +2 -2
  82. package/dist/routes/datastores/components/DatastoreFormDialog.d.ts +4 -2
  83. package/dist/routes/datastores/components/DatastoreFormDialog.d.ts.map +1 -1
  84. package/dist/routes/datastores/components/DatastoreFormDialog.js +7 -4
  85. package/dist/routes/datastores/components/DatastoresToolbar.d.ts +3 -1
  86. package/dist/routes/datastores/components/DatastoresToolbar.d.ts.map +1 -1
  87. package/dist/routes/datastores/components/DatastoresToolbar.js +18 -5
  88. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts.map +1 -1
  89. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js +6 -2
  90. package/dist/routes/datastores/components/ProvisionDatastoreDialog.d.ts +7 -0
  91. package/dist/routes/datastores/components/ProvisionDatastoreDialog.d.ts.map +1 -0
  92. package/dist/routes/datastores/components/ProvisionDatastoreDialog.js +10 -0
  93. package/dist/routes/datastores/flatten.d.ts +4 -0
  94. package/dist/routes/datastores/flatten.d.ts.map +1 -1
  95. package/dist/routes/datastores/flatten.js +11 -0
  96. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts +4 -1
  97. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts.map +1 -1
  98. package/dist/routes/datastores/hooks/useDatastoreColumns.js +6 -2
  99. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.d.ts +3 -0
  100. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.d.ts.map +1 -0
  101. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.js +21 -0
  102. package/dist/routes/datastores/index.d.ts.map +1 -1
  103. package/dist/routes/datastores/index.js +44 -6
  104. package/dist/routes/datastores/link-provider.d.ts +11 -0
  105. package/dist/routes/datastores/link-provider.d.ts.map +1 -0
  106. package/dist/routes/datastores/link-provider.js +34 -0
  107. package/dist/routes/integrations/components/connect-forms/types.d.ts +1 -0
  108. package/dist/routes/integrations/components/connect-forms/types.d.ts.map +1 -1
  109. package/dist/routes/integrations/connection-view.d.ts.map +1 -1
  110. package/dist/routes/integrations/connection-view.js +19 -6
  111. package/dist/routes/integrations/types.d.ts +2 -0
  112. package/dist/routes/integrations/types.d.ts.map +1 -1
  113. package/dist/routes/memories/components/ChangeScopeDialog.d.ts +1 -1
  114. package/dist/routes/memories/components/ChangeScopeDialog.d.ts.map +1 -1
  115. package/dist/routes/memories/components/ChangeScopeDialog.js +3 -3
  116. package/dist/routes/memories/components/EmptyStates.d.ts +1 -2
  117. package/dist/routes/memories/components/EmptyStates.d.ts.map +1 -1
  118. package/dist/routes/memories/components/EmptyStates.js +2 -2
  119. package/dist/routes/memories/components/MemoriesPanel.d.ts +1 -1
  120. package/dist/routes/memories/components/MemoriesPanel.d.ts.map +1 -1
  121. package/dist/routes/memories/components/MemoriesPanel.js +9 -9
  122. package/dist/routes/memories/components/MemoryActions.d.ts +1 -1
  123. package/dist/routes/memories/components/MemoryActions.d.ts.map +1 -1
  124. package/dist/routes/memories/components/MemoryActions.js +4 -4
  125. package/dist/routes/memories/components/MemoryFields.d.ts +1 -1
  126. package/dist/routes/memories/components/MemoryFields.d.ts.map +1 -1
  127. package/dist/routes/memories/components/MemoryFields.js +2 -2
  128. package/dist/routes/memories/components/MemoryFormDialog.d.ts +1 -1
  129. package/dist/routes/memories/components/MemoryFormDialog.d.ts.map +1 -1
  130. package/dist/routes/memories/components/MemoryFormDialog.js +2 -2
  131. package/dist/routes/memories/components/TeamPicker.d.ts +1 -1
  132. package/dist/routes/memories/components/TeamPicker.d.ts.map +1 -1
  133. package/dist/routes/memories/components/TeamPicker.js +2 -2
  134. package/dist/routes/memories/hooks/useMemoryColumns.d.ts +1 -1
  135. package/dist/routes/memories/hooks/useMemoryColumns.d.ts.map +1 -1
  136. package/dist/routes/memories/hooks/useMemoryColumns.js +3 -3
  137. package/dist/routes/memories/index.d.ts.map +1 -1
  138. package/dist/routes/memories/index.js +21 -21
  139. package/package.json +7 -6
@@ -5,7 +5,7 @@ import { buildSections, matchesManualActionSection } from "./execution-section-m
5
5
  import { ExecutionActivityRow } from "./ExecutionActivityRow.js";
6
6
  import { ExecutionSectionItem } from "./ExecutionSectionItem.js";
7
7
  export function ExecutionSections(props) {
8
- const { entityTypes, provenance, manualActions = [], manualActionEntities, activeStartedAtMs = null, onManualActionResolve, onManualActionSkip, isResolvingManualAction = false, } = props;
8
+ const { entityTypes, provenance, manualActions = [], manualActionEntities, activeStartedAtMs = null, onManualActionResolve, isResolvingManualAction = false, } = props;
9
9
  const [expandedIndex, setExpandedIndex] = useState(0);
10
10
  const [lastAutoExpandedRunning, setLastAutoExpandedRunning] = useState(-1);
11
11
  const activities = provenance?.activities ?? [];
@@ -26,6 +26,6 @@ export function ExecutionSections(props) {
26
26
  const effectiveStatus = activeFallbackApplies ? "running" : section.status;
27
27
  const fallbackStartedAtMs = activeFallbackApplies ? activeStartedAtMs : null;
28
28
  const sectionManualActions = manualActions.filter((manualAction) => matchesManualActionSection(manualAction, section.entry, sections.length));
29
- return (_jsx(ExecutionSectionItem, { entry: section.entry, activities: section.activities, status: effectiveStatus, fallbackStartedAtMs: fallbackStartedAtMs, isExpanded: expandedIndex === index, onToggle: () => setExpandedIndex((prev) => (prev === index ? null : index)), manualActions: sectionManualActions, manualActionEntities: manualActionEntities, onManualActionResolve: onManualActionResolve, onManualActionSkip: onManualActionSkip, isResolvingManualAction: isResolvingManualAction }, section.key));
29
+ return (_jsx(ExecutionSectionItem, { entry: section.entry, activities: section.activities, status: effectiveStatus, fallbackStartedAtMs: fallbackStartedAtMs, isExpanded: expandedIndex === index, onToggle: () => setExpandedIndex((prev) => (prev === index ? null : index)), manualActions: sectionManualActions, manualActionEntities: manualActionEntities, onManualActionResolve: onManualActionResolve, isResolvingManualAction: isResolvingManualAction }, section.key));
30
30
  }) }));
31
31
  }
@@ -1,4 +1,4 @@
1
- import type { ManualAction } from "#ldo-ui";
1
+ import type { ManualAction, ManualActionResolution } from "#ldo-ui";
2
2
  export type ManualActionEntity = {
3
3
  subjectIri: string;
4
4
  label: string;
@@ -14,10 +14,9 @@ type ManualActionCardProps = {
14
14
  entities: ManualActionEntity[];
15
15
  description?: string | null;
16
16
  isResolving: boolean;
17
- onApprove: () => void;
18
- onReject: () => void;
19
- onSkip?: () => void;
17
+ onResolve: (resolution: ManualActionResolution) => void;
20
18
  };
21
19
  export declare function ManualActionCard(props: ManualActionCardProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function resolutionChoices(retryable: boolean, defaultResolution: "COMPLETED" | "RETRY"): ManualActionResolution[];
22
21
  export {};
23
22
  //# sourceMappingURL=ManualActionCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ManualActionCard.d.ts","sourceRoot":"","sources":["../../../src/components/planning/ManualActionCard.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG5C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAiB5D"}
1
+ {"version":3,"file":"ManualActionCard.d.ts","sourceRoot":"","sources":["../../../src/components/planning/ManualActionCard.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGpE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI,CAAC;CACzD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,2CAsB5D;AAwID,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,OAAO,EAClB,iBAAiB,EAAE,WAAW,GAAG,OAAO,GACvC,sBAAsB,EAAE,CAI1B"}
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { isPresent } from "@manta-eu/lib/present";
3
3
  import { addAll } from "@manta-eu/lib/set";
4
- import { CircleCheck, Hand, MessageSquareWarning } from "lucide-react";
4
+ import { CircleCheck, Hand, MessageSquareWarning, RotateCcw } from "lucide-react";
5
5
  import { cn } from "#lib/utils";
6
6
  export function ManualActionCard(props) {
7
- const { manualAction, entities, description, isResolving, onApprove, onReject } = props;
8
- const title = description ?? buildManualActionTitle(manualAction);
7
+ const { manualAction, entities, description, isResolving, onResolve } = props;
8
+ const title = description ?? manualAction.description ?? buildManualActionTitle(manualAction);
9
9
  const displayEntities = isPresent(entities) ? entities : [buildFallbackEntity(manualAction)];
10
- return (_jsx("div", { className: "mx-2 my-2", children: _jsxs("div", { className: "overflow-hidden rounded-xl border border-dashed border-active-border/50 bg-card shadow-sm", children: [_jsx(CardHeader, { title: title }), _jsx(EntityTable, { entities: displayEntities }), _jsx(CardFooter, { isResolving: isResolving, onApprove: onApprove, onReject: onReject })] }) }));
10
+ return (_jsx("div", { className: "mx-2 my-2", children: _jsxs("div", { className: "overflow-hidden rounded-xl border border-dashed border-active-border/50 bg-card shadow-sm", children: [_jsx(CardHeader, { title: title }), _jsx(EntityTable, { entities: displayEntities }), _jsx(CardFooter, { isResolving: isResolving, retryable: manualAction.retryable ?? false, defaultResolution: manualAction.defaultResolution ?? "COMPLETED", onResolve: onResolve })] }) }));
11
11
  }
12
12
  function CardHeader(props) {
13
13
  const { title } = props;
@@ -39,9 +39,27 @@ function PropertyChangePills(props) {
39
39
  }
40
40
  return (_jsxs("div", { className: "flex flex-1 items-center justify-end gap-1.5", children: [_jsx("span", { className: "rounded-[5px] bg-accent px-1.5 py-0.5 font-mono text-[11px] leading-4 text-muted-foreground", children: change.oldValue }), _jsx("span", { className: "font-mono text-[11px] text-muted-foreground", children: "\u2192" }), _jsx("span", { className: "rounded-[5px] bg-success px-1.5 py-0.5 font-mono text-[11px] leading-4 text-success-foreground", children: change.newValue })] }));
41
41
  }
42
+ const primaryButton = cn("flex h-8 items-center gap-1.5 rounded-lg bg-success px-3", "typography-tiny text-success-foreground", "hover:bg-success/80 transition-colors", "disabled:opacity-50 disabled:cursor-not-allowed");
43
+ const secondaryButton = cn("flex h-8 items-center gap-1.5 rounded-lg border border-default-border bg-card px-3", "typography-tiny text-default-foreground", "hover:bg-muted-border transition-colors", "disabled:opacity-50 disabled:cursor-not-allowed");
44
+ export function resolutionChoices(retryable, defaultResolution) {
45
+ const positive = retryable ? ["COMPLETED", "RETRY"] : ["COMPLETED"];
46
+ const ordered = retryable && defaultResolution === "RETRY" ? [...positive].reverse() : positive;
47
+ return [...ordered, "REJECTED"];
48
+ }
49
+ const choiceLabels = {
50
+ COMPLETED: "Done, I fixed it",
51
+ RETRY: "Retry",
52
+ REJECTED: "Reject",
53
+ };
54
+ const choiceIcons = {
55
+ COMPLETED: _jsx(CircleCheck, { className: "size-4" }),
56
+ RETRY: _jsx(RotateCcw, { className: "size-4" }),
57
+ REJECTED: _jsx(MessageSquareWarning, { className: "size-4" }),
58
+ };
42
59
  function CardFooter(props) {
43
- const { isResolving, onApprove, onReject } = props;
44
- return (_jsx("div", { className: "flex items-center justify-between bg-muted px-3 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", disabled: isResolving, onClick: onApprove, className: cn("flex h-8 items-center gap-1.5 rounded-lg bg-success px-3", "typography-tiny text-success-foreground", "hover:bg-success/80 transition-colors", "disabled:opacity-50 disabled:cursor-not-allowed"), children: [_jsx(CircleCheck, { className: "size-4" }), "Mark as Completed"] }), _jsxs("button", { type: "button", disabled: isResolving, onClick: onReject, className: cn("flex h-8 items-center gap-1.5 rounded-lg border border-default-border bg-card px-3", "typography-tiny text-default-foreground", "hover:bg-muted-border transition-colors", "disabled:opacity-50 disabled:cursor-not-allowed"), children: [_jsx(MessageSquareWarning, { className: "size-4" }), "Mark as Rejected"] })] }) }));
60
+ const { isResolving, retryable, defaultResolution, onResolve } = props;
61
+ const choices = resolutionChoices(retryable, defaultResolution);
62
+ return (_jsx("div", { className: "flex items-center justify-between bg-muted px-3 py-3", children: _jsx("div", { className: "flex items-center gap-2", children: choices.map((choice, index) => (_jsxs("button", { type: "button", disabled: isResolving, onClick: () => onResolve(choice), className: index === 0 ? primaryButton : secondaryButton, children: [choiceIcons[choice], choiceLabels[choice]] }, choice))) }) }));
45
63
  }
46
64
  function buildManualActionTitle(manualAction) {
47
65
  const subject = manualAction.subjectLabel || manualAction.typeName;
@@ -38,5 +38,5 @@ export function CampaignChannelMix(props) {
38
38
  return (_jsxs("text", { x: viewBox.cx, y: viewBox.cy, textAnchor: "middle", dominantBaseline: "middle", children: [_jsx("tspan", { x: viewBox.cx, y: viewBox.cy, className: "fill-default-foreground typography-extra-large-header ", children: totalCampaigns }), _jsx("tspan", { x: viewBox.cx, y: (viewBox.cy || 0) + 24, className: "fill-muted-foreground typography-tiny", children: "Campaigns" })] }));
39
39
  }
40
40
  return null;
41
- } }) })] }) }), _jsx("div", { className: "space-y-2", children: data.channels.map((channel, index) => (_jsxs("div", { className: "flex items-center justify-between rounded-md border border-muted-border/50 p-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "size-3 rounded-full", style: { backgroundColor: chartData[index]?.fill } }), _jsx("span", { className: "typography-small-medium", children: channel.channel })] }), _jsxs("div", { className: "flex items-center gap-4 typography-tiny text-muted-foreground", children: [_jsxs("span", { children: [channel.campaignCount, " campaign", channel.campaignCount !== 1 ? "s" : ""] }), _jsxs("span", { children: [channel.percentage.toFixed(1), "%"] }), _jsx("span", { className: "font-mono", children: formatMetric(channel.totalSpend, "currency") })] })] }, channel.channel))) }), _jsx(InsightCardFooter, { left: `Total: ${totalCampaigns} campaigns across ${data.channels.length} channels`, right: `${formatMetric(totalSpend, "currency")} spend` })] })] }));
41
+ } }) })] }) }), _jsx("div", { className: "space-y-2", children: data.channels.map((channel, index) => (_jsxs("div", { className: "flex items-center justify-between rounded-md border border-muted-border/50 p-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "size-3 rounded-full", style: { backgroundColor: chartData[index]?.fill } }), _jsx("span", { className: "typography-small-medium", children: channel.channel })] }), _jsxs("div", { className: "flex items-center gap-4 typography-tiny text-muted-foreground", children: [_jsxs("span", { children: [channel.campaignCount, " campaign", channel.campaignCount !== 1 ? "s" : ""] }), _jsxs("span", { children: [channel.percentage.toFixed(1), "%"] }), _jsx("span", { className: "font-mono", children: formatMetric(channel.totalSpend, "currency", channel.currency) })] })] }, channel.channel))) }), _jsx(InsightCardFooter, { left: `Total: ${totalCampaigns} campaigns across ${data.channels.length} channels`, right: `${formatMetric(totalSpend, "currency", data.currency)} spend` })] })] }));
42
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CampaignGrid.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/CampaignGrid.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAoHvD,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kDAyBpD"}
1
+ {"version":3,"file":"CampaignGrid.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/CampaignGrid.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAwHvD,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,gBAAgB,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kDAyBpD"}
@@ -44,12 +44,12 @@ function Campaign(props) {
44
44
  index,
45
45
  value,
46
46
  }));
47
- return (_jsxs("div", { className: "rounded-lg border border-muted-border/50 p-4 space-y-3 hover:border-muted-border transition-colors", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "typography-small-semibold line-clamp-1", children: campaign.name || "Unnamed Campaign" }), _jsxs("div", { className: "flex items-center gap-2 typography-tiny", children: [_jsxs(Badge, { variant: "secondary", className: "gap-1", children: [_jsx(Circle, { className: cn("size-2", getStatusColor(campaign.status)) }), _jsx("span", { children: campaign.status || "Active" })] }), campaign.channel && _jsx("span", { className: "text-muted-foreground", children: campaign.channel })] })] }), _jsx("div", { className: "h-12 -mx-2", children: _jsx(ChartContainer, { config: sparklineConfig, className: "h-full w-full", children: _jsx(LineChart, { data: sparklineData, children: _jsx(Line, { type: "monotone", dataKey: "value", stroke: "var(--color-value)", strokeWidth: 2, dot: false, isAnimationActive: false }) }) }) }), _jsxs("div", { className: "space-y-2 pt-2 border-t border-muted-border/50", children: [_jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(DollarSign, { className: "size-3" }), "Spend"] }), _jsx("span", { className: "font-mono typography-small-semibold", children: formatMetric(campaign.spend, "currency") })] }), _jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(TrendingUp, { className: "size-3" }), "Revenue"] }), _jsx("span", { className: "font-mono typography-small-semibold", children: formatMetric(campaign.revenue, "currency") })] }), _jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(Target, { className: "size-3" }), "ROAS"] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: cn("font-mono typography-small-semibold", roasTier.color), children: [campaign.roas.toFixed(2), "x"] }), _jsx(Badge, { variant: "secondary", className: cn("typography-tiny", roasTier.color), children: roasTier.label })] })] })] })] }, campaign.uri));
47
+ return (_jsxs("div", { className: "rounded-lg border border-muted-border/50 p-4 space-y-3 hover:border-muted-border transition-colors", children: [_jsxs("div", { className: "space-y-1", children: [_jsx("div", { className: "typography-small-semibold line-clamp-1", children: campaign.name || "Unnamed Campaign" }), _jsxs("div", { className: "flex items-center gap-2 typography-tiny", children: [_jsxs(Badge, { variant: "secondary", className: "gap-1", children: [_jsx(Circle, { className: cn("size-2", getStatusColor(campaign.status)) }), _jsx("span", { children: campaign.status || "Active" })] }), campaign.channel && _jsx("span", { className: "text-muted-foreground", children: campaign.channel })] })] }), _jsx("div", { className: "h-12 -mx-2", children: _jsx(ChartContainer, { config: sparklineConfig, className: "h-full w-full", children: _jsx(LineChart, { data: sparklineData, children: _jsx(Line, { type: "monotone", dataKey: "value", stroke: "var(--color-value)", strokeWidth: 2, dot: false, isAnimationActive: false }) }) }) }), _jsxs("div", { className: "space-y-2 pt-2 border-t border-muted-border/50", children: [_jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(DollarSign, { className: "size-3" }), "Spend"] }), _jsx("span", { className: "font-mono typography-small-semibold", children: formatMetric(campaign.spend, "currency", campaign.currency) })] }), _jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(TrendingUp, { className: "size-3" }), "Revenue"] }), _jsx("span", { className: "font-mono typography-small-semibold", children: formatMetric(campaign.revenue, "currency", campaign.currency) })] }), _jsxs("div", { className: "flex items-center justify-between typography-tiny", children: [_jsxs("span", { className: "flex items-center gap-1.5 text-muted-foreground", children: [_jsx(Target, { className: "size-3" }), "ROAS"] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: cn("font-mono typography-small-semibold", roasTier.color), children: [campaign.roas.toFixed(2), "x"] }), _jsx(Badge, { variant: "secondary", className: cn("typography-tiny", roasTier.color), children: roasTier.label })] })] })] })] }, campaign.uri));
48
48
  }
49
49
  export function CampaignGrid(props) {
50
50
  const { data } = props;
51
51
  if (!isPresent(data.campaigns))
52
52
  return null;
53
53
  const totalSpend = data.campaigns.reduce((sum, c) => sum + c.spend, 0);
54
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: LayoutGrid, title: `Campaigns (${data.campaigns.length})` }), _jsxs(CardContent, { className: "pt-4", children: [_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: data.campaigns.map((campaign) => (_jsx(Campaign, { campaign: campaign }, campaign.uri))) }), _jsx(InsightCardFooter, { left: `Showing ${data.campaigns.length} campaigns`, right: `Total Spend: ${formatMetric(totalSpend, "currency")}` })] })] }));
54
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: LayoutGrid, title: `Campaigns (${data.campaigns.length})` }), _jsxs(CardContent, { className: "pt-4", children: [_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: data.campaigns.map((campaign) => (_jsx(Campaign, { campaign: campaign }, campaign.uri))) }), _jsx(InsightCardFooter, { left: `Showing ${data.campaigns.length} campaigns`, right: `Total Spend: ${formatMetric(totalSpend, "currency", data.currency)}` })] })] }));
55
55
  }
@@ -6,7 +6,7 @@ import { InsightCardHeader } from "./InsightCardHeader.js";
6
6
  import { MetricDisplay } from "./MetricDisplay.js";
7
7
  import { Card, CardContent } from "#components/card/index";
8
8
  import { ChartContainer, ChartTooltip, ChartTooltipContent } from "#components/chart/index";
9
- import { formatMetric } from "#lib/rdf/insights";
9
+ import { formatCompactMoney, formatMetric } from "#lib/rdf/insights";
10
10
  export function DailyPerformance(props) {
11
11
  const { data } = props;
12
12
  if (!isPresent(data.days))
@@ -46,5 +46,5 @@ export function DailyPerformance(props) {
46
46
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
47
47
  }
48
48
  const dateRange = startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : "";
49
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Calendar, title: "Daily Performance", dateRange: dateRange }), _jsxs(CardContent, { className: "pt-4 space-y-4", children: [_jsx(ChartContainer, { config: chartConfig, className: "h-[300px]", children: _jsxs(LineChart, { data: chartData, margin: { left: 0, right: 0 }, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: "date", tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => value }), _jsx(YAxis, { tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => `$${(value / 1000).toFixed(0)}k` }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Legend, {}), _jsx(Line, { dataKey: "revenue", type: "monotone", stroke: "var(--color-revenue)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "spend", type: "monotone", stroke: "var(--color-spend)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "orders", type: "monotone", stroke: "var(--color-orders)", strokeWidth: 2, strokeDasharray: "5 5", dot: false })] }) }), _jsxs("div", { className: "grid grid-cols-3 gap-4 pt-4", children: [_jsx(MetricDisplay, { icon: DollarSign, label: "Revenue", value: formatMetric(totalRevenue, "currency"), subtitle: `$${avgDailyRevenue.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: DollarSign, label: "Spend", value: formatMetric(totalSpend, "currency"), subtitle: `$${avgDailySpend.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: ShoppingCart, label: "Orders", value: totalOrders.toString(), subtitle: `${avgDailyOrders.toFixed(1)}/day avg` })] }), _jsxs("div", { className: "pt-2 border-t border-muted-border/50 flex items-center justify-between", children: [_jsx("span", { className: "typography-tiny text-muted-foreground", children: "Overall ROAS" }), _jsxs("span", { className: "typography-small-semibold font-mono", children: [overallRoas.toFixed(2), "x"] })] })] })] }));
49
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Calendar, title: "Daily Performance", dateRange: dateRange }), _jsxs(CardContent, { className: "pt-4 space-y-4", children: [_jsx(ChartContainer, { config: chartConfig, className: "h-[300px]", children: _jsxs(LineChart, { data: chartData, margin: { left: 0, right: 0 }, children: [_jsx(CartesianGrid, { vertical: false }), _jsx(XAxis, { dataKey: "date", tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => value }), _jsx(YAxis, { tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => formatCompactMoney(value, data.currency) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Legend, {}), _jsx(Line, { dataKey: "revenue", type: "monotone", stroke: "var(--color-revenue)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "spend", type: "monotone", stroke: "var(--color-spend)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "orders", type: "monotone", stroke: "var(--color-orders)", strokeWidth: 2, strokeDasharray: "5 5", dot: false })] }) }), _jsxs("div", { className: "grid grid-cols-3 gap-4 pt-4", children: [_jsx(MetricDisplay, { icon: DollarSign, label: "Revenue", value: formatMetric(totalRevenue, "currency", data.currency), subtitle: `$${avgDailyRevenue.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: DollarSign, label: "Spend", value: formatMetric(totalSpend, "currency", data.currency), subtitle: `$${avgDailySpend.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: ShoppingCart, label: "Orders", value: totalOrders.toString(), subtitle: `${avgDailyOrders.toFixed(1)}/day avg` })] }), _jsxs("div", { className: "pt-2 border-t border-muted-border/50 flex items-center justify-between", children: [_jsx("span", { className: "typography-tiny text-muted-foreground", children: "Overall ROAS" }), _jsxs("span", { className: "typography-small-semibold font-mono", children: [overallRoas.toFixed(2), "x"] })] })] })] }));
50
50
  }
@@ -1,6 +1,6 @@
1
- import type { CompanyKpis } from "#lib/rdf/types";
1
+ import type { KpiSnapshot } from "#lib/rdf/types";
2
2
  type KpiDashboardProps = {
3
- kpis: CompanyKpis;
3
+ kpis: KpiSnapshot[];
4
4
  };
5
5
  export declare function KpiDashboard(props: KpiDashboardProps): import("react/jsx-runtime").JSX.Element | null;
6
6
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"KpiDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/KpiDashboard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,WAAW,CAAC;CACnB,CAAC;AAoDF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kDA0CpD"}
1
+ {"version":3,"file":"KpiDashboard.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/KpiDashboard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAa,MAAM,gBAAgB,CAAC;AAE7D,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,WAAW,EAAE,CAAC;CACrB,CAAC;AA4DF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kDAqBpD"}
@@ -3,61 +3,41 @@ import { isPresent } from "@manta-eu/lib/present";
3
3
  import { Building2 } from "lucide-react";
4
4
  import { InsightCardHeader } from "./InsightCardHeader.js";
5
5
  import { Card, CardContent } from "#components/card/index";
6
- import { formatMetric } from "#lib/rdf/insights";
7
- const kpiMetadata = {
8
- mer30d: {
9
- label: "MER (30d)",
10
- format: "multiplier",
11
- description: "Marketing Efficiency Ratio - revenue generated per dollar spent",
12
- },
13
- blendedRoas30d: {
14
- label: "ROAS (30d)",
15
- format: "multiplier",
16
- description: "Return on Ad Spend - 30-day blended ROAS",
17
- },
18
- blendedCac30dUsd: {
19
- label: "CAC (30d)",
20
- format: "currency",
21
- description: "Customer Acquisition Cost - cost to acquire one customer",
22
- },
23
- last30dSpendUsd: {
24
- label: "Spend (30d)",
25
- format: "currency",
26
- description: "Total advertising spend in the last 30 days",
27
- },
28
- last30dRevenueUsd: {
29
- label: "Revenue (30d)",
30
- format: "currency",
31
- description: "Total revenue generated in the last 30 days",
32
- },
33
- avgAov30d: {
34
- label: "AOV (30d)",
35
- format: "currency",
36
- description: "Average Order Value - average revenue per order",
37
- },
38
- grossMargin30d: {
39
- label: "Gross Margin (30d)",
40
- format: "percentage",
41
- description: "Gross profit margin percentage",
42
- },
43
- };
6
+ import { formatMetric, percentChange } from "#lib/rdf/insights";
7
+ const kpiMetadata = [
8
+ { key: "spend", label: "Spend", format: "currency" },
9
+ { key: "revenue", label: "Revenue", format: "currency" },
10
+ { key: "roas", label: "ROAS", format: "multiplier" },
11
+ { key: "purchases", label: "Purchases", format: "count" },
12
+ { key: "cpa", label: "CPA", format: "currency" },
13
+ { key: "aov", label: "AOV", format: "currency" },
14
+ ];
15
+ function formatValue(value, format, currency) {
16
+ if (format === "count")
17
+ return Math.round(value).toLocaleString("en-US");
18
+ return format === "currency" ? formatMetric(value, format, currency) : formatMetric(value, format);
19
+ }
20
+ function formatChange(change) {
21
+ return `${change > 0 ? "+" : ""}${change.toFixed(1)}%`;
22
+ }
23
+ function SnapshotGrid(props) {
24
+ const { snapshot, showCurrency } = props;
25
+ const metrics = kpiMetadata.flatMap((metadata) => {
26
+ const value = snapshot.current[metadata.key];
27
+ if (value === null)
28
+ return [];
29
+ return [{ ...metadata, value, change: percentChange(value, snapshot.previous?.[metadata.key]) }];
30
+ });
31
+ if (!isPresent(metrics))
32
+ return null;
33
+ return (_jsxs("div", { className: "space-y-2", children: [showCurrency && (_jsx("div", { className: "typography-tiny-medium tracking-wide text-muted-foreground uppercase", children: snapshot.currency })), _jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-3", children: metrics.map((metric) => (_jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "typography-tiny-medium tracking-wide text-muted-foreground/70 uppercase", children: metric.label }), _jsx("div", { className: "typography-large-body-semibold font-mono leading-tight", children: formatValue(metric.value, metric.format, snapshot.currency) }), metric.change !== null && (_jsxs("div", { className: "typography-tiny text-muted-foreground", children: [formatChange(metric.change), " vs prev 30d"] }))] }, metric.key))) })] }));
34
+ }
44
35
  export function KpiDashboard(props) {
45
36
  const { kpis } = props;
46
- const metrics = [];
47
- Object.entries(kpis).forEach(([key, value]) => {
48
- if (value !== null) {
49
- const metadata = kpiMetadata[key];
50
- if (metadata) {
51
- metrics.push({
52
- label: metadata.label,
53
- value,
54
- format: metadata.format,
55
- });
56
- }
57
- }
58
- });
59
- if (!isPresent(metrics)) {
37
+ if (!isPresent(kpis)) {
60
38
  return null;
61
39
  }
62
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Building2, title: "Your Performance (30 days)" }), _jsx(CardContent, { className: "pt-4", children: _jsx("div", { className: "grid grid-cols-2 md:grid-cols-3 gap-3", children: metrics.map((metric) => (_jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "typography-tiny-medium tracking-wide text-muted-foreground/70 uppercase", children: metric.label }), _jsx("div", { className: "typography-large-body-semibold font-mono leading-tight", children: formatMetric(metric.value, metric.format) })] }, metric.label))) }) })] }));
40
+ const [first] = kpis;
41
+ const dateRange = first?.startDate && first.endDate ? `${first.startDate} – ${first.endDate}` : undefined;
42
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Building2, title: "Your Performance (30 days)", dateRange: dateRange }), _jsx(CardContent, { className: "pt-4 space-y-4", children: kpis.map((snapshot) => (_jsx(SnapshotGrid, { snapshot: snapshot, showCurrency: kpis.length > 1 }, snapshot.currency))) })] }));
63
43
  }
@@ -6,7 +6,7 @@ import { InsightCardHeader } from "./InsightCardHeader.js";
6
6
  import { MetricDisplay } from "./MetricDisplay.js";
7
7
  import { Card, CardContent, CardFooter } from "#components/card/index";
8
8
  import { ChartContainer, ChartTooltip, ChartTooltipContent } from "#components/chart/index";
9
- import { formatMetric } from "#lib/rdf/insights";
9
+ import { formatCompactMoney, formatMetric } from "#lib/rdf/insights";
10
10
  export function MetaDailyInsights(props) {
11
11
  const { data } = props;
12
12
  if (!isPresent(data.days))
@@ -45,5 +45,5 @@ export function MetaDailyInsights(props) {
45
45
  return date.toLocaleDateString("en-US", { month: "short", day: "numeric" });
46
46
  }
47
47
  const dateRange = startDate && endDate ? `${formatDate(startDate)} - ${formatDate(endDate)}` : "";
48
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Calendar, title: "Meta Campaign Daily Insights", subtitle: data.campaignName, dateRange: dateRange }), _jsxs(CardContent, { className: "pt-4 space-y-4", children: [_jsx("div", { className: "bg-muted/10 rounded-radius p-4", children: _jsx(ChartContainer, { config: chartConfig, className: "h-[300px]", children: _jsxs(LineChart, { data: chartData, margin: { left: 0, right: 0 }, children: [_jsx(CartesianGrid, { vertical: false, strokeOpacity: 0.3 }), _jsx(XAxis, { dataKey: "date", tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => value }), _jsx(YAxis, { tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => `$${(value / 1000).toFixed(0)}k` }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Legend, {}), _jsx(Line, { dataKey: "revenue", type: "monotone", stroke: "var(--color-revenue)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "spend", type: "monotone", stroke: "var(--color-spend)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "roas", type: "monotone", stroke: "var(--color-roas)", strokeWidth: 2, strokeDasharray: "5 5", dot: false })] }) }) }), _jsxs("div", { className: "grid grid-cols-3 gap-4 pt-4", children: [_jsx(MetricDisplay, { icon: DollarSign, label: "Spend", value: formatMetric(totalSpend, "currency"), subtitle: `$${avgDailySpend.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: TrendingUp, label: "Revenue", value: formatMetric(totalRevenue, "currency"), subtitle: `$${avgDailyRevenue.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: ShoppingCart, label: "Purchases", value: totalPurchases.toString(), subtitle: `${(totalPurchases / data.days.length).toFixed(1)}/day avg` })] })] }), _jsx(CardFooter, { className: "pt-0 border-t border-muted-border pt-4", children: _jsxs("div", { className: "flex items-center justify-between w-full typography-tiny", children: [_jsx("span", { className: "text-muted-foreground", children: "Overall ROAS" }), _jsxs("span", { className: "font-mono typography-small-semibold", children: [overallRoas.toFixed(2), "x"] })] }) })] }));
48
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Calendar, title: "Meta Campaign Daily Insights", subtitle: data.campaignName, dateRange: dateRange }), _jsxs(CardContent, { className: "pt-4 space-y-4", children: [_jsx("div", { className: "bg-muted/10 rounded-radius p-4", children: _jsx(ChartContainer, { config: chartConfig, className: "h-[300px]", children: _jsxs(LineChart, { data: chartData, margin: { left: 0, right: 0 }, children: [_jsx(CartesianGrid, { vertical: false, strokeOpacity: 0.3 }), _jsx(XAxis, { dataKey: "date", tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => value }), _jsx(YAxis, { tickLine: false, axisLine: false, tickMargin: 8, tickFormatter: (value) => formatCompactMoney(value, data.currency) }), _jsx(ChartTooltip, { content: _jsx(ChartTooltipContent, {}) }), _jsx(Legend, {}), _jsx(Line, { dataKey: "revenue", type: "monotone", stroke: "var(--color-revenue)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "spend", type: "monotone", stroke: "var(--color-spend)", strokeWidth: 2, dot: false }), _jsx(Line, { dataKey: "roas", type: "monotone", stroke: "var(--color-roas)", strokeWidth: 2, strokeDasharray: "5 5", dot: false })] }) }) }), _jsxs("div", { className: "grid grid-cols-3 gap-4 pt-4", children: [_jsx(MetricDisplay, { icon: DollarSign, label: "Spend", value: formatMetric(totalSpend, "currency", data.currency), subtitle: `$${avgDailySpend.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: TrendingUp, label: "Revenue", value: formatMetric(totalRevenue, "currency", data.currency), subtitle: `$${avgDailyRevenue.toFixed(0)}/day avg` }), _jsx(MetricDisplay, { icon: ShoppingCart, label: "Purchases", value: totalPurchases.toString(), subtitle: `${(totalPurchases / data.days.length).toFixed(1)}/day avg` })] })] }), _jsx(CardFooter, { className: "pt-0 border-t border-muted-border pt-4", children: _jsxs("div", { className: "flex items-center justify-between w-full typography-tiny", children: [_jsx("span", { className: "text-muted-foreground", children: "Overall ROAS" }), _jsxs("span", { className: "font-mono typography-small-semibold", children: [overallRoas.toFixed(2), "x"] })] }) })] }));
49
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/ProductCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG9D,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CA8ElD"}
1
+ {"version":3,"file":"ProductCard.d.ts","sourceRoot":"","sources":["../../../src/components/rdf-insights/ProductCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAG9D,KAAK,gBAAgB,GAAG;IACtB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAgFlD"}
@@ -15,5 +15,5 @@ export function ProductCard(props) {
15
15
  "out-of-stock": { color: "bg-error-foreground", text: "Out of Stock" },
16
16
  };
17
17
  const status = statusConfig[availabilityStatus];
18
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(ShoppingBag, { className: "size-5 text-muted-foreground mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsx(CardTitle, { className: "typography-body", children: product.name }), product.vendor && (_jsxs("div", { className: "flex items-center gap-1 mt-1 typography-small text-muted-foreground", children: [_jsx(User, { className: "size-3.5" }), _jsx("span", { children: product.vendor })] }))] })] }) }), _jsxs(CardContent, { className: "space-y-3", children: [variant?.price !== null && variant?.price !== undefined && (_jsx("div", { className: "typography-large-body-semibold font-mono", children: formatMetric(variant.price, "currency") })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `size-2 rounded-full ${status.color}` }), _jsx("span", { className: "typography-small text-muted-foreground", children: status.text }), product.quantity != null && product.quantity < 0 && (_jsx("span", { className: "typography-tiny text-muted-foreground", children: "(Negative inventory tracked)" }))] }), isPresent(product.tags) && (_jsx("div", { className: "flex flex-wrap gap-1", children: product.tags.map((tag) => (_jsx(Badge, { variant: "secondary", className: "typography-tiny", children: tag }, tag))) })), variant?.cost === 0 && (_jsxs("div", { className: cn("flex items-start gap-2 p-3 rounded-md", "bg-warning", "border border-muted-border"), children: [_jsx(AlertCircle, { className: "size-4 text-warning-foreground mt-0.5" }), _jsxs("div", { className: "flex-1 typography-small", children: [_jsx("p", { className: "typography-small-medium text-warning-foreground", children: "Missing cost data" }), _jsx("p", { className: "text-warning-foreground/80 typography-tiny mt-0.5", children: "Add cost to track profit margins" })] })] })), product.url && (_jsxs("a", { href: product.url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 typography-small text-primary hover:underline", children: ["View in Shopify", _jsx(ExternalLink, { className: "size-3.5" })] }))] })] }));
18
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(ShoppingBag, { className: "size-5 text-muted-foreground mt-0.5" }), _jsxs("div", { className: "flex-1", children: [_jsx(CardTitle, { className: "typography-body", children: product.name }), product.vendor && (_jsxs("div", { className: "flex items-center gap-1 mt-1 typography-small text-muted-foreground", children: [_jsx(User, { className: "size-3.5" }), _jsx("span", { children: product.vendor })] }))] })] }) }), _jsxs(CardContent, { className: "space-y-3", children: [variant?.price !== null && variant?.price !== undefined && (_jsx("div", { className: "typography-large-body-semibold font-mono", children: formatMetric(variant.price, "currency", variant.currency) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `size-2 rounded-full ${status.color}` }), _jsx("span", { className: "typography-small text-muted-foreground", children: status.text }), product.quantity != null && product.quantity < 0 && (_jsx("span", { className: "typography-tiny text-muted-foreground", children: "(Negative inventory tracked)" }))] }), isPresent(product.tags) && (_jsx("div", { className: "flex flex-wrap gap-1", children: product.tags.map((tag) => (_jsx(Badge, { variant: "secondary", className: "typography-tiny", children: tag }, tag))) })), variant?.cost === 0 && (_jsxs("div", { className: cn("flex items-start gap-2 p-3 rounded-md", "bg-warning", "border border-muted-border"), children: [_jsx(AlertCircle, { className: "size-4 text-warning-foreground mt-0.5" }), _jsxs("div", { className: "flex-1 typography-small", children: [_jsx("p", { className: "typography-small-medium text-warning-foreground", children: "Missing cost data" }), _jsx("p", { className: "text-warning-foreground/80 typography-tiny mt-0.5", children: "Add cost to track profit margins" })] })] })), product.url && (_jsxs("a", { href: product.url, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1 typography-small text-primary hover:underline", children: ["View in Shopify", _jsx(ExternalLink, { className: "size-3.5" })] }))] })] }));
19
19
  }
@@ -53,5 +53,5 @@ export function ProductVariantsTable(props) {
53
53
  return null;
54
54
  const totalInventory = data.variants.reduce((sum, v) => sum + (v.inventory || 0), 0);
55
55
  const avgMargin = data.variants.reduce((sum, v) => sum + (v.marginPct || 0), 0) / data.variants.length;
56
- return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Package, title: `Product Variants (${data.variants.length})` }), _jsxs(CardContent, { className: "pt-4", children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[200px]", children: "Variant Name" }), _jsx(TableHead, { children: "SKU" }), _jsx(TableHead, { children: "Price" }), _jsx(TableHead, { children: "Cost" }), _jsx(TableHead, { className: "w-[200px]", children: "Margin %" }), _jsx(TableHead, { children: "Inventory" })] }) }), _jsx(TableBody, { children: data.variants.map((variant) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsx("div", { className: "typography-small-medium", children: variant.variantName || "Default" }), variant.productName && (_jsx("div", { className: "typography-tiny text-muted-foreground", children: variant.productName }))] }), _jsx(TableCell, { className: "font-mono typography-tiny text-muted-foreground", children: variant.sku || "—" }), _jsx(TableCell, { className: "font-mono", children: isNotNil(variant.price) ? (_jsxs(_Fragment, { children: [variant.compareAtPrice && variant.compareAtPrice > variant.price && (_jsx("div", { className: "typography-tiny text-muted-foreground line-through", children: formatMetric(variant.compareAtPrice, "currency") })), _jsx("div", { children: formatMetric(variant.price, "currency") })] })) : ("—") }), _jsx(TableCell, { className: "font-mono", children: isNotNil(variant.cost) ? formatMetric(variant.cost, "currency") : "—" }), _jsx(TableCell, { children: isNotNil(variant.marginPct) ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Circle, { className: cn("size-2", getMarginIconColor(variant.marginPct)) }), _jsxs("span", { className: cn("font-mono typography-small-semibold", getMarginColor(variant.marginPct)), children: [variant.marginPct.toFixed(0), "%"] }), _jsx("div", { className: "flex-1 h-2 bg-muted rounded-full overflow-hidden", children: _jsx("div", { className: cn("h-full transition-all", getMarginBgColor(variant.marginPct)), style: { width: `${Math.min(variant.marginPct, 100)}%` } }) })] })) : ("—") }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Circle, { className: cn("size-2", getStockIconColor(variant.inventory)) }), _jsxs("div", { children: [_jsx("div", { className: "font-mono typography-small-semibold", children: variant.inventory !== null ? variant.inventory : "—" }), variant.inventory !== null && (_jsx("div", { className: "typography-tiny text-muted-foreground", children: getStockText(variant.inventory) }))] })] }) })] }, variant.uri))) })] }), _jsx(InsightCardFooter, { left: `${data.variants.length} variants · Avg margin: ${avgMargin.toFixed(1)}%`, right: `Total inventory: ${totalInventory} units` })] })] }));
56
+ return (_jsxs(Card, { className: "shadow-none gap-4 py-4", children: [_jsx(InsightCardHeader, { icon: Package, title: `Product Variants (${data.variants.length})` }), _jsxs(CardContent, { className: "pt-4", children: [_jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[200px]", children: "Variant Name" }), _jsx(TableHead, { children: "SKU" }), _jsx(TableHead, { children: "Price" }), _jsx(TableHead, { children: "Cost" }), _jsx(TableHead, { className: "w-[200px]", children: "Margin %" }), _jsx(TableHead, { children: "Inventory" })] }) }), _jsx(TableBody, { children: data.variants.map((variant) => (_jsxs(TableRow, { children: [_jsxs(TableCell, { children: [_jsx("div", { className: "typography-small-medium", children: variant.variantName || "Default" }), variant.productName && (_jsx("div", { className: "typography-tiny text-muted-foreground", children: variant.productName }))] }), _jsx(TableCell, { className: "font-mono typography-tiny text-muted-foreground", children: variant.sku || "—" }), _jsx(TableCell, { className: "font-mono", children: isNotNil(variant.price) ? (_jsxs(_Fragment, { children: [variant.compareAtPrice && variant.compareAtPrice > variant.price && (_jsx("div", { className: "typography-tiny text-muted-foreground line-through", children: formatMetric(variant.compareAtPrice, "currency", variant.currency) })), _jsx("div", { children: formatMetric(variant.price, "currency", variant.currency) })] })) : ("—") }), _jsx(TableCell, { className: "font-mono", children: isNotNil(variant.cost) ? formatMetric(variant.cost, "currency", variant.currency) : "—" }), _jsx(TableCell, { children: isNotNil(variant.marginPct) ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Circle, { className: cn("size-2", getMarginIconColor(variant.marginPct)) }), _jsxs("span", { className: cn("font-mono typography-small-semibold", getMarginColor(variant.marginPct)), children: [variant.marginPct.toFixed(0), "%"] }), _jsx("div", { className: "flex-1 h-2 bg-muted rounded-full overflow-hidden", children: _jsx("div", { className: cn("h-full transition-all", getMarginBgColor(variant.marginPct)), style: { width: `${Math.min(variant.marginPct, 100)}%` } }) })] })) : ("—") }), _jsx(TableCell, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Circle, { className: cn("size-2", getStockIconColor(variant.inventory)) }), _jsxs("div", { children: [_jsx("div", { className: "font-mono typography-small-semibold", children: variant.inventory !== null ? variant.inventory : "—" }), variant.inventory !== null && (_jsx("div", { className: "typography-tiny text-muted-foreground", children: getStockText(variant.inventory) }))] })] }) })] }, variant.uri))) })] }), _jsx(InsightCardFooter, { left: `${data.variants.length} variants · Avg margin: ${avgMargin.toFixed(1)}%`, right: `Total inventory: ${totalInventory} units` })] })] }));
57
57
  }
@@ -10,7 +10,7 @@ export declare function useExtractedInsights(jsonLd: JsonLdGraphDocument): {
10
10
  productVariants: import("../../lib/rdf/types.js").ProductVariantsData | null;
11
11
  campaignGrid: import("../../lib/rdf/types.js").CampaignGridData | null;
12
12
  product: import("../../lib/rdf/types.js").Product | null;
13
- kpis: import("../../lib/rdf/types.js").CompanyKpis | null;
13
+ kpis: import("../../lib/rdf/types.js").KpiSnapshot[] | null;
14
14
  entityRelationTree: import("./EntityRelationTreeInsight.js").EntityRelationTreeRoot[];
15
15
  hasExtractableInsights: boolean;
16
16
  };
@@ -4,7 +4,7 @@ import { m, meta } from "@manta-eu/rdf-lib/namespaces";
4
4
  import { flatten, uniq } from "ramda";
5
5
  import { useMemo } from "react";
6
6
  import { extractEntityRelationTreeRoots } from "./extractEntityRelationTreeRoots.js";
7
- import { extractCampaignChannelMix, extractCampaignGrid, extractCampaignPerformanceSummary, extractCompanyKpis, extractDailyPerformance, extractMetaDailyInsights, extractProduct, extractProductProfitability, extractProductSalesRanking, extractProductVariants, extractRoasBenchmark, } from "#lib/rdf/index";
7
+ import { extractCampaignChannelMix, extractCampaignGrid, extractCampaignPerformanceSummary, extractDailyPerformance, extractKpiSnapshots, extractMetaDailyInsights, extractProduct, extractProductProfitability, extractProductSalesRanking, extractProductVariants, extractRoasBenchmark, } from "#lib/rdf/index";
8
8
  const typeToExtractors = {
9
9
  [m.ProductProfitability]: ["productProfitability"],
10
10
  [m.ProductSalesRanking]: ["productSalesRanking"],
@@ -15,7 +15,7 @@ const typeToExtractors = {
15
15
  [meta.Campaign]: ["campaignChannelMix", "campaignGrid"],
16
16
  [m.ProductVariant]: ["productVariants"],
17
17
  [m.Product]: ["product"],
18
- [m.CompanyKPISnapshot]: ["kpis"],
18
+ [m.KPISnapshot]: ["kpis"],
19
19
  };
20
20
  function collectTypes(jsonLd) {
21
21
  return new Set(flatten(jsonLd["@graph"].map((node) => getNodeTypes(node).map(expandPrefix))));
@@ -45,7 +45,7 @@ export function useExtractedInsights(jsonLd) {
45
45
  const productVariants = requiredExtractors.has("productVariants") ? extractProductVariants(jsonLd) : null;
46
46
  const campaignGrid = requiredExtractors.has("campaignGrid") ? extractCampaignGrid(jsonLd) : null;
47
47
  const product = requiredExtractors.has("product") ? extractProduct(jsonLd) : null;
48
- const kpis = requiredExtractors.has("kpis") ? extractCompanyKpis(jsonLd) : null;
48
+ const kpis = requiredExtractors.has("kpis") ? extractKpiSnapshots(jsonLd) : null;
49
49
  // Candidates only — EntityRelationTreeInsight filters against live connected-account IRIs.
50
50
  const entityRelationTree = extractEntityRelationTreeRoots(jsonLd);
51
51
  const hasExtractableInsights = productProfitability ||
@@ -28,6 +28,7 @@ export declare function canPickDatastore(args: {
28
28
  canSwitchDatastore?: boolean;
29
29
  canSelect: boolean;
30
30
  datastores?: readonly Datastore[];
31
+ isOperator?: boolean;
31
32
  }): boolean;
32
33
  export type DatastorePickerRowsArgs = {
33
34
  isOperator?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"datastore-picker-rows.d.ts","sourceRoot":"","sources":["../../src/datastore-picker/datastore-picker-rows.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,QAAgB,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuHF,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAO7E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;CACnC,GAAG,OAAO,CAIV;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC9C,CAAC;AAGF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,kBAAkB,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAU9G;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,kBAAkB,EAAE,CAsBvF"}
1
+ {"version":3,"file":"datastore-picker-rows.d.ts","sourceRoot":"","sources":["../../src/datastore-picker/datastore-picker-rows.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,gBAAgB,QAAgB,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAuHF,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,GAAG,OAAO,CAO7E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAOV;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,SAAS,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC9C,CAAC;AAGF,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,kBAAkB,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAU9G;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,kBAAkB,EAAE,CAsBvF"}
@@ -107,8 +107,12 @@ export function hasDatastoreChoice(datastores) {
107
107
  return only !== undefined && hasAncestorAbove(list, only);
108
108
  }
109
109
  export function canPickDatastore(args) {
110
- const { canSwitchDatastore, canSelect, datastores } = args;
111
- return !!canSwitchDatastore && canSelect && hasDatastoreChoice(datastores);
110
+ const { canSwitchDatastore, canSelect, datastores, isOperator } = args;
111
+ if (!canSwitchDatastore || !canSelect)
112
+ return false;
113
+ // An operator picks from the datastore tree, so there is no list here to count, and their right
114
+ // to switch is already settled by canSwitchDatastore.
115
+ return isOperator === true || hasDatastoreChoice(datastores);
112
116
  }
113
117
  // Below this the search box is more clutter than help.
114
118
  export const searchThreshold = 7;
@@ -6,7 +6,7 @@ import { memo } from "react";
6
6
  import { OpenInPlatformLink } from "../OpenInPlatformLink.js";
7
7
  import { EntityCardShell } from "./EntityCardShell.js";
8
8
  import { formatBidStrategy, formatDate, formatNetworksSummary, getChannelDescriptor, googleRed, statusColors, } from "./google-campaign-utils.js";
9
- import { readEntityIri, readVocabString } from "./typed-node-helpers.js";
9
+ import { readEntityIri, readSchemaOrgString } from "./typed-node-helpers.js";
10
10
  import { useEntity } from "#hooks/use-entity";
11
11
  import { formatRelativeUpdated } from "#lib/format-date";
12
12
  function row(Icon, text) {
@@ -19,7 +19,7 @@ export const GoogleCampaignView = memo(function GoogleCampaignView(props) {
19
19
  const { data, selected = false, ringColor, onContextMenu } = props;
20
20
  const iri = readEntityIri(data.jsonLd);
21
21
  const { data: typed } = useEntity({ schema: GoogleCampaignCardSchema, iri });
22
- const currency = readVocabString(data.jsonLd, "currency");
22
+ const currency = readSchemaOrgString(data.jsonLd, "currency");
23
23
  const statusKey = typed?.campaignStatus?.toString().toLowerCase() ?? "";
24
24
  const statusColor = statusColors[statusKey] ?? "#6b7280";
25
25
  const servingStatus = typed?.campaignServingStatus?.toString();
@@ -109,7 +109,7 @@ function integrationsFilterClause(integrations) {
109
109
  function pendingApprovalGraphsSubquery() {
110
110
  return `{ SELECT DISTINCT ?graph WHERE {
111
111
  GRAPH ?graph {
112
- ?manualAction <${m.isManualAction}> true .
112
+ ?manualAction a <${m.ManualAction}> .
113
113
  OPTIONAL { ?manualAction <${m.status}> ?manualActionStatus . }
114
114
  }
115
115
  FILTER (!BOUND(?manualActionStatus) || UCASE(REPLACE(STR(?manualActionStatus), "^.*[#/]", "")) = "PENDING")
@@ -0,0 +1,10 @@
1
+ import type { ChatInputProps } from "#components/chat/chat-input/index";
2
+ import type { ChatDraft } from "#lib/chat-types";
3
+ type ComposerSubmit = NonNullable<ChatInputProps["onSubmit"]>;
4
+ export declare function useComposerDraft(initialDraft: ChatDraft | undefined, submit: ComposerSubmit): {
5
+ key: string;
6
+ initialValue: string | undefined;
7
+ onSubmit: (value: string, files: File[]) => void | Promise<void>;
8
+ };
9
+ export {};
10
+ //# sourceMappingURL=use-composer-draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-composer-draft.d.ts","sourceRoot":"","sources":["../../src/hooks/use-composer-draft.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,KAAK,cAAc,GAAG,WAAW,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;AAO9D,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,SAAS,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc;;;sBAKhF,MAAM,SAAS,IAAI,EAAE;EAYhC"}
@@ -0,0 +1,20 @@
1
+ import { useCallback, useState } from "react";
2
+ // Module scope, so a remount of the whole chat (startup gate, datastore switch, the host moving
3
+ // its mount) does not bring back a draft the host still passes after it was sent.
4
+ const sentDraftIds = new Set();
5
+ // Props for `ChatInput`: a new draft `id` remounts it with the text, an undefined draft keeps it.
6
+ export function useComposerDraft(initialDraft, submit) {
7
+ const [seeded, setSeeded] = useState(initialDraft);
8
+ if (initialDraft && initialDraft.id !== seeded?.id)
9
+ setSeeded(initialDraft);
10
+ const onSubmit = useCallback((value, files) => {
11
+ if (seeded)
12
+ sentDraftIds.add(seeded.id);
13
+ return submit(value, files);
14
+ }, [seeded, submit]);
15
+ return {
16
+ key: seeded?.id ?? "composer",
17
+ initialValue: seeded && !sentDraftIds.has(seeded.id) ? seeded.text : undefined,
18
+ onSubmit,
19
+ };
20
+ }
@@ -1,5 +1,8 @@
1
- export declare const manualActionStatuses: readonly ["PENDING", "COMPLETED", "REJECTED"];
1
+ import { MantaManualActionSchema } from "@manta-eu/ldo";
2
+ import type { LensEntity } from "./lens-entity.js";
3
+ export declare const manualActionStatuses: readonly ["PENDING", "COMPLETED", "REJECTED", "RETRY"];
2
4
  export type ManualActionStatus = (typeof manualActionStatuses)[number];
5
+ export type ManualActionResolution = Exclude<ManualActionStatus, "PENDING">;
3
6
  export type ManualAction = {
4
7
  subjectIri: string;
5
8
  subjectLabel: string | null;
@@ -7,12 +10,19 @@ export type ManualAction = {
7
10
  typeName: string;
8
11
  status: ManualActionStatus | null;
9
12
  assignees: string[];
13
+ description?: string | null;
14
+ aboutIri?: string | null;
15
+ retryable?: boolean;
16
+ defaultResolution?: "COMPLETED" | "RETRY" | null;
10
17
  };
11
18
  export type UseManualActionsOptions = {
12
19
  planGraphIri: string;
13
20
  enabled?: boolean;
14
21
  refetchIntervalMs?: number | false;
15
22
  };
23
+ export type ManualActionEntity = LensEntity<typeof MantaManualActionSchema>;
24
+ export declare function toManualAction(entity: ManualActionEntity): ManualAction | null;
25
+ export declare function toManualActions(entities: ManualActionEntity[]): ManualAction[];
16
26
  export declare function useManualActions(config: UseManualActionsOptions): import("@tanstack/react-query").UseQueryResult<ManualAction[], Error>;
17
27
  export declare function useInvalidateManualActions(): (planGraphIri: string) => Promise<void>;
18
28
  export type UseManualActionsForPlansOptions = {
@@ -1 +1 @@
1
- {"version":3,"file":"use-manual-actions.d.ts","sourceRoot":"","sources":["../../src/hooks/use-manual-actions.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,oBAAoB,+CAAgD,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AASvE,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC,CAAC;AAuDF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,yEAmB/D;AAED,wBAAgB,0BAA0B,KAEhC,cAAc,MAAM,mBAC7B;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC,CAAC;AAuCF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,+BAA+B,sFAqB/E"}
1
+ {"version":3,"file":"use-manual-actions.d.ts","sourceRoot":"","sources":["../../src/hooks/use-manual-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAMxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,eAAO,MAAM,oBAAoB,wDAAyD,CAAC;AAE3F,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvE,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC;AAa5E,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,WAAW,GAAG,OAAO,GAAG,IAAI,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE5E,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,GAAG,IAAI,CAiB9E;AAMD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,YAAY,EAAE,CAK9E;AAYD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,uBAAuB,yEAa/D;AAED,wBAAgB,0BAA0B,KAEhC,cAAc,MAAM,mBAC7B;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACpC,CAAC;AASF,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,+BAA+B,sFAc/E"}