@manta-eu/react 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/components/chart/ForecastLegend.d.ts.map +1 -1
  2. package/dist/components/chart/ForecastLegend.js +10 -2
  3. package/dist/components/chart/ForecastLegend.js.map +1 -1
  4. package/dist/components/chart/forecast-model.d.ts.map +1 -1
  5. package/dist/components/chart/forecast-model.js.map +1 -1
  6. package/dist/components/chat-view/Chat.js +2 -2
  7. package/dist/components/file-dropzone/index.d.ts +25 -0
  8. package/dist/components/file-dropzone/index.d.ts.map +1 -0
  9. package/dist/components/file-dropzone/index.js +70 -0
  10. package/dist/components/file-dropzone/index.js.map +1 -0
  11. package/dist/components/planning/plan-detail-utils.d.ts.map +1 -1
  12. package/dist/components/planning/plan-detail-utils.js +2 -0
  13. package/dist/components/planning/plan-detail-utils.js.map +1 -1
  14. package/dist/components/toast/index.d.ts +1 -0
  15. package/dist/components/toast/index.d.ts.map +1 -1
  16. package/dist/components/toast/index.js +1 -0
  17. package/dist/components/toast/index.js.map +1 -1
  18. package/dist/globals.css +27 -0
  19. package/dist/graph/NodeDetailPanel/TypeSummaryPanel.d.ts.map +1 -1
  20. package/dist/graph/NodeDetailPanel/TypeSummaryPanel.js.map +1 -1
  21. package/dist/hooks/use-sparql-construct.d.ts +2 -0
  22. package/dist/hooks/use-sparql-construct.d.ts.map +1 -1
  23. package/dist/hooks/use-sparql-construct.js +2 -1
  24. package/dist/hooks/use-sparql-construct.js.map +1 -1
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/lib/datastore-route-table.d.ts +3 -0
  30. package/dist/lib/datastore-route-table.d.ts.map +1 -1
  31. package/dist/lib/datastore-route-table.js +3 -0
  32. package/dist/lib/datastore-route-table.js.map +1 -1
  33. package/dist/lib/hooks/{useChatUpload.d.ts → useFileUpload.d.ts} +3 -3
  34. package/dist/lib/hooks/{useChatUpload.d.ts.map → useFileUpload.d.ts.map} +1 -1
  35. package/dist/lib/hooks/{useChatUpload.js → useFileUpload.js} +2 -2
  36. package/dist/lib/hooks/{useChatUpload.js.map → useFileUpload.js.map} +1 -1
  37. package/dist/routes/data-collection/index.d.ts +3 -0
  38. package/dist/routes/data-collection/index.d.ts.map +1 -0
  39. package/dist/routes/data-collection/index.js +43 -0
  40. package/dist/routes/data-collection/index.js.map +1 -0
  41. package/dist/routes/data-explorer/components/DataExplorerLayout.d.ts.map +1 -1
  42. package/dist/routes/data-explorer/components/DataExplorerLayout.js +5 -2
  43. package/dist/routes/data-explorer/components/DataExplorerLayout.js.map +1 -1
  44. package/dist/routes/data-explorer/components/Rail.d.ts.map +1 -1
  45. package/dist/routes/data-explorer/components/Rail.js +3 -3
  46. package/dist/routes/data-explorer/components/Rail.js.map +1 -1
  47. package/dist/routes/data-explorer/components/uploads/IngestRunsList.d.ts +8 -0
  48. package/dist/routes/data-explorer/components/uploads/IngestRunsList.d.ts.map +1 -0
  49. package/dist/routes/data-explorer/components/uploads/IngestRunsList.js +18 -0
  50. package/dist/routes/data-explorer/components/uploads/IngestRunsList.js.map +1 -0
  51. package/dist/routes/data-explorer/components/uploads/IngestStateStatus.d.ts +14 -0
  52. package/dist/routes/data-explorer/components/uploads/IngestStateStatus.d.ts.map +1 -0
  53. package/dist/routes/data-explorer/components/uploads/IngestStateStatus.js +33 -0
  54. package/dist/routes/data-explorer/components/uploads/IngestStateStatus.js.map +1 -0
  55. package/dist/routes/data-explorer/components/uploads/UploadedFilesList.d.ts +8 -0
  56. package/dist/routes/data-explorer/components/uploads/UploadedFilesList.d.ts.map +1 -0
  57. package/dist/routes/data-explorer/components/uploads/UploadedFilesList.js +34 -0
  58. package/dist/routes/data-explorer/components/uploads/UploadedFilesList.js.map +1 -0
  59. package/dist/routes/data-explorer/components/uploads/UploadsPages.d.ts +7 -0
  60. package/dist/routes/data-explorer/components/uploads/UploadsPages.d.ts.map +1 -0
  61. package/dist/routes/data-explorer/components/uploads/UploadsPages.js +35 -0
  62. package/dist/routes/data-explorer/components/uploads/UploadsPages.js.map +1 -0
  63. package/dist/routes/data-explorer/index.d.ts +1 -0
  64. package/dist/routes/data-explorer/index.d.ts.map +1 -1
  65. package/dist/routes/data-explorer/index.js +1 -0
  66. package/dist/routes/data-explorer/index.js.map +1 -1
  67. package/dist/routes/data-explorer/namespaces.d.ts +4 -0
  68. package/dist/routes/data-explorer/namespaces.d.ts.map +1 -1
  69. package/dist/routes/data-explorer/namespaces.js +6 -1
  70. package/dist/routes/data-explorer/namespaces.js.map +1 -1
  71. package/dist/routes/data-explorer/uploads.d.ts +56 -0
  72. package/dist/routes/data-explorer/uploads.d.ts.map +1 -0
  73. package/dist/routes/data-explorer/uploads.js +140 -0
  74. package/dist/routes/data-explorer/uploads.js.map +1 -0
  75. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts +1 -0
  76. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts.map +1 -1
  77. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js +16 -9
  78. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js.map +1 -1
  79. package/dist/routes/datastores/components/StatusBadge.d.ts +7 -8
  80. package/dist/routes/datastores/components/StatusBadge.d.ts.map +1 -1
  81. package/dist/routes/datastores/components/StatusBadge.js +17 -1
  82. package/dist/routes/datastores/components/StatusBadge.js.map +1 -1
  83. package/dist/routes/datastores/flatten.d.ts +6 -1
  84. package/dist/routes/datastores/flatten.d.ts.map +1 -1
  85. package/dist/routes/datastores/flatten.js +37 -2
  86. package/dist/routes/datastores/flatten.js.map +1 -1
  87. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts +2 -0
  88. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts.map +1 -1
  89. package/dist/routes/datastores/hooks/useDatastoreColumns.js +8 -4
  90. package/dist/routes/datastores/hooks/useDatastoreColumns.js.map +1 -1
  91. package/dist/routes/datastores/index.d.ts.map +1 -1
  92. package/dist/routes/datastores/index.js +53 -18
  93. package/dist/routes/datastores/index.js.map +1 -1
  94. package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.d.ts +3 -0
  95. package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.d.ts.map +1 -0
  96. package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.js +38 -0
  97. package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.js.map +1 -0
  98. package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.d.ts +4 -0
  99. package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.d.ts.map +1 -0
  100. package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.js +32 -0
  101. package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.js.map +1 -0
  102. package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.d.ts.map +1 -1
  103. package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.js +1 -0
  104. package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.js.map +1 -1
  105. package/dist/routes/integrations/connect-forms.d.ts +1 -0
  106. package/dist/routes/integrations/connect-forms.d.ts.map +1 -1
  107. package/dist/routes/integrations/connect-forms.js +1 -0
  108. package/dist/routes/integrations/connect-forms.js.map +1 -1
  109. package/dist/routes/integrations/connection-view.d.ts.map +1 -1
  110. package/dist/routes/integrations/connection-view.js +1 -0
  111. package/dist/routes/integrations/connection-view.js.map +1 -1
  112. package/dist/shared/graph-derived-queries.d.ts.map +1 -1
  113. package/dist/shared/graph-derived-queries.js +2 -1
  114. package/dist/shared/graph-derived-queries.js.map +1 -1
  115. package/package.json +21 -9
  116. package/src/app-subpaths.json +1 -0
  117. package/src/components/chart/ForecastLegend.tsx +15 -13
  118. package/src/components/chart/forecast-model.ts +6 -8
  119. package/src/components/chat-view/Chat.tsx +2 -2
  120. package/src/components/file-dropzone/index.tsx +165 -0
  121. package/src/components/planning/plan-detail-utils.tsx +2 -0
  122. package/src/components/toast/index.tsx +1 -0
  123. package/src/graph/NodeDetailPanel/TypeSummaryPanel.tsx +12 -14
  124. package/src/hooks/use-sparql-construct.ts +4 -2
  125. package/src/index.ts +1 -0
  126. package/src/lib/datastore-route-table.ts +6 -0
  127. package/src/lib/hooks/{useChatUpload.ts → useFileUpload.ts} +2 -2
  128. package/src/routes/data-collection/index.tsx +82 -0
  129. package/src/routes/data-explorer/components/DataExplorerLayout.tsx +5 -2
  130. package/src/routes/data-explorer/components/Rail.tsx +13 -2
  131. package/src/routes/data-explorer/components/uploads/IngestRunsList.tsx +63 -0
  132. package/src/routes/data-explorer/components/uploads/IngestStateStatus.tsx +45 -0
  133. package/src/routes/data-explorer/components/uploads/UploadedFilesList.tsx +93 -0
  134. package/src/routes/data-explorer/components/uploads/UploadsPages.tsx +80 -0
  135. package/src/routes/data-explorer/index.tsx +6 -0
  136. package/src/routes/data-explorer/namespaces.ts +7 -1
  137. package/src/routes/data-explorer/uploads.ts +205 -0
  138. package/src/routes/datastores/components/LinkUnlinkedDatastoreDialog.tsx +49 -10
  139. package/src/routes/datastores/components/StatusBadge.tsx +22 -7
  140. package/src/routes/datastores/flatten.ts +50 -2
  141. package/src/routes/datastores/hooks/useDatastoreColumns.tsx +21 -1
  142. package/src/routes/datastores/index.tsx +64 -13
  143. package/src/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.tsx +61 -0
  144. package/src/routes/integrations/components/connect-forms/google-merchant-center-account-tree.ts +38 -0
  145. package/src/routes/integrations/components/connect-forms/setup-account-unavailable-reason.ts +1 -0
  146. package/src/routes/integrations/connect-forms.ts +1 -0
  147. package/src/routes/integrations/connection-view.ts +1 -0
  148. package/src/shared/graph-derived-queries.ts +2 -0
