@manta-eu/react 0.2.0 → 0.2.1

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 (235) hide show
  1. package/dist/components/channel-icon/index.d.ts +1 -0
  2. package/dist/components/channel-icon/index.d.ts.map +1 -1
  3. package/dist/components/channel-icon/index.js +1 -1
  4. package/dist/components/channel-icon/index.js.map +1 -1
  5. package/dist/components/planning/ActioningView.d.ts +1 -2
  6. package/dist/components/planning/ActioningView.d.ts.map +1 -1
  7. package/dist/components/planning/ActioningView.js +19 -44
  8. package/dist/components/planning/ActioningView.js.map +1 -1
  9. package/dist/components/planning/ManualActionCard.d.ts +4 -0
  10. package/dist/components/planning/ManualActionCard.d.ts.map +1 -1
  11. package/dist/components/planning/ManualActionCard.js +7 -5
  12. package/dist/components/planning/ManualActionCard.js.map +1 -1
  13. package/dist/components/planning/ManualActionList.d.ts +4 -0
  14. package/dist/components/planning/ManualActionList.d.ts.map +1 -1
  15. package/dist/components/planning/ManualActionList.js +8 -2
  16. package/dist/components/planning/ManualActionList.js.map +1 -1
  17. package/dist/components/planning/actioning-view-utils.d.ts +9 -0
  18. package/dist/components/planning/actioning-view-utils.d.ts.map +1 -1
  19. package/dist/components/planning/actioning-view-utils.js +19 -1
  20. package/dist/components/planning/actioning-view-utils.js.map +1 -1
  21. package/dist/components/tooltip/index.d.ts +7 -0
  22. package/dist/components/tooltip/index.d.ts.map +1 -1
  23. package/dist/components/tooltip/index.js +10 -1
  24. package/dist/components/tooltip/index.js.map +1 -1
  25. package/dist/globals.css +19 -6
  26. package/dist/graph/entity-link.d.ts +14 -0
  27. package/dist/graph/entity-link.d.ts.map +1 -0
  28. package/dist/graph/entity-link.js +12 -0
  29. package/dist/graph/entity-link.js.map +1 -0
  30. package/dist/graph/graph-list-utils.d.ts +1 -0
  31. package/dist/graph/graph-list-utils.d.ts.map +1 -1
  32. package/dist/graph/graph-list-utils.js +1 -0
  33. package/dist/graph/graph-list-utils.js.map +1 -1
  34. package/dist/graph/graph-model.d.ts.map +1 -1
  35. package/dist/graph/graph-model.js +14 -5
  36. package/dist/graph/graph-model.js.map +1 -1
  37. package/dist/graph/nodes/BrandAssetAnalysisView.d.ts.map +1 -1
  38. package/dist/graph/nodes/BrandAssetAnalysisView.js +2 -1
  39. package/dist/graph/nodes/BrandAssetAnalysisView.js.map +1 -1
  40. package/dist/graph/nodes/BrandFontSuggestionView.d.ts.map +1 -1
  41. package/dist/graph/nodes/BrandFontSuggestionView.js +2 -1
  42. package/dist/graph/nodes/BrandFontSuggestionView.js.map +1 -1
  43. package/dist/graph/nodes/BrandGuidelinesView.js +1 -1
  44. package/dist/graph/nodes/BrandGuidelinesView.js.map +1 -1
  45. package/dist/graph/nodes/NodePropertyTable.d.ts +4 -2
  46. package/dist/graph/nodes/NodePropertyTable.d.ts.map +1 -1
  47. package/dist/graph/nodes/NodePropertyTable.js +12 -59
  48. package/dist/graph/nodes/NodePropertyTable.js.map +1 -1
  49. package/dist/graph/nodes/node-property-row.d.ts +30 -0
  50. package/dist/graph/nodes/node-property-row.d.ts.map +1 -0
  51. package/dist/graph/nodes/node-property-row.js +35 -0
  52. package/dist/graph/nodes/node-property-row.js.map +1 -0
  53. package/dist/graph/property-formatters.d.ts +1 -0
  54. package/dist/graph/property-formatters.d.ts.map +1 -1
  55. package/dist/graph/property-formatters.js +4 -1
  56. package/dist/graph/property-formatters.js.map +1 -1
  57. package/dist/graph/special-nodes/AttributeList.d.ts +27 -0
  58. package/dist/graph/special-nodes/AttributeList.d.ts.map +1 -0
  59. package/dist/graph/special-nodes/AttributeList.js +57 -0
  60. package/dist/graph/special-nodes/AttributeList.js.map +1 -0
  61. package/dist/graph/special-nodes/EntityPreview.d.ts +5 -0
  62. package/dist/graph/special-nodes/EntityPreview.d.ts.map +1 -0
  63. package/dist/graph/special-nodes/EntityPreview.js +19 -0
  64. package/dist/graph/special-nodes/EntityPreview.js.map +1 -0
  65. package/dist/graph/special-nodes/EntityValueChip.d.ts +11 -0
  66. package/dist/graph/special-nodes/EntityValueChip.d.ts.map +1 -0
  67. package/dist/graph/special-nodes/EntityValueChip.js +40 -0
  68. package/dist/graph/special-nodes/EntityValueChip.js.map +1 -0
  69. package/dist/graph/special-nodes/GenericView.d.ts +2 -1
  70. package/dist/graph/special-nodes/GenericView.d.ts.map +1 -1
  71. package/dist/graph/special-nodes/GenericView.js +23 -69
  72. package/dist/graph/special-nodes/GenericView.js.map +1 -1
  73. package/dist/graph/special-nodes/card-facts.d.ts +52 -0
  74. package/dist/graph/special-nodes/card-facts.d.ts.map +1 -0
  75. package/dist/graph/special-nodes/card-facts.js +189 -0
  76. package/dist/graph/special-nodes/card-facts.js.map +1 -0
  77. package/dist/graph/special-nodes/property-icons.d.ts +9 -0
  78. package/dist/graph/special-nodes/property-icons.d.ts.map +1 -0
  79. package/dist/graph/special-nodes/property-icons.js +39 -0
  80. package/dist/graph/special-nodes/property-icons.js.map +1 -0
  81. package/dist/graph/status-utils.d.ts +16 -0
  82. package/dist/graph/status-utils.d.ts.map +1 -1
  83. package/dist/graph/status-utils.js +35 -0
  84. package/dist/graph/status-utils.js.map +1 -1
  85. package/dist/graph/types.d.ts +5 -0
  86. package/dist/graph/types.d.ts.map +1 -1
  87. package/dist/hooks/use-approve-anyway.d.ts +12 -0
  88. package/dist/hooks/use-approve-anyway.d.ts.map +1 -0
  89. package/dist/hooks/use-approve-anyway.js +81 -0
  90. package/dist/hooks/use-approve-anyway.js.map +1 -0
  91. package/dist/hooks/use-connection-problems.d.ts.map +1 -1
  92. package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
  93. package/dist/ldo-ui.d.ts +2 -1
  94. package/dist/ldo-ui.d.ts.map +1 -1
  95. package/dist/ldo-ui.js +1 -0
  96. package/dist/ldo-ui.js.map +1 -1
  97. package/dist/lib/manual-actions.d.ts +2 -1
  98. package/dist/lib/manual-actions.d.ts.map +1 -1
  99. package/dist/lib/manual-actions.js +3 -2
  100. package/dist/lib/manual-actions.js.map +1 -1
  101. package/dist/providers/ActiveChatProvider.js +3 -1
  102. package/dist/providers/ActiveChatProvider.js.map +1 -1
  103. package/dist/providers/ChatManagerProvider.d.ts.map +1 -1
  104. package/dist/providers/ChatManagerProvider.js +3 -1
  105. package/dist/providers/ChatManagerProvider.js.map +1 -1
  106. package/dist/providers/chat-socket-auth.d.ts +7 -2
  107. package/dist/providers/chat-socket-auth.d.ts.map +1 -1
  108. package/dist/providers/chat-socket-auth.js +87 -16
  109. package/dist/providers/chat-socket-auth.js.map +1 -1
  110. package/dist/providers/index.d.ts.map +1 -1
  111. package/dist/providers/index.js +8 -1
  112. package/dist/providers/index.js.map +1 -1
  113. package/dist/routes/activity/detail/ActivityChanges.d.ts +2 -0
  114. package/dist/routes/activity/detail/ActivityChanges.d.ts.map +1 -1
  115. package/dist/routes/activity/detail/ActivityChanges.js +8 -7
  116. package/dist/routes/activity/detail/ActivityChanges.js.map +1 -1
  117. package/dist/routes/activity/detail/ActivityDetail.d.ts +1 -0
  118. package/dist/routes/activity/detail/ActivityDetail.d.ts.map +1 -1
  119. package/dist/routes/activity/detail/ActivityDetail.js +5 -4
  120. package/dist/routes/activity/detail/ActivityDetail.js.map +1 -1
  121. package/dist/routes/activity/detail/ActivityStepsRail.d.ts +1 -0
  122. package/dist/routes/activity/detail/ActivityStepsRail.d.ts.map +1 -1
  123. package/dist/routes/activity/detail/ActivityStepsRail.js +3 -2
  124. package/dist/routes/activity/detail/ActivityStepsRail.js.map +1 -1
  125. package/dist/routes/activity/detail/ActivityTimeline.js +2 -2
  126. package/dist/routes/activity/detail/ActivityTimeline.js.map +1 -1
  127. package/dist/routes/activity/detail/NoChangesMessage.d.ts +6 -0
  128. package/dist/routes/activity/detail/NoChangesMessage.d.ts.map +1 -0
  129. package/dist/routes/activity/detail/NoChangesMessage.js +10 -0
  130. package/dist/routes/activity/detail/NoChangesMessage.js.map +1 -0
  131. package/dist/routes/data-explorer/components/RawTriples.js +1 -1
  132. package/dist/routes/data-explorer/components/RawTriples.js.map +1 -1
  133. package/dist/routes/data-explorer/components/RecentTypes.js +1 -1
  134. package/dist/routes/data-explorer/components/RecentTypes.js.map +1 -1
  135. package/dist/routes/data-explorer/components/class-detail/Instances.js +1 -1
  136. package/dist/routes/data-explorer/components/class-detail/Instances.js.map +1 -1
  137. package/dist/routes/data-explorer/components/namespace-detail/NamespaceClassRow.js +1 -1
  138. package/dist/routes/data-explorer/components/namespace-detail/NamespaceClassRow.js.map +1 -1
  139. package/dist/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.js +1 -1
  140. package/dist/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.js.map +1 -1
  141. package/dist/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.js +1 -1
  142. package/dist/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.js.map +1 -1
  143. package/dist/routes/data-explorer/components/record-detail/ChatPreview.js +2 -2
  144. package/dist/routes/data-explorer/components/record-detail/ChatPreview.js.map +1 -1
  145. package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.d.ts.map +1 -1
  146. package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.js +13 -6
  147. package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.js.map +1 -1
  148. package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts +3 -0
  149. package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts.map +1 -1
  150. package/dist/routes/integrations/components/connect-forms/data-connection-fields.js +9 -2
  151. package/dist/routes/integrations/components/connect-forms/data-connection-fields.js.map +1 -1
  152. package/dist/routes/plans/components/ActionPlanPage.d.ts.map +1 -1
  153. package/dist/routes/plans/components/ActionPlanPage.js +4 -7
  154. package/dist/routes/plans/components/ActionPlanPage.js.map +1 -1
  155. package/dist/routes/plans/components/PlanDetailRouteContent.d.ts.map +1 -1
  156. package/dist/routes/plans/components/PlanDetailRouteContent.js +2 -1
  157. package/dist/routes/plans/components/PlanDetailRouteContent.js.map +1 -1
  158. package/package.json +12 -7
  159. package/src/app-subpaths.json +1 -0
  160. package/src/components/channel-icon/index.tsx +1 -1
  161. package/src/components/planning/ActioningView.tsx +62 -70
  162. package/src/components/planning/ManualActionCard.tsx +66 -14
  163. package/src/components/planning/ManualActionList.tsx +30 -11
  164. package/src/components/planning/actioning-view-utils.ts +27 -1
  165. package/src/components/tooltip/index.tsx +35 -2
  166. package/src/graph/entity-link.tsx +20 -0
  167. package/src/graph/graph-list-utils.ts +2 -0
  168. package/src/graph/graph-model.ts +15 -6
  169. package/src/graph/nodes/BrandAssetAnalysisView.tsx +3 -1
  170. package/src/graph/nodes/BrandFontSuggestionView.tsx +7 -1
  171. package/src/graph/nodes/BrandGuidelinesView.tsx +1 -1
  172. package/src/graph/nodes/NodePropertyTable.tsx +32 -143
  173. package/src/graph/nodes/node-property-row.tsx +154 -0
  174. package/src/graph/property-formatters.ts +5 -1
  175. package/src/graph/special-nodes/AttributeList.tsx +177 -0
  176. package/src/graph/special-nodes/EntityPreview.tsx +27 -0
  177. package/src/graph/special-nodes/EntityValueChip.tsx +85 -0
  178. package/src/graph/special-nodes/GenericView.tsx +49 -139
  179. package/src/graph/special-nodes/card-facts.ts +278 -0
  180. package/src/graph/special-nodes/property-icons.ts +59 -0
  181. package/src/graph/status-utils.ts +40 -0
  182. package/src/graph/types.ts +4 -0
  183. package/src/hooks/use-approve-anyway.ts +100 -0
  184. package/src/ldo-ui.ts +8 -0
  185. package/src/lib/manual-actions.ts +3 -2
  186. package/src/providers/ActiveChatProvider.tsx +3 -1
  187. package/src/providers/ChatManagerProvider.tsx +3 -1
  188. package/src/providers/chat-socket-auth.ts +95 -16
  189. package/src/providers/index.tsx +16 -1
  190. package/src/routes/activity/detail/ActivityChanges.tsx +21 -5
  191. package/src/routes/activity/detail/ActivityDetail.tsx +24 -5
  192. package/src/routes/activity/detail/ActivityStepsRail.tsx +4 -2
  193. package/src/routes/activity/detail/ActivityTimeline.tsx +2 -2
  194. package/src/routes/activity/detail/NoChangesMessage.tsx +20 -0
  195. package/src/routes/data-explorer/components/RawTriples.tsx +1 -1
  196. package/src/routes/data-explorer/components/RecentTypes.tsx +1 -1
  197. package/src/routes/data-explorer/components/class-detail/Instances.tsx +1 -1
  198. package/src/routes/data-explorer/components/namespace-detail/NamespaceClassRow.tsx +1 -1
  199. package/src/routes/data-explorer/components/ontology-coverage/CoverageTermColumns.tsx +1 -1
  200. package/src/routes/data-explorer/components/ontology-coverage/UnobservedRuleVocabulary.tsx +1 -1
  201. package/src/routes/data-explorer/components/record-detail/ChatPreview.tsx +2 -2
  202. package/src/routes/integrations/components/connect-forms/DataConnectionConnectForm.tsx +38 -12
  203. package/src/routes/integrations/components/connect-forms/data-connection-fields.ts +11 -2
  204. package/src/routes/plans/components/ActionPlanPage.tsx +9 -8
  205. package/src/routes/plans/components/PlanDetailRouteContent.tsx +4 -3
  206. package/dist/components/planning/ExecutionActivityRow.d.ts +0 -9
  207. package/dist/components/planning/ExecutionActivityRow.d.ts.map +0 -1
  208. package/dist/components/planning/ExecutionActivityRow.js +0 -53
  209. package/dist/components/planning/ExecutionActivityRow.js.map +0 -1
  210. package/dist/components/planning/ExecutionSectionItem.d.ts +0 -20
  211. package/dist/components/planning/ExecutionSectionItem.d.ts.map +0 -1
  212. package/dist/components/planning/ExecutionSectionItem.js +0 -66
  213. package/dist/components/planning/ExecutionSectionItem.js.map +0 -1
  214. package/dist/components/planning/ExecutionSections.d.ts +0 -16
  215. package/dist/components/planning/ExecutionSections.d.ts.map +0 -1
  216. package/dist/components/planning/ExecutionSections.js +0 -32
  217. package/dist/components/planning/ExecutionSections.js.map +0 -1
  218. package/dist/components/planning/action-plan-icons.d.ts +0 -3
  219. package/dist/components/planning/action-plan-icons.d.ts.map +0 -1
  220. package/dist/components/planning/action-plan-icons.js +0 -25
  221. package/dist/components/planning/action-plan-icons.js.map +0 -1
  222. package/dist/components/planning/execution-section-model.d.ts +0 -24
  223. package/dist/components/planning/execution-section-model.d.ts.map +0 -1
  224. package/dist/components/planning/execution-section-model.js +0 -150
  225. package/dist/components/planning/execution-section-model.js.map +0 -1
  226. package/dist/routes/data-explorer/components/TruncateTooltip.d.ts +0 -7
  227. package/dist/routes/data-explorer/components/TruncateTooltip.d.ts.map +0 -1
  228. package/dist/routes/data-explorer/components/TruncateTooltip.js +0 -9
  229. package/dist/routes/data-explorer/components/TruncateTooltip.js.map +0 -1
  230. package/src/components/planning/ExecutionActivityRow.tsx +0 -89
  231. package/src/components/planning/ExecutionSectionItem.tsx +0 -184
  232. package/src/components/planning/ExecutionSections.tsx +0 -97
  233. package/src/components/planning/action-plan-icons.tsx +0 -27
  234. package/src/components/planning/execution-section-model.ts +0 -190
  235. package/src/routes/data-explorer/components/TruncateTooltip.tsx +0 -25