@@ -1,4 +1,4 @@
1
- import { useActionPlanByIriQuery, useActionPlanDiffQuery, useActionPlanRulesQuery, useActionPlanRunHistoryQuery, useActionPlanSamplesQuery, useActionPlansQuery, useConversationStartersQuery, useCoverageRulesQuery, useCurrentFactValueQuery, useExplainFactQuery, useGetActionPlansSummaryQuery, useMemoriesQuery, useMemorySchemasQuery, useNodePropertiesQuery, useNodeRelationshipsQuery, useRulesQuery, useShaclShapesQuery, useSparqlQueryQuery, useUserPreferencesQuery, useValidateActionPlanQuery, } from "@manta-eu/client";
1
+ import { useActionPlanByIriQuery, useActionPlanDiffQuery, useActionPlanRulesQuery, useActionPlanRunHistoryQuery, useActionPlanSamplesQuery, useActionPlansQuery, useConversationStartersQuery, useCoverageRulesQuery, useCurrentFactValueQuery, useExplainFactQuery, useGetActionPlansSummaryQuery, useManualActionProgressQuery, useMemoriesQuery, useMemorySchemasQuery, useNodePropertiesQuery, useNodeRelationshipsQuery, useRulesQuery, useShaclShapesQuery, useSparqlQueryQuery, useUserPreferencesQuery, useValidateActionPlanQuery, } from "@manta-eu/client";
2
2
  import { invalidateQueries } from "#lib/tanstack-query";
3
3
  // Queries whose resolver reads the active datastore's knowledge graph, so any graph write can change them.
4
4
  export const graphDerivedQueries = [
@@ -13,6 +13,7 @@ export const graphDerivedQueries = [
13
13
  useCurrentFactValueQuery,
14
14
  useExplainFactQuery,
15
15
  useGetActionPlansSummaryQuery,
16
+ useManualActionProgressQuery,
16
17
  useMemoriesQuery,
17
18
  useMemorySchemasQuery,
18
19
  useNodePropertiesQuery,
@@ -1 +1 @@
1
- {"version":3,"file":"graph-derived-queries.js","sourceRoot":"","sources":["../../src/shared/graph-derived-queries.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC5B,qBAAqB,EACrB,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,EAC7B,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAE9E,2GAA2G;AAC3G,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,uBAAuB;IACvB,sBAAsB;IACtB,uBAAuB;IACvB,4BAA4B;IAC5B,yBAAyB;IACzB,mBAAmB;IACnB,4BAA4B;IAC5B,qBAAqB;IACrB,wBAAwB;IACxB,mBAAmB;IACnB,6BAA6B;IAC7B,gBAAgB;IAChB,qBAAqB;IACrB,sBAAsB;IACtB,yBAAyB;IACzB,aAAa;IACb,mBAAmB;IACnB,mBAAmB;IACnB,uBAAuB;IACvB,0BAA0B;CAC3B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,WAAwB;IAC1E,MAAM,iBAAiB,CAAC,WAAW,EAAE,CAAC,GAAG,mBAAmB,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC"}
1
+ {"version":3,"file":"graph-derived-queries.js","sourceRoot":"","sources":["../../src/shared/graph-derived-queries.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,EACzB,mBAAmB,EACnB,4BAA4B,EAC5B,qBAAqB,EACrB,wBAAwB,EACxB,mBAAmB,EACnB,6BAA6B,EAC7B,4BAA4B,EAC5B,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAE9E,2GAA2G;AAC3G,MAAM,CAAC,MAAM,mBAAmB,GAAsB;IACpD,uBAAuB;IACvB,sBAAsB;IACtB,uBAAuB;IACvB,4BAA4B;IAC5B,yBAAyB;IACzB,mBAAmB;IACnB,4BAA4B;IAC5B,qBAAqB;IACrB,wBAAwB;IACxB,mBAAmB;IACnB,6BAA6B;IAC7B,4BAA4B;IAC5B,gBAAgB;IAChB,qBAAqB;IACrB,sBAAsB;IACtB,yBAAyB;IACzB,aAAa;IACb,mBAAmB;IACnB,mBAAmB;IACnB,uBAAuB;IACvB,0BAA0B;CAC3B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,6BAA6B,CAAC,WAAwB;IAC1E,MAAM,iBAAiB,CAAC,WAAW,EAAE,CAAC,GAAG,mBAAmB,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;AACpG,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manta-eu/react",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "The Manta design system, knowledge-graph UI and agent components",
5
5
  "homepage": "https://developers.manta.eu",
6
6
  "keywords": [
@@ -310,6 +310,11 @@
310
310
  "types": "./dist/engine.d.ts",
311
311
  "import": "./dist/engine.js"
312
312
  },
313
+ "./file-dropzone": {
314
+ "manta-source": "./src/components/file-dropzone/index.tsx",
315
+ "types": "./dist/components/file-dropzone/index.d.ts",
316
+ "import": "./dist/components/file-dropzone/index.js"
317
+ },
313
318
  "./filter-sheet": {
314
319
  "manta-source": "./src/components/filter-sheet/index.tsx",
315
320
  "types": "./dist/components/filter-sheet/index.d.ts",
@@ -721,6 +726,11 @@
721
726
  "types": "./dist/routes/activity/format.d.ts",
722
727
  "import": "./dist/routes/activity/format.js"
723
728
  },
729
+ "./routes/data-collection": {
730
+ "manta-source": "./src/routes/data-collection/index.tsx",
731
+ "types": "./dist/routes/data-collection/index.d.ts",
732
+ "import": "./dist/routes/data-collection/index.js"
733
+ },
724
734
  "./routes/data-explorer": {
725
735
  "manta-source": "./src/routes/data-explorer/index.tsx",
726
736
  "types": "./dist/routes/data-explorer/index.d.ts",
@@ -1137,14 +1147,14 @@
1137
1147
  "tailwindcss-animate": "^1.0.7",
1138
1148
  "ts-pattern": "^5.9.0",
1139
1149
  "usehooks-ts": "^3.1.1",
1140
- "zod": "^4.5.0",
1150
+ "zod": "^4.6.7",
1141
1151
  "zustand": "^5.0.2",
1142
- "@manta-eu/auth": "0.8.1",
1143
- "@manta-eu/client": "0.8.1",
1144
- "@manta-eu/lib": "0.8.1",
1145
- "@manta-eu/rdf-lib": "0.8.1",
1146
- "@manta-eu/datalog-parser": "0.8.1",
1147
- "@manta-eu/ldo": "0.8.1"
1152
+ "@manta-eu/auth": "0.9.0",
1153
+ "@manta-eu/client": "0.9.0",
1154
+ "@manta-eu/datalog-parser": "0.9.0",
1155
+ "@manta-eu/ldo": "0.9.0",
1156
+ "@manta-eu/lib": "0.9.0",
1157
+ "@manta-eu/rdf-lib": "0.9.0"
1148
1158
  },
1149
1159
  "peerDependencies": {
1150
1160
  "@ulb-darmstadt/shacl-form": "^2.1.1-rc1",
@@ -1177,10 +1187,11 @@
1177
1187
  "storybook": "^10.6.4",
1178
1188
  "tailwindcss": "^4.3.3",
1179
1189
  "tsc-alias": "^1.9.5",
1190
+ "tsx": "^4.21.0",
1180
1191
  "typescript": "^7.0.2",
1181
1192
  "uuid": "^13.0.0",
1182
1193
  "vite": "^8.3.5",
1183
- "vitest": "^4.1.8",
1194
+ "vitest": "~5.0.5",
1184
1195
  "@manta-eu/oxlint-config": "0.1.0"
1185
1196
  },
1186
1197
  "imports": {
@@ -1202,6 +1213,7 @@
1202
1213
  "scripts": {
1203
1214
  "build": "rimraf dist && postcss src/globals.css -o dist/globals.css && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json --resolve-full-paths",
1204
1215
  "build-storybook": "NODE_OPTIONS=\"$NODE_OPTIONS --max-old-space-size=6144\" storybook build",
1216
+ "build-storybook:public": "NODE_OPTIONS=\"$NODE_OPTIONS --max-old-space-size=6144\" storybook build -c .storybook-public -o storybook-public",
1205
1217
  "lint": "oxlint --type-aware --report-unused-disable-directives-severity=warn .",
1206
1218
  "storybook": "storybook dev -p 6007",
1207
1219
  "test": "vitest --run",
@@ -109,6 +109,7 @@
109
109
  "./routes/activity/detail/LazyMount",
110
110
  "./routes/activity/detail/timeline",
111
111
  "./routes/activity/format",
112
+ "./routes/data-collection",
112
113
  "./routes/data-explorer",
113
114
  "./routes/data-explorer/namespaces",
114
115
  "./routes/datastores",
@@ -32,20 +32,22 @@ export function forecastLegendItems(source: LegendSource): ForecastLegendItem[]
32
32
  hasProjection,
33
33
  legendLabels,
34
34
  } = source;
35
- const withoutItems = [...new Set(withoutLabels)].map(
36
- (label): ForecastLegendItem => ({ label, color: withoutColor, kind: "thin-line" }),
37
- );
35
+ const withoutItems = [...new Set(withoutLabels)].map((label): ForecastLegendItem => ({
36
+ label,
37
+ color: withoutColor,
38
+ kind: "thin-line",
39
+ }));
38
40
  const oneGainColor = new Set(gains.map((gain) => gain.color)).size === 1;
39
- const gainItems = (oneGainColor ? gains.slice(0, 1) : gains).map(
40
- (gain): ForecastLegendItem => ({
41
- label: oneGainColor ? gainLabel : `${gainLabel} · ${gain.metric}`,
42
- color: gain.color,
43
- kind: "fill",
44
- }),
45
- );
46
- const betweenItems = betweenLabels.map(
47
- (label): ForecastLegendItem => ({ label, color: firstColor, kind: "faint-fill" }),
48
- );
41
+ const gainItems = (oneGainColor ? gains.slice(0, 1) : gains).map((gain): ForecastLegendItem => ({
42
+ label: oneGainColor ? gainLabel : `${gainLabel} · ${gain.metric}`,
43
+ color: gain.color,
44
+ kind: "fill",
45
+ }));
46
+ const betweenItems = betweenLabels.map((label): ForecastLegendItem => ({
47
+ label,
48
+ color: firstColor,
49
+ kind: "faint-fill",
50
+ }));
49
51
  if (metrics.length > 1) {
50
52
  return [
51
53
  ...metrics.map((metric): ForecastLegendItem => ({ label: metric.label, color: metric.color, kind: "line" })),
@@ -104,14 +104,12 @@ export function buildForecastModel(input: ModelInput) {
104
104
  config: Object.fromEntries(
105
105
  [...metrics, ...withPlans.map((entry) => entry.without)].map((line) => [line.key, { label: line.label }]),
106
106
  ) satisfies ChartConfig,
107
- hoverMetrics: metrics.map(
108
- (metric): ForecastHoverMetric => ({
109
- key: metric.key,
110
- label: metric.label,
111
- lowerIsBetter: metric.lowerIsBetter === true,
112
- ...(isPresent(metric.withoutPlans) ? { withoutKey: metric.withoutPlans.key } : {}),
113
- }),
114
- ),
107
+ hoverMetrics: metrics.map((metric): ForecastHoverMetric => ({
108
+ key: metric.key,
109
+ label: metric.label,
110
+ lowerIsBetter: metric.lowerIsBetter === true,
111
+ ...(isPresent(metric.withoutPlans) ? { withoutKey: metric.withoutPlans.key } : {}),
112
+ })),
115
113
  legendItems: forecastLegendItems({
116
114
  metrics: metrics.map((metric, index) => ({ label: metric.label, color: metricColor(metric, index) })),
117
115
  withoutLabels: withPlans.map((entry) => entry.without.label),
@@ -50,7 +50,7 @@ import { useSelectedModel } from "#hooks/useSelectedModel";
50
50
  import { isDevBuild } from "#lib/build-config";
51
51
  import type { ChatDraft, CopilotMenuDefaults, Datastore, PageContext, User } from "#lib/chat-types";
52
52
  import { findLatestShowcase } from "#lib/find-latest-showcase";
53
- import { useChatUpload } from "#lib/hooks/useChatUpload";
53
+ import { useFileUpload } from "#lib/hooks/useFileUpload";
54
54
  import { planStatusLabel } from "#lib/plan-status-label";
55
55
  import { cn } from "#lib/utils";
56
56
  import { ActiveChatProvider, useActiveChatConnection } from "#providers/ActiveChatProvider";
@@ -360,7 +360,7 @@ function ChatContent(props: ChatContentProps) {
360
360
 
361
361
  const toolsUsedInSession = useRef(new Set<string>());
362
362
 
363
- const { uploadFiles, isUploading, progress: uploadProgress } = useChatUpload();
363
+ const { uploadFiles, isUploading, progress: uploadProgress } = useFileUpload();
364
364
 
365
365
  const beginAssistantTurn = useCallback(() => {
366
366
  turnKeyRef.current += 1;
@@ -0,0 +1,165 @@
1
+ import { maxUploadBytes } from "@manta-eu/client/shared/upload";
2
+ import { fileIdentityKey } from "@manta-eu/lib/file";
3
+ import { humanBytes } from "@manta-eu/lib/humanize";
4
+ import { isPresent } from "@manta-eu/lib/present";
5
+ import { FileText, UploadCloud } from "lucide-react";
6
+ import { useRef, useState, type ChangeEvent, type DragEvent } from "react";
7
+ import { Progress } from "#components/progress/index";
8
+ import { cn } from "#style";
9
+
10
+ export type RejectedDrop = {
11
+ file: File;
12
+ reason: string;
13
+ };
14
+
15
+ export function partitionDroppedFiles(files: readonly File[], maxFileSize: number) {
16
+ const accepted: File[] = [];
17
+ const rejected: RejectedDrop[] = [];
18
+
19
+ for (const file of files) {
20
+ if (file.size === 0) rejected.push({ file, reason: "File is empty" });
21
+ else if (file.size > maxFileSize) rejected.push({ file, reason: `File exceeds ${humanBytes(maxFileSize)}` });
22
+ else accepted.push(file);
23
+ }
24
+
25
+ return { accepted, rejected };
26
+ }
27
+
28
+ function hasFiles(dataTransfer: DataTransfer | null) {
29
+ return dataTransfer?.types.includes("Files") ?? false;
30
+ }
31
+
32
+ export type FileDropzoneProps = {
33
+ onFiles: (files: File[]) => void;
34
+ onRejected?: (rejected: RejectedDrop[]) => void;
35
+ disabled?: boolean;
36
+ maxFileSize?: number;
37
+ title?: string;
38
+ description?: string;
39
+ className?: string;
40
+ };
41
+
42
+ export function FileDropzone(props: FileDropzoneProps) {
43
+ const {
44
+ onFiles,
45
+ onRejected,
46
+ disabled = false,
47
+ maxFileSize = maxUploadBytes,
48
+ title = "Drop files here",
49
+ description,
50
+ className,
51
+ } = props;
52
+
53
+ const inputRef = useRef<HTMLInputElement>(null);
54
+ const [isDragging, setIsDragging] = useState(false);
55
+
56
+ function handleFiles(files: File[]) {
57
+ const { accepted, rejected } = partitionDroppedFiles(files, maxFileSize);
58
+
59
+ if (isPresent(rejected)) onRejected?.(rejected);
60
+ if (isPresent(accepted)) onFiles(accepted);
61
+ }
62
+
63
+ function handleDragOver(event: DragEvent<HTMLButtonElement>) {
64
+ if (disabled || !hasFiles(event.dataTransfer)) return;
65
+
66
+ event.preventDefault();
67
+ setIsDragging(true);
68
+ }
69
+
70
+ function handleDragLeave(event: DragEvent<HTMLButtonElement>) {
71
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget)) return;
72
+
73
+ setIsDragging(false);
74
+ }
75
+
76
+ function handleDrop(event: DragEvent<HTMLButtonElement>) {
77
+ event.preventDefault();
78
+ setIsDragging(false);
79
+
80
+ if (disabled) return;
81
+
82
+ handleFiles(Array.from(event.dataTransfer.files));
83
+ }
84
+
85
+ function handleChange(event: ChangeEvent<HTMLInputElement>) {
86
+ handleFiles(Array.from(event.target.files ?? []));
87
+ event.target.value = "";
88
+ }
89
+
90
+ return (
91
+ <>
92
+ <button
93
+ type="button"
94
+ disabled={disabled}
95
+ onClick={() => inputRef.current?.click()}
96
+ onDragEnter={handleDragOver}
97
+ onDragOver={handleDragOver}
98
+ onDragLeave={handleDragLeave}
99
+ onDrop={handleDrop}
100
+ data-dragging={isDragging}
101
+ className={cn(
102
+ "flex min-h-56 w-full flex-col items-center justify-center gap-3 rounded-3xl border-2 border-dashed px-6 py-10",
103
+ "border-muted-border bg-faint text-center outline-none transition-colors",
104
+ "hover:border-accent-foreground/60 focus-visible:ring-2 focus-visible:ring-active-border/50",
105
+ "disabled:cursor-not-allowed disabled:opacity-60",
106
+ isDragging && "border-accent-foreground/80 bg-accent-foreground/[0.04]",
107
+ className,
108
+ )}
109
+ >
110
+ <UploadCloud className="size-8 text-accent-foreground" />
111
+ <span className="typography-body-medium text-default-foreground">{title}</span>
112
+ <span className="typography-small text-muted-foreground">
113
+ or <span className="text-primary underline underline-offset-2">browse your computer</span>
114
+ </span>
115
+ <span className="typography-caption text-muted-foreground">
116
+ {description ?? `Any file type, up to ${humanBytes(maxFileSize)} each`}
117
+ </span>
118
+ </button>
119
+
120
+ <input
121
+ ref={inputRef}
122
+ type="file"
123
+ multiple
124
+ hidden
125
+ disabled={disabled}
126
+ onChange={handleChange}
127
+ data-testid="file-dropzone-input"
128
+ />
129
+ </>
130
+ );
131
+ }
132
+
133
+ export type UploadProgressListProps = {
134
+ files: readonly File[];
135
+ progress: Record<string, number>;
136
+ className?: string;
137
+ };
138
+
139
+ export function UploadProgressList(props: UploadProgressListProps) {
140
+ const { files, progress, className } = props;
141
+
142
+ if (!isPresent(files)) return null;
143
+
144
+ return (
145
+ <ul className={cn("flex flex-col gap-2", className)} aria-label="Uploads in progress">
146
+ {files.map((file) => {
147
+ const percent = progress[fileIdentityKey(file)] ?? 0;
148
+
149
+ return (
150
+ <li
151
+ key={fileIdentityKey(file)}
152
+ className="flex items-center gap-3 rounded-lg border border-muted-border px-3 py-2"
153
+ >
154
+ <FileText className="size-4 shrink-0 text-muted-foreground" />
155
+ <span className="typography-small min-w-0 flex-1 truncate text-default-foreground">{file.name}</span>
156
+ <Progress value={percent} size="sm" className="w-24 shrink-0 sm:w-40" aria-label={`${file.name} upload`} />
157
+ <span className="typography-caption w-10 shrink-0 text-right tabular-nums text-muted-foreground">
158
+ {percent}%
159
+ </span>
160
+ </li>
161
+ );
162
+ })}
163
+ </ul>
164
+ );
165
+ }
@@ -109,6 +109,7 @@ export function formatIntegration(integration: GQLIntegration | null): string {
109
109
  .with(GQLIntegration.Meta, () => "Meta")
110
110
  .with(GQLIntegration.Google, () => "Google")
111
111
  .with(GQLIntegration.GoogleAnalytics, () => "Google Analytics")
112
+ .with(GQLIntegration.GoogleMerchantCenter, () => "Merchant Center")
112
113
  .with(GQLIntegration.Linkedin, () => "LinkedIn")
113
114
  .with(GQLIntegration.MicrosoftAds, () => "Microsoft Ads")
114
115
  .with(GQLIntegration.Shopify, () => "Shopify")
@@ -126,6 +127,7 @@ export function integrationToChannel(integration: GQLIntegration): Channel | nul
126
127
  [GQLIntegration.Meta]: "META",
127
128
  [GQLIntegration.Google]: "GOOGLE_ADS",
128
129
  [GQLIntegration.GoogleAnalytics]: null,
130
+ [GQLIntegration.GoogleMerchantCenter]: null,
129
131
  [GQLIntegration.Linkedin]: null,
130
132
  [GQLIntegration.MicrosoftAds]: null,
131
133
  [GQLIntegration.Shopify]: "SHOPIFY",
@@ -71,3 +71,4 @@ const Toaster = ({ className, ...props }: ToasterProps) => {
71
71
  };
72
72
 
73
73
  export { Toaster };
74
+ export { toast } from "sonner";
@@ -107,20 +107,18 @@ function buildInstanceColumns(args: BuildInstanceColumnsArgs): ColumnDef<SampleI
107
107
  }
108
108
 
109
109
  columns.push(
110
- ...previewKeys.map(
111
- (key): ColumnDef<SampleInstance> => ({
112
- id: `preview:${key}`,
113
- header: titleCase(key),
114
- meta: denseCellMeta,
115
- cell: ({ row }) => {
116
- const value = propertyValue(row.original, key);
117
- if (!isPresent(value)) return null;
118
- return (
119
- <span className="typography-tiny block truncate text-muted-foreground">{formatDisplayValue(value)}</span>
120
- );
121
- },
122
- }),
123
- ),
110
+ ...previewKeys.map((key): ColumnDef<SampleInstance> => ({
111
+ id: `preview:${key}`,
112
+ header: titleCase(key),
113
+ meta: denseCellMeta,
114
+ cell: ({ row }) => {
115
+ const value = propertyValue(row.original, key);
116
+ if (!isPresent(value)) return null;
117
+ return (
118
+ <span className="typography-tiny block truncate text-muted-foreground">{formatDisplayValue(value)}</span>
119
+ );
120
+ },
121
+ })),
124
122
  );
125
123
 
126
124
  return columns;
@@ -1,4 +1,4 @@
1
- import { keepPreviousData, useQuery } from "@tanstack/react-query";
1
+ import { keepPreviousData, useQuery, type Query } from "@tanstack/react-query";
2
2
  import type { QueryContext } from "ldkit";
3
3
  import type { RDF } from "ldkit/rdf";
4
4
  import { withAbortSignal } from "../engine";
@@ -39,10 +39,11 @@ export type UseSparqlSelectOptions = {
39
39
  queryKey: readonly unknown[];
40
40
  enabled?: boolean;
41
41
  staleTime?: number;
42
+ refetchInterval?: number | false | ((query: Query<RDF.Bindings[]>) => number | false | undefined);
42
43
  };
43
44
 
44
45
  export function useSparqlSelect(config: UseSparqlSelectOptions) {
45
- const { query, queryKey, enabled = true, staleTime } = config;
46
+ const { query, queryKey, enabled = true, staleTime, refetchInterval } = config;
46
47
  const engine = useSparqlEngine();
47
48
  const options = useSparqlOptions();
48
49
 
@@ -54,5 +55,6 @@ export function useSparqlSelect(config: UseSparqlSelectOptions) {
54
55
  },
55
56
  enabled: enabled && !!query,
56
57
  staleTime,
58
+ refetchInterval,
57
59
  });
58
60
  }
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ export * from "./components/disabled-action-tooltip";
23
23
  export * from "./components/dropdown";
24
24
  export * from "./components/editable-text";
25
25
  export * from "./components/empty-state";
26
+ export * from "./components/file-dropzone";
26
27
  export * from "./components/filter-sheet";
27
28
  export * from "./components/hover-media";
28
29
  export * from "./components/input";
@@ -24,6 +24,9 @@ export type RouteElements = {
24
24
  DataExplorerIndex?: ReactNode;
25
25
  DataExplorerRecent?: ReactNode;
26
26
  DataExplorerCoverage?: ReactNode;
27
+ DataExplorerFiles?: ReactNode;
28
+ DataExplorerIngest?: ReactNode;
29
+ DataCollection?: ReactNode;
27
30
  DataExplorerPrefix?: ReactNode;
28
31
  DataExplorerType?: ReactNode;
29
32
  DataEntity?: ReactNode;
@@ -72,12 +75,15 @@ export const datastoreRoutes: RouteDefinition[] = [
72
75
  { path: "/data", render: (e) => e.DataExplorerIndex || notFoundElement(e) },
73
76
  { path: "/data/recent", render: (e) => e.DataExplorerRecent || notFoundElement(e) },
74
77
  { path: "/data/coverage", render: (e) => e.DataExplorerCoverage || notFoundElement(e) },
78
+ { path: "/data/files", render: (e) => e.DataExplorerFiles || notFoundElement(e) },
79
+ { path: "/data/ingest-runs", render: (e) => e.DataExplorerIngest || notFoundElement(e) },
75
80
  { path: "/data/entity/:iri", render: (e) => e.DataEntity || notFoundElement(e) },
76
81
  { path: "/data/:prefix", render: (e) => e.DataExplorerPrefix || notFoundElement(e) },
77
82
  { path: "/data/:prefix/:type", render: (e) => e.DataExplorerType || notFoundElement(e) },
78
83
  { path: "/data/:prefix/:type/:id", render: (e) => e.DataDetail || notFoundElement(e) },
79
84
  ],
80
85
  },
86
+ { path: "/data-collection", render: (e) => e.DataCollection || notFoundElement(e) },
81
87
  { path: newChatPath, render: (e) => e.Chat },
82
88
  { path: "/chat/:chatId", render: (e) => e.Chat },
83
89
  { path: notFoundRoutePath, render: notFoundElement },
@@ -21,13 +21,13 @@ export type UploadProgress = Record<string, number>;
21
21
 
22
22
  const uploadConcurrency = 6;
23
23
 
24
- export type UseChatUploadResult = {
24
+ export type UseFileUploadResult = {
25
25
  uploadFiles: (files: File[]) => Promise<UploadFilesResult>;
26
26
  isUploading: boolean;
27
27
  progress: UploadProgress;
28
28
  };
29
29
 
30
- export function useChatUpload(): UseChatUploadResult {
30
+ export function useFileUpload(): UseFileUploadResult {
31
31
  const { mantaClient, uploadRelay } = useMantaClient();
32
32
 
33
33
  const { mutateAsync: initiateUpload } = useInitiateUploadMutation(mantaClient);
@@ -0,0 +1,82 @@
1
+ import { fileIdentityKey } from "@manta-eu/lib/file";
2
+ import { isPresent } from "@manta-eu/lib/present";
3
+ import { FolderUp } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { toast } from "sonner";
6
+ import { FileDropzone, UploadProgressList, type RejectedDrop } from "#components/file-dropzone/index";
7
+ import { PageBody, PageHeader } from "#components/layouts/page/index";
8
+ import { RouteLayout } from "#components/layouts/RouteLayout";
9
+ import { useFileUpload } from "#lib/hooks/useFileUpload";
10
+ import { useAuthContext } from "#providers/AuthContext";
11
+ import { UploadedFilesSection } from "#routes/data-explorer/components/uploads/UploadsPages";
12
+ import { useInvalidateUploads } from "#routes/data-explorer/uploads";
13
+
14
+ function showRejected(rejected: RejectedDrop[]) {
15
+ for (const { file, reason } of rejected) toast.error(`${file.name}: ${reason}`);
16
+ }
17
+
18
+ export function DataCollectionUploader() {
19
+ const { uploadFiles, progress } = useFileUpload();
20
+ const invalidateUploads = useInvalidateUploads();
21
+ const [inFlight, setInFlight] = useState<File[]>([]);
22
+
23
+ async function handleFiles(files: File[]) {
24
+ setInFlight((current) => [...current, ...files]);
25
+
26
+ try {
27
+ const { uploaded, failed } = await uploadFiles(files);
28
+
29
+ for (const { file } of failed) toast.error(`${file.name} could not be uploaded. Try again.`);
30
+ if (isPresent(uploaded)) toast.success(`Uploaded ${uploaded.length} of ${files.length} files`);
31
+ } finally {
32
+ const done = new Set(files.map(fileIdentityKey));
33
+
34
+ setInFlight((current) => current.filter((file) => !done.has(fileIdentityKey(file))));
35
+ await invalidateUploads();
36
+ }
37
+ }
38
+
39
+ return (
40
+ <div className="flex flex-col gap-3">
41
+ <FileDropzone onFiles={(files) => void handleFiles(files)} onRejected={showRejected} />
42
+ <UploadProgressList files={inFlight} progress={progress} />
43
+ </div>
44
+ );
45
+ }
46
+
47
+ export function DataCollectionRoute() {
48
+ const { datastore, datastores, user, logout, isOperator, canSwitchDatastore } = useAuthContext();
49
+
50
+ return (
51
+ <RouteLayout
52
+ user={user}
53
+ datastore={datastore}
54
+ datastores={datastores}
55
+ isOperator={isOperator}
56
+ canSwitchDatastore={canSwitchDatastore}
57
+ logout={logout}
58
+ >
59
+ <PageHeader title="Data collection" icon={FolderUp} />
60
+ <PageBody className="px-4 py-6 sm:px-6">
61
+ <div className="mx-auto flex max-w-4xl flex-col gap-8">
62
+ <div className="flex flex-col gap-2">
63
+ <h2 className="typography-header text-default-foreground">Upload your sample data</h2>
64
+ <p className="typography-small max-w-prose text-muted-foreground">
65
+ Add the sample files you want to share with us. They are stored in this datastore only. Remove personal
66
+ data before you upload.
67
+ </p>
68
+ </div>
69
+
70
+ <DataCollectionUploader />
71
+
72
+ <section className="flex flex-col gap-3" aria-labelledby="data-collection-files">
73
+ <h3 id="data-collection-files" className="typography-body-medium text-default-foreground">
74
+ Uploaded files
75
+ </h3>
76
+ <UploadedFilesSection emptyText="Nothing uploaded yet. Files you add appear here with their status." />
77
+ </section>
78
+ </div>
79
+ </PageBody>
80
+ </RouteLayout>
81
+ );
82
+ }
@@ -4,7 +4,7 @@ import { useEffect } from "react";
4
4
  import { Outlet, useMatch } from "react-router";
5
5
  import { useDeclaredNamespaces } from "../declared-namespaces";
6
6
  import { useRailState } from "../hooks/useRailState";
7
- import { coveragePath, explorerPath } from "../namespaces";
7
+ import { coveragePath, explorerPath, filesPath, ingestRunsPath } from "../namespaces";
8
8
  import { Rail } from "./Rail";
9
9
  import { RailSheet } from "./RailSheet";
10
10
  import { LoadingRows } from "./RawTriples";
@@ -31,7 +31,10 @@ export function DataExplorerLayout() {
31
31
  const { datastore, datastores, user, logout, isOperator, canSwitchDatastore } = useAuthContext();
32
32
 
33
33
  const isIndex = Boolean(useMatch(explorerPath));
34
- const isFullPage = Boolean(useMatch(coveragePath));
34
+ const isCoverage = Boolean(useMatch(coveragePath));
35
+ const isFiles = Boolean(useMatch(filesPath));
36
+ const isIngest = Boolean(useMatch(ingestRunsPath));
37
+ const isFullPage = isCoverage || isFiles || isIngest;
35
38
  const railState = useRailState();
36
39
  const { isLoading: isLoadingNamespaces } = useDeclaredNamespaces();
37
40
 
@@ -1,10 +1,19 @@
1
1
  import { isPresent } from "@manta-eu/lib/present";
2
- import { Clock, Gauge } from "lucide-react";
2
+ import { Clock, FileUp, Gauge, Workflow } from "lucide-react";
3
3
  import { useEffect, useMemo, useRef } from "react";
4
4
  import { useMatch } from "react-router";
5
5
  import { buildClassGroups, useExplorerClasses, useInstanceCounts, useSubclassEdges } from "../classes";
6
6
  import { type RailState } from "../hooks/useRailState";
7
- import { coveragePath, explorerPath, isNamespaceSegment, prefixForIri, recentPath, groupKeyFor } from "../namespaces";
7
+ import {
8
+ coveragePath,
9
+ explorerPath,
10
+ filesPath,
11
+ groupKeyFor,
12
+ ingestRunsPath,
13
+ isNamespaceSegment,
14
+ prefixForIri,
15
+ recentPath,
16
+ } from "../namespaces";
8
17
  import { ClassTreeNodes } from "./ClassTreeNodes";
9
18
  import { NamespaceGroupRow } from "./NamespaceGroupRow";
10
19
  import { RailLink } from "./RailLink";
@@ -63,6 +72,8 @@ export function Rail(props: RailProps) {
63
72
  <SearchInput placeholder="Filter types…" value={filter} onChange={(e) => setFilter(e.target.value)} />
64
73
  <RailLink to={recentPath} icon={Clock} label="Recently modified" />
65
74
  <RailLink to={coveragePath} icon={Gauge} label="Ontology coverage" />
75
+ <RailLink to={filesPath} icon={FileUp} label="Files" />
76
+ <RailLink to={ingestRunsPath} icon={Workflow} label="Ingest runs" />
66
77
  </div>
67
78
 
68
79
  <div ref={listRef} className="min-h-0 flex-1 overflow-y-auto px-2 pb-4">