@@ -1,89 +0,0 @@
1
- import { humanDuration } from "@manta-eu/lib/humanize";
2
- import { AlertCircle, Check, ClockFading, Loader2, X } from "lucide-react";
3
- import { useEffect, useState } from "react";
4
- import { match } from "ts-pattern";
5
- import { parseTimestampMs, type ResolvedManualActionStatus } from "./execution-section-model";
6
- import type { ExecutionProvenanceActivity } from "#ldo-ui";
7
- import { cn } from "#lib/utils";
8
-
9
- type ExecutionActivityRowProps = {
10
- activity: ExecutionProvenanceActivity;
11
- manualActionStatus?: ResolvedManualActionStatus | null;
12
- };
13
-
14
- type ActivityIconState = "error" | "rejected" | "completed" | "running" | "pending";
15
-
16
- function resolveIconState(
17
- activity: ExecutionProvenanceActivity,
18
- manualActionStatus: ResolvedManualActionStatus | null,
19
- ): ActivityIconState {
20
- if (activity.errorMessage) return "error";
21
- if (manualActionStatus === "REJECTED") return "rejected";
22
- if (manualActionStatus === "COMPLETED" || !!activity.endedAtTime) return "completed";
23
- if (activity.startedAtTime) return "running";
24
- return "pending";
25
- }
26
-
27
- export function ExecutionActivityRow(props: ExecutionActivityRowProps) {
28
- const { activity, manualActionStatus = null } = props;
29
- const iconState = resolveIconState(activity, manualActionStatus);
30
- const hasError = iconState === "error";
31
- const isPending = iconState === "pending";
32
-
33
- return (
34
- <div className={cn("flex items-center justify-between pl-5 pr-3 py-2", hasError && "bg-error/10")}>
35
- <div className="flex items-center gap-3 min-w-0">
36
- {match(iconState)
37
- .with("error", () => <AlertCircle className="size-4 shrink-0 text-error-foreground" />)
38
- .with("rejected", () => <X className="size-4 shrink-0 text-error-foreground" />)
39
- .with("completed", () => <Check className="size-4 shrink-0 text-success-foreground" />)
40
- .with("running", () => <Loader2 className="size-4 shrink-0 text-muted-foreground animate-spin" />)
41
- .with("pending", () => <ClockFading className="size-4 shrink-0 text-muted-foreground/40" />)
42
- .exhaustive()}
43
- <span
44
- className={cn(
45
- "text-[12px] font-mono leading-[18px] truncate",
46
- hasError ? "text-error-foreground" : isPending ? "text-muted-foreground" : "text-default-foreground",
47
- )}
48
- >
49
- {activity.errorMessage ?? activity.entityLabel}
50
- </span>
51
- </div>
52
- <ActivityElapsed activity={activity} hasError={hasError} isPending={isPending} />
53
- </div>
54
- );
55
- }
56
-
57
- function ActivityElapsed(props: { activity: ExecutionProvenanceActivity; hasError: boolean; isPending: boolean }) {
58
- const { activity, hasError, isPending } = props;
59
- const isRunning = !!activity.startedAtTime && !activity.endedAtTime;
60
- const [now, setNow] = useState(() => Date.now());
61
-
62
- useEffect(() => {
63
- if (!isRunning) return;
64
- const id = setInterval(() => setNow(Date.now()), 1000);
65
- return () => clearInterval(id);
66
- }, [isRunning]);
67
-
68
- if (!activity.startedAtTime) {
69
- return <span className="text-[12px] font-mono leading-[18px] shrink-0 ml-3 text-muted-foreground">-</span>;
70
- }
71
-
72
- const start = parseTimestampMs(activity.startedAtTime);
73
- if (start === null) {
74
- return <span className="text-[12px] font-mono leading-[18px] shrink-0 ml-3 text-muted-foreground">-</span>;
75
- }
76
- const end = parseTimestampMs(activity.endedAtTime) ?? now;
77
- const label = humanDuration({ start, end, largest: 2 }) || "0 seconds";
78
-
79
- return (
80
- <span
81
- className={cn(
82
- "text-[12px] font-mono leading-[18px] shrink-0 ml-3",
83
- hasError ? "text-error-foreground" : isPending ? "text-muted-foreground" : "text-default-foreground",
84
- )}
85
- >
86
- {label}
87
- </span>
88
- );
89
- }
@@ -1,184 +0,0 @@
1
- import { humanDuration } from "@manta-eu/lib/humanize";
2
- import { isPresent } from "@manta-eu/lib/present";
3
- import { AlertCircle, Check, ChevronDown, ClockFading, Loader2 } from "lucide-react";
4
- import { isNotNil } from "ramda";
5
- import { useEffect, useState } from "react";
6
- import { match } from "ts-pattern";
7
- import { resolveStepIcon } from "./action-plan-icons";
8
- import type { EntityTypeEntry } from "./ActionPlanDetailView";
9
- import { findResolvedManualActionStatus, parseTimestampMs, type SectionStatus } from "./execution-section-model";
10
- import { ExecutionActivityRow } from "./ExecutionActivityRow";
11
- import { ManualActionCard, type ManualActionTarget } from "./ManualActionCard";
12
- import type { ExecutionProvenanceActivity, ManualAction, ManualActionResolution, ManualActionResolving } from "#ldo-ui";
13
- import { getPendingManualActions, getResolvedManualActions } from "#lib/manual-actions";
14
- import { cn } from "#lib/utils";
15
-
16
- export type ExecutionSectionItemProps = {
17
- entry: EntityTypeEntry;
18
- activities: ExecutionProvenanceActivity[];
19
- status: SectionStatus;
20
- fallbackStartedAtMs?: number | null;
21
- isExpanded: boolean;
22
- onToggle: () => void;
23
- manualActions?: ManualAction[];
24
- manualActionEntities?: Map<string, ManualActionTarget[]>;
25
- onManualActionResolve?: (subjectIri: string, resolution: ManualActionResolution) => void;
26
- resolvingManualAction?: ManualActionResolving;
27
- canResolveManualAction?: boolean;
28
- manualActionDisabledReason?: string;
29
- };
30
-
31
- export function ExecutionSectionItem(props: ExecutionSectionItemProps) {
32
- const {
33
- entry,
34
- activities,
35
- status,
36
- fallbackStartedAtMs = null,
37
- isExpanded,
38
- onToggle,
39
- manualActions = [],
40
- manualActionEntities,
41
- onManualActionResolve,
42
- resolvingManualAction,
43
- canResolveManualAction,
44
- manualActionDisabledReason,
45
- } = props;
46
-
47
- const pendingManualActions = getPendingManualActions(manualActions);
48
- const resolvedManualActions = getResolvedManualActions(manualActions);
49
- const hasContent = isPresent(activities) || isPresent(pendingManualActions);
50
-
51
- return (
52
- <div>
53
- <button
54
- type="button"
55
- onClick={onToggle}
56
- className="flex w-full items-center gap-3 pl-2 pr-3 py-3 text-left transition-colors hover:bg-muted/30"
57
- >
58
- <div className="flex shrink-0 items-center p-1.5 rounded-lg">
59
- <ChevronDown
60
- className={cn("size-5 shrink-0 text-muted-foreground transition-transform", !isExpanded && "-rotate-90")}
61
- />
62
- </div>
63
-
64
- <div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-faint">
65
- {resolveStepIcon(entry.typeName, "size-5 text-muted-foreground")}
66
- </div>
67
-
68
- <div className="flex-1 min-w-0 flex flex-col gap-0.5">
69
- <span
70
- className={cn(
71
- "typography-small-medium block truncate",
72
- status === "pending" ? "text-muted-foreground" : "text-default-foreground",
73
- )}
74
- >
75
- {entry.typeName}
76
- </span>
77
- {entry.description && (
78
- <span className="typography-tiny text-muted-foreground block truncate">{entry.description}</span>
79
- )}
80
- </div>
81
-
82
- <div className="flex items-center gap-3 shrink-0">
83
- <SectionElapsed
84
- activities={activities}
85
- status={status}
86
- estimatedTime={entry.estimatedTime ?? null}
87
- fallbackStartedAtMs={fallbackStartedAtMs}
88
- />
89
- <SectionStatusIcon status={status} />
90
- </div>
91
- </button>
92
-
93
- {isExpanded && hasContent && (
94
- <div className="bg-faint border-t border-default-border py-1.5">
95
- {activities.map((activity) => (
96
- <ExecutionActivityRow
97
- key={activity.iri}
98
- activity={activity}
99
- manualActionStatus={findResolvedManualActionStatus(activity, resolvedManualActions)}
100
- />
101
- ))}
102
-
103
- {pendingManualActions.map((ma) => (
104
- <ManualActionCard
105
- key={ma.subjectIri}
106
- manualAction={ma}
107
- entities={manualActionEntities?.get(ma.subjectIri) ?? []}
108
- description={ma.description ?? entry.description ?? undefined}
109
- resolving={resolvingManualAction}
110
- onResolve={(resolution) => onManualActionResolve?.(ma.subjectIri, resolution)}
111
- canResolve={canResolveManualAction}
112
- disabledReason={manualActionDisabledReason}
113
- />
114
- ))}
115
- </div>
116
- )}
117
- </div>
118
- );
119
- }
120
-
121
- function SectionStatusIcon(props: { status: SectionStatus }) {
122
- return match(props.status)
123
- .with("completed", () => (
124
- <div className="size-5 flex items-center justify-center rounded-full bg-success">
125
- <Check className="size-3 text-success-foreground" />
126
- </div>
127
- ))
128
- .with("running", () => <Loader2 className="size-5 text-muted-foreground animate-spin" />)
129
- .with("failed", () => (
130
- <div className="size-5 flex items-center justify-center rounded-full bg-error">
131
- <AlertCircle className="size-3 text-error-foreground" />
132
- </div>
133
- ))
134
- .with("pending", () => <ClockFading className="size-5 text-muted-foreground/40" />)
135
- .exhaustive();
136
- }
137
-
138
- function SectionElapsed(props: {
139
- activities: ExecutionProvenanceActivity[];
140
- status: SectionStatus;
141
- estimatedTime: string | null;
142
- fallbackStartedAtMs?: number | null;
143
- }) {
144
- const { activities, status, estimatedTime, fallbackStartedAtMs = null } = props;
145
- const isRunning = status === "running";
146
- const [now, setNow] = useState(() => Date.now());
147
-
148
- useEffect(() => {
149
- if (!isRunning) return;
150
- const id = setInterval(() => setNow(Date.now()), 1000);
151
- return () => clearInterval(id);
152
- }, [isRunning]);
153
-
154
- const started = activities
155
- .map((a) => a.startedAtTime)
156
- .filter(isNotNil)
157
- .sort();
158
- if (!isPresent(started)) {
159
- if (fallbackStartedAtMs !== null) {
160
- return (
161
- <span className="typography-tiny text-muted-foreground whitespace-nowrap">
162
- {humanDuration({ start: fallbackStartedAtMs, end: now, largest: 2 }) || "0 seconds"}
163
- </span>
164
- );
165
- }
166
- if (estimatedTime) {
167
- return <span className="typography-tiny text-muted-foreground/60 whitespace-nowrap">{estimatedTime}</span>;
168
- }
169
- return null;
170
- }
171
-
172
- const ended = activities
173
- .map((a) => a.endedAtTime)
174
- .filter(isNotNil)
175
- .sort()
176
- .reverse();
177
- const start = parseTimestampMs(started[0]);
178
- if (start === null) return null;
179
- const end = isPresent(ended) && !isRunning ? parseTimestampMs(ended[0]) : now;
180
- if (end === null) return null;
181
- const label = humanDuration({ start, end, largest: 2 }) || "0 seconds";
182
-
183
- return <span className="typography-tiny text-muted-foreground whitespace-nowrap">{label}</span>;
184
- }
@@ -1,97 +0,0 @@
1
- import { isPresent } from "@manta-eu/lib/present";
2
- import { useEffect, useState } from "react";
3
- import type { EntityTypeEntry } from "./ActionPlanDetailView";
4
- import { buildSections, matchesManualActionSection } from "./execution-section-model";
5
- import { ExecutionActivityRow } from "./ExecutionActivityRow";
6
- import { ExecutionSectionItem } from "./ExecutionSectionItem";
7
- import type { ManualActionTarget } from "./ManualActionCard";
8
- import type {
9
- ActionPlanExecutionProvenanceResult,
10
- ManualAction,
11
- ManualActionResolution,
12
- ManualActionResolving,
13
- } from "#ldo-ui";
14
-
15
- export type ExecutionSectionsProps = {
16
- entityTypes: EntityTypeEntry[];
17
- provenance: ActionPlanExecutionProvenanceResult | null;
18
- manualActions?: ManualAction[];
19
- manualActionEntities?: Map<string, ManualActionTarget[]>;
20
- activeStartedAtMs?: number | null;
21
- onManualActionResolve?: (subjectIri: string, resolution: ManualActionResolution) => void;
22
- resolvingManualAction?: ManualActionResolving;
23
- canResolveManualAction?: boolean;
24
- manualActionDisabledReason?: string;
25
- };
26
-
27
- export function ExecutionSections(props: ExecutionSectionsProps) {
28
- const {
29
- entityTypes,
30
- provenance,
31
- manualActions = [],
32
- manualActionEntities,
33
- activeStartedAtMs = null,
34
- onManualActionResolve,
35
- resolvingManualAction,
36
- canResolveManualAction,
37
- manualActionDisabledReason,
38
- } = props;
39
- const [expandedIndex, setExpandedIndex] = useState<number | null>(0);
40
- const [lastAutoExpandedRunning, setLastAutoExpandedRunning] = useState(-1);
41
- const activities = provenance?.activities ?? [];
42
- const planFailed = !!provenance?.planErrorMessage;
43
-
44
- const sections = buildSections({ entityTypes, activities, provenance, planFailed });
45
-
46
- const runningSectionIndex = sections.findIndex((s) => s.status === "running");
47
-
48
- useEffect(() => {
49
- if (runningSectionIndex >= 0 && runningSectionIndex !== lastAutoExpandedRunning) {
50
- setLastAutoExpandedRunning(runningSectionIndex);
51
- setExpandedIndex(runningSectionIndex);
52
- }
53
- }, [lastAutoExpandedRunning, runningSectionIndex]);
54
-
55
- if (!isPresent(sections) && isPresent(activities)) {
56
- return (
57
- <div className="overflow-hidden rounded-xl border border-default-border bg-card">
58
- <div className="bg-faint py-1.5">
59
- {activities.map((activity) => (
60
- <ExecutionActivityRow key={activity.iri} activity={activity} />
61
- ))}
62
- </div>
63
- </div>
64
- );
65
- }
66
-
67
- return (
68
- <div className="overflow-hidden rounded-xl border border-default-border bg-card divide-y divide-default-border">
69
- {sections.map((section, index) => {
70
- const activeFallbackApplies = index === 0 && section.status === "pending" && activeStartedAtMs !== null;
71
- const effectiveStatus = activeFallbackApplies ? "running" : section.status;
72
- const fallbackStartedAtMs = activeFallbackApplies ? activeStartedAtMs : null;
73
- const sectionManualActions = manualActions.filter((manualAction) =>
74
- matchesManualActionSection(manualAction, section.entry, sections.length),
75
- );
76
-
77
- return (
78
- <ExecutionSectionItem
79
- key={section.key}
80
- entry={section.entry}
81
- activities={section.activities}
82
- status={effectiveStatus}
83
- fallbackStartedAtMs={fallbackStartedAtMs}
84
- isExpanded={expandedIndex === index}
85
- onToggle={() => setExpandedIndex((prev) => (prev === index ? null : index))}
86
- manualActions={sectionManualActions}
87
- manualActionEntities={manualActionEntities}
88
- onManualActionResolve={onManualActionResolve}
89
- resolvingManualAction={resolvingManualAction}
90
- canResolveManualAction={canResolveManualAction}
91
- manualActionDisabledReason={manualActionDisabledReason}
92
- />
93
- );
94
- })}
95
- </div>
96
- );
97
- }
@@ -1,27 +0,0 @@
1
- import { BarChart2, Bell, CircleDot, DollarSign, Image, Settings, Target, UsersRound, Zap } from "lucide-react";
2
- import type { LucideIcon } from "lucide-react";
3
- import type { ReactNode } from "react";
4
-
5
- const stepIconKeywords: [string[], LucideIcon][] = [
6
- [["campaign", "ad", "advertising", "promote", "advertise", "boost"], Target],
7
- [["audience", "target", "people", "customer", "user", "segment"], UsersRound],
8
- [["remind", "review", "check", "monitor", "follow"], Bell],
9
- [["budget", "spend", "cost", "money", "bid"], DollarSign],
10
- [["creative", "image", "video", "asset", "copy", "content"], Image],
11
- [["launch", "publish", "start", "run", "enable", "activate"], Zap],
12
- [["report", "analys", "track", "metric", "data", "insight"], BarChart2],
13
- [["rule", "logic", "automat", "workflow"], Settings],
14
- ];
15
-
16
- function matchStepIcon(name: string): LucideIcon {
17
- const lower = name.toLowerCase();
18
- for (const [keywords, icon] of stepIconKeywords) {
19
- if (keywords.some((kw) => lower.includes(kw))) return icon;
20
- }
21
- return CircleDot;
22
- }
23
-
24
- export function resolveStepIcon(name: string, className: string): ReactNode {
25
- const Icon = matchStepIcon(name);
26
- return <Icon className={className} />;
27
- }
@@ -1,190 +0,0 @@
1
- import { iriLocalName } from "@manta-eu/client/shared/rdf-namespaces";
2
- import { isPresent } from "@manta-eu/lib/present";
3
- import type { EntityTypeEntry } from "./ActionPlanDetailView";
4
- import type { ActionPlanExecutionProvenanceResult, ExecutionProvenanceActivity, ManualAction } from "#ldo-ui";
5
-
6
- export type SectionStatus = "pending" | "running" | "completed" | "failed";
7
-
8
- export type SectionData = {
9
- key: string;
10
- entry: EntityTypeEntry;
11
- activities: ExecutionProvenanceActivity[];
12
- status: SectionStatus;
13
- };
14
-
15
- export type ResolvedManualActionStatus = Extract<ManualAction["status"], "COMPLETED" | "REJECTED">;
16
-
17
- export type BuildSectionsArgs = {
18
- entityTypes: EntityTypeEntry[];
19
- activities: ExecutionProvenanceActivity[];
20
- provenance: ActionPlanExecutionProvenanceResult | null;
21
- planFailed: boolean;
22
- };
23
-
24
- export function parseTimestampMs(timestamp: string | null | undefined): number | null {
25
- if (!timestamp) return null;
26
- const value = new Date(timestamp).getTime();
27
- return Number.isFinite(value) ? value : null;
28
- }
29
-
30
- export function deriveSectionStatus(
31
- activities: ExecutionProvenanceActivity[],
32
- provenance: ActionPlanExecutionProvenanceResult | null,
33
- planFailed: boolean,
34
- ): SectionStatus {
35
- if (!isPresent(activities)) {
36
- if (planFailed) return "failed";
37
- if (provenance?.planEndedAtTime) return "completed";
38
- return "pending";
39
- }
40
- if (activities.some((a) => a.errorMessage)) return "failed";
41
- if (activities.every((a) => a.endedAtTime)) {
42
- return planFailed ? "failed" : "completed";
43
- }
44
- if (activities.some((a) => a.startedAtTime)) return "running";
45
- return "pending";
46
- }
47
-
48
- function isSectionLabelOrIriMatch(activity: ExecutionProvenanceActivity, typeName: string): boolean {
49
- const entityLabel = activity.entityLabel.toLowerCase();
50
- const activityIri = activity.iri.toLowerCase();
51
- const typeSlug = typeName.replace(/\s+/g, "_");
52
- return entityLabel.includes(typeName) || activityIri.includes(typeSlug) || activityIri.includes(typeName);
53
- }
54
-
55
- function isSectionPauseAliasMatch(activity: ExecutionProvenanceActivity, typeName: string): boolean {
56
- if (!typeName.includes("pause")) return false;
57
- const entityLabel = activity.entityLabel.toLowerCase();
58
- const opType = (activity.operationType ?? "").toLowerCase();
59
- return opType.includes("delete") || opType.includes("pause") || entityLabel.includes("pause");
60
- }
61
-
62
- function isSectionScaleAliasMatch(activity: ExecutionProvenanceActivity, typeName: string): boolean {
63
- if (!typeName.includes("scale")) return false;
64
- const entityLabel = activity.entityLabel.toLowerCase();
65
- const opType = (activity.operationType ?? "").toLowerCase();
66
- return (
67
- opType.includes("addit") ||
68
- opType.includes("scale") ||
69
- opType.includes("budget") ||
70
- entityLabel.includes("scale") ||
71
- entityLabel.includes("budget")
72
- );
73
- }
74
-
75
- function isSectionMonitorAliasMatch(activity: ExecutionProvenanceActivity, typeName: string): boolean {
76
- if (!typeName.includes("monitor")) return false;
77
- const entityLabel = activity.entityLabel.toLowerCase();
78
- const opType = (activity.operationType ?? "").toLowerCase();
79
- return opType.includes("monitor") || entityLabel.includes("monitor") || entityLabel.includes("verif");
80
- }
81
-
82
- function isSectionEntityTypeNameMatch(activity: ExecutionProvenanceActivity, typeName: string): boolean {
83
- if (!activity.entityTypeName) return false;
84
- return typeName.includes(activity.entityTypeName.toLowerCase());
85
- }
86
-
87
- function isSectionRelatedEntitiesMatch(activity: ExecutionProvenanceActivity, entry: EntityTypeEntry): boolean {
88
- const typeName = entry.typeName.toLowerCase();
89
- const entryTypeIri = entry.typeIri.toLowerCase();
90
- for (const entity of activity.entities) {
91
- if (!entity.typeName) continue;
92
- const entityTypeLower = entity.typeName.toLowerCase();
93
- if (typeName.includes(entityTypeLower)) return true;
94
- if (entryTypeIri.includes(entityTypeLower)) return true;
95
- }
96
- return false;
97
- }
98
-
99
- export function matchesSection(activity: ExecutionProvenanceActivity, entry: EntityTypeEntry): boolean {
100
- const typeName = entry.typeName.toLowerCase();
101
- const predicates = [
102
- () => isSectionLabelOrIriMatch(activity, typeName),
103
- () => isSectionPauseAliasMatch(activity, typeName),
104
- () => isSectionScaleAliasMatch(activity, typeName),
105
- () => isSectionMonitorAliasMatch(activity, typeName),
106
- () => isSectionEntityTypeNameMatch(activity, typeName),
107
- () => isSectionRelatedEntitiesMatch(activity, entry),
108
- ];
109
- return predicates.some((p) => p());
110
- }
111
-
112
- export function matchesManualActionSection(
113
- manualAction: ManualAction,
114
- entry: EntityTypeEntry,
115
- sectionCount: number,
116
- ): boolean {
117
- if (sectionCount === 1) return true;
118
-
119
- const entryTypeIri = entry.typeIri.toLowerCase();
120
- const entryTypeName = entry.typeName.toLowerCase();
121
- const manualTypeIri = manualAction.typeIri.toLowerCase();
122
- const manualTypeName = manualAction.typeName.toLowerCase();
123
- const manualSubject = manualAction.subjectLabel?.toLowerCase() ?? manualAction.subjectIri.toLowerCase();
124
-
125
- return (
126
- manualTypeIri === entryTypeIri ||
127
- manualTypeName === entryTypeName ||
128
- entryTypeIri.includes(manualTypeName) ||
129
- manualTypeIri.includes(entryTypeName) ||
130
- manualSubject.includes(entryTypeName)
131
- );
132
- }
133
-
134
- export function matchesManualActionActivity(
135
- manualAction: ManualAction,
136
- activity: ExecutionProvenanceActivity,
137
- ): boolean {
138
- if (activity.entityIri === manualAction.subjectIri) return true;
139
- if (activity.entities.some((entity) => entity.iri === manualAction.subjectIri)) return true;
140
-
141
- const subjectLabel = manualAction.subjectLabel?.toLowerCase();
142
- if (subjectLabel && activity.entityLabel.toLowerCase().includes(subjectLabel)) return true;
143
-
144
- return activity.iri.toLowerCase().includes(iriLocalName(manualAction.subjectIri).toLowerCase());
145
- }
146
-
147
- export function findResolvedManualActionStatus(
148
- activity: ExecutionProvenanceActivity,
149
- manualActions: ManualAction[],
150
- ): ResolvedManualActionStatus | null {
151
- const found = manualActions.find((manualAction) => matchesManualActionActivity(manualAction, activity));
152
- return found?.status === "COMPLETED" || found?.status === "REJECTED" ? found.status : null;
153
- }
154
-
155
- export function buildSections(args: BuildSectionsArgs): SectionData[] {
156
- const { entityTypes, activities, provenance, planFailed } = args;
157
- const matched = new Set<string>();
158
-
159
- const sections: SectionData[] = entityTypes.map((entry) => {
160
- const sectionActivities = activities.filter((a) => {
161
- if (matched.has(a.iri)) return false;
162
- return matchesSection(a, entry);
163
- });
164
- for (const a of sectionActivities) matched.add(a.iri);
165
-
166
- return {
167
- key: entry.typeIri,
168
- entry,
169
- activities: sectionActivities,
170
- status: deriveSectionStatus(sectionActivities, provenance, planFailed),
171
- };
172
- });
173
-
174
- const unmatched = activities.filter((a) => !matched.has(a.iri));
175
- if (isPresent(unmatched)) {
176
- sections.push({
177
- key: "__unmatched",
178
- entry: {
179
- typeIri: "__unmatched",
180
- typeName: "Other actions",
181
- addedCount: 0,
182
- updatedCount: unmatched.length,
183
- },
184
- activities: unmatched,
185
- status: deriveSectionStatus(unmatched, provenance, planFailed),
186
- });
187
- }
188
-
189
- return sections;
190
- }
@@ -1,25 +0,0 @@
1
- import { useState } from "react";
2
- import { Tooltip, TooltipContent, TooltipTrigger } from "#components/tooltip/index";
3
-
4
- type TruncateTooltipProps = { text: string; className: string };
5
-
6
- export function TruncateTooltip(props: TruncateTooltipProps) {
7
- const { text, className } = props;
8
-
9
- const [isTruncated, setIsTruncated] = useState(false);
10
-
11
- return (
12
- <Tooltip>
13
- <TooltipTrigger asChild>
14
- <span
15
- className={className}
16
- onMouseEnter={(event) => setIsTruncated(event.currentTarget.scrollWidth > event.currentTarget.clientWidth)}
17
- >
18
- {text}
19
- </span>
20
- </TooltipTrigger>
21
-
22
- {isTruncated && <TooltipContent>{text}</TooltipContent>}
23
- </Tooltip>
24
- );
25
- }