@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.
- package/dist/components/chart/ForecastLegend.d.ts.map +1 -1
- package/dist/components/chart/ForecastLegend.js +10 -2
- package/dist/components/chart/ForecastLegend.js.map +1 -1
- package/dist/components/chart/forecast-model.d.ts.map +1 -1
- package/dist/components/chart/forecast-model.js.map +1 -1
- package/dist/components/chat-view/Chat.js +2 -2
- package/dist/components/file-dropzone/index.d.ts +25 -0
- package/dist/components/file-dropzone/index.d.ts.map +1 -0
- package/dist/components/file-dropzone/index.js +70 -0
- package/dist/components/file-dropzone/index.js.map +1 -0
- package/dist/components/planning/plan-detail-utils.d.ts.map +1 -1
- package/dist/components/planning/plan-detail-utils.js +2 -0
- package/dist/components/planning/plan-detail-utils.js.map +1 -1
- package/dist/components/toast/index.d.ts +1 -0
- package/dist/components/toast/index.d.ts.map +1 -1
- package/dist/components/toast/index.js +1 -0
- package/dist/components/toast/index.js.map +1 -1
- package/dist/globals.css +27 -0
- package/dist/graph/NodeDetailPanel/TypeSummaryPanel.d.ts.map +1 -1
- package/dist/graph/NodeDetailPanel/TypeSummaryPanel.js.map +1 -1
- package/dist/hooks/use-sparql-construct.d.ts +2 -0
- package/dist/hooks/use-sparql-construct.d.ts.map +1 -1
- package/dist/hooks/use-sparql-construct.js +2 -1
- package/dist/hooks/use-sparql-construct.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/datastore-route-table.d.ts +3 -0
- package/dist/lib/datastore-route-table.d.ts.map +1 -1
- package/dist/lib/datastore-route-table.js +3 -0
- package/dist/lib/datastore-route-table.js.map +1 -1
- package/dist/lib/hooks/{useChatUpload.d.ts → useFileUpload.d.ts} +3 -3
- package/dist/lib/hooks/{useChatUpload.d.ts.map → useFileUpload.d.ts.map} +1 -1
- package/dist/lib/hooks/{useChatUpload.js → useFileUpload.js} +2 -2
- package/dist/lib/hooks/{useChatUpload.js.map → useFileUpload.js.map} +1 -1
- package/dist/routes/data-collection/index.d.ts +3 -0
- package/dist/routes/data-collection/index.d.ts.map +1 -0
- package/dist/routes/data-collection/index.js +43 -0
- package/dist/routes/data-collection/index.js.map +1 -0
- package/dist/routes/data-explorer/components/DataExplorerLayout.d.ts.map +1 -1
- package/dist/routes/data-explorer/components/DataExplorerLayout.js +5 -2
- package/dist/routes/data-explorer/components/DataExplorerLayout.js.map +1 -1
- package/dist/routes/data-explorer/components/Rail.d.ts.map +1 -1
- package/dist/routes/data-explorer/components/Rail.js +3 -3
- package/dist/routes/data-explorer/components/Rail.js.map +1 -1
- package/dist/routes/data-explorer/components/uploads/IngestRunsList.d.ts +8 -0
- package/dist/routes/data-explorer/components/uploads/IngestRunsList.d.ts.map +1 -0
- package/dist/routes/data-explorer/components/uploads/IngestRunsList.js +18 -0
- package/dist/routes/data-explorer/components/uploads/IngestRunsList.js.map +1 -0
- package/dist/routes/data-explorer/components/uploads/IngestStateStatus.d.ts +14 -0
- package/dist/routes/data-explorer/components/uploads/IngestStateStatus.d.ts.map +1 -0
- package/dist/routes/data-explorer/components/uploads/IngestStateStatus.js +33 -0
- package/dist/routes/data-explorer/components/uploads/IngestStateStatus.js.map +1 -0
- package/dist/routes/data-explorer/components/uploads/UploadedFilesList.d.ts +8 -0
- package/dist/routes/data-explorer/components/uploads/UploadedFilesList.d.ts.map +1 -0
- package/dist/routes/data-explorer/components/uploads/UploadedFilesList.js +34 -0
- package/dist/routes/data-explorer/components/uploads/UploadedFilesList.js.map +1 -0
- package/dist/routes/data-explorer/components/uploads/UploadsPages.d.ts +7 -0
- package/dist/routes/data-explorer/components/uploads/UploadsPages.d.ts.map +1 -0
- package/dist/routes/data-explorer/components/uploads/UploadsPages.js +35 -0
- package/dist/routes/data-explorer/components/uploads/UploadsPages.js.map +1 -0
- package/dist/routes/data-explorer/index.d.ts +1 -0
- package/dist/routes/data-explorer/index.d.ts.map +1 -1
- package/dist/routes/data-explorer/index.js +1 -0
- package/dist/routes/data-explorer/index.js.map +1 -1
- package/dist/routes/data-explorer/namespaces.d.ts +4 -0
- package/dist/routes/data-explorer/namespaces.d.ts.map +1 -1
- package/dist/routes/data-explorer/namespaces.js +6 -1
- package/dist/routes/data-explorer/namespaces.js.map +1 -1
- package/dist/routes/data-explorer/uploads.d.ts +56 -0
- package/dist/routes/data-explorer/uploads.d.ts.map +1 -0
- package/dist/routes/data-explorer/uploads.js +140 -0
- package/dist/routes/data-explorer/uploads.js.map +1 -0
- package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts +1 -0
- package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts.map +1 -1
- package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js +16 -9
- package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js.map +1 -1
- package/dist/routes/datastores/components/StatusBadge.d.ts +7 -8
- package/dist/routes/datastores/components/StatusBadge.d.ts.map +1 -1
- package/dist/routes/datastores/components/StatusBadge.js +17 -1
- package/dist/routes/datastores/components/StatusBadge.js.map +1 -1
- package/dist/routes/datastores/flatten.d.ts +6 -1
- package/dist/routes/datastores/flatten.d.ts.map +1 -1
- package/dist/routes/datastores/flatten.js +37 -2
- package/dist/routes/datastores/flatten.js.map +1 -1
- package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts +2 -0
- package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts.map +1 -1
- package/dist/routes/datastores/hooks/useDatastoreColumns.js +8 -4
- package/dist/routes/datastores/hooks/useDatastoreColumns.js.map +1 -1
- package/dist/routes/datastores/index.d.ts.map +1 -1
- package/dist/routes/datastores/index.js +53 -18
- package/dist/routes/datastores/index.js.map +1 -1
- package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.js +38 -0
- package/dist/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.js.map +1 -0
- package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.d.ts +4 -0
- package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.js +32 -0
- package/dist/routes/integrations/components/connect-forms/google-merchant-center-account-tree.js.map +1 -0
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.js +1 -0
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.js.map +1 -1
- package/dist/routes/integrations/connect-forms.d.ts +1 -0
- package/dist/routes/integrations/connect-forms.d.ts.map +1 -1
- package/dist/routes/integrations/connect-forms.js +1 -0
- package/dist/routes/integrations/connect-forms.js.map +1 -1
- package/dist/routes/integrations/connection-view.d.ts.map +1 -1
- package/dist/routes/integrations/connection-view.js +1 -0
- package/dist/routes/integrations/connection-view.js.map +1 -1
- package/dist/shared/graph-derived-queries.d.ts.map +1 -1
- package/dist/shared/graph-derived-queries.js +2 -1
- package/dist/shared/graph-derived-queries.js.map +1 -1
- package/package.json +21 -9
- package/src/app-subpaths.json +1 -0
- package/src/components/chart/ForecastLegend.tsx +15 -13
- package/src/components/chart/forecast-model.ts +6 -8
- package/src/components/chat-view/Chat.tsx +2 -2
- package/src/components/file-dropzone/index.tsx +165 -0
- package/src/components/planning/plan-detail-utils.tsx +2 -0
- package/src/components/toast/index.tsx +1 -0
- package/src/graph/NodeDetailPanel/TypeSummaryPanel.tsx +12 -14
- package/src/hooks/use-sparql-construct.ts +4 -2
- package/src/index.ts +1 -0
- package/src/lib/datastore-route-table.ts +6 -0
- package/src/lib/hooks/{useChatUpload.ts → useFileUpload.ts} +2 -2
- package/src/routes/data-collection/index.tsx +82 -0
- package/src/routes/data-explorer/components/DataExplorerLayout.tsx +5 -2
- package/src/routes/data-explorer/components/Rail.tsx +13 -2
- package/src/routes/data-explorer/components/uploads/IngestRunsList.tsx +63 -0
- package/src/routes/data-explorer/components/uploads/IngestStateStatus.tsx +45 -0
- package/src/routes/data-explorer/components/uploads/UploadedFilesList.tsx +93 -0
- package/src/routes/data-explorer/components/uploads/UploadsPages.tsx +80 -0
- package/src/routes/data-explorer/index.tsx +6 -0
- package/src/routes/data-explorer/namespaces.ts +7 -1
- package/src/routes/data-explorer/uploads.ts +205 -0
- package/src/routes/datastores/components/LinkUnlinkedDatastoreDialog.tsx +49 -10
- package/src/routes/datastores/components/StatusBadge.tsx +22 -7
- package/src/routes/datastores/flatten.ts +50 -2
- package/src/routes/datastores/hooks/useDatastoreColumns.tsx +21 -1
- package/src/routes/datastores/index.tsx +64 -13
- package/src/routes/integrations/components/connect-forms/GoogleMerchantCenterConnectForm.tsx +61 -0
- package/src/routes/integrations/components/connect-forms/google-merchant-center-account-tree.ts +38 -0
- package/src/routes/integrations/components/connect-forms/setup-account-unavailable-reason.ts +1 -0
- package/src/routes/integrations/connect-forms.ts +1 -0
- package/src/routes/integrations/connection-view.ts +1 -0
- package/src/shared/graph-derived-queries.ts +2 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { enableDatastoreRemoval } from "@manta-eu/client/shared/constants";
|
|
1
2
|
import { isPresent } from "@manta-eu/lib/present";
|
|
2
3
|
import { LoaderCircle } from "lucide-react";
|
|
3
4
|
import { Badge, type BadgeVariant } from "#components/badge/index";
|
|
@@ -7,13 +8,13 @@ import {
|
|
|
7
8
|
type DatastoreStatusCategory,
|
|
8
9
|
} from "#components/datastore-management/tree";
|
|
9
10
|
|
|
10
|
-
export type DatastoreStatusFilterValue = "noDatastore" | "checking" | DatastoreStatusCategory;
|
|
11
|
+
export type DatastoreStatusFilterValue = "noDatastore" | "checking" | "notBuilt" | DatastoreStatusCategory;
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
): DatastoreStatusFilterValue {
|
|
13
|
+
type StatusNode = { datastoreId?: string; removed: boolean; isUnlinked?: boolean; unbuilt?: boolean };
|
|
14
|
+
|
|
15
|
+
export function datastoreStatusFilterValue(node: StatusNode, status?: DatastoreStatus): DatastoreStatusFilterValue {
|
|
16
16
|
if (!isPresent(node.datastoreId)) return "noDatastore";
|
|
17
|
+
if (node.unbuilt === true && status?.exists !== true) return "notBuilt";
|
|
17
18
|
return datastoreStatusCategory(node, status) ?? "checking";
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -33,10 +34,23 @@ const badgeConfigByFilterValue: Record<DatastoreStatusFilterValue, BadgeConfig>
|
|
|
33
34
|
variant: "destructive",
|
|
34
35
|
title: "This id never reached the datastore registry, so nothing will build it. Provision it again.",
|
|
35
36
|
},
|
|
37
|
+
notBuilt: {
|
|
38
|
+
label: "Not built",
|
|
39
|
+
variant: "warning",
|
|
40
|
+
title: "Registered in Infisical, but no datastore exists for it. Link it to build it, or delete it.",
|
|
41
|
+
},
|
|
36
42
|
offline: { label: "Offline", variant: "destructive" },
|
|
37
43
|
unavailable: { label: "Status unavailable", variant: "copy" },
|
|
38
44
|
checking: { label: "Checking…", variant: "copy", spinning: true },
|
|
39
|
-
removed:
|
|
45
|
+
removed: enableDatastoreRemoval
|
|
46
|
+
? { label: "Removed", variant: "copy" }
|
|
47
|
+
: {
|
|
48
|
+
label: "Removed",
|
|
49
|
+
variant: "warning",
|
|
50
|
+
title:
|
|
51
|
+
"Out of use: a permanent delete stopped partway, or it was removed before removal was turned off. " +
|
|
52
|
+
"Delete it permanently only if it should go for good.",
|
|
53
|
+
},
|
|
40
54
|
noDatastore: { label: "No datastore", variant: "copy" },
|
|
41
55
|
};
|
|
42
56
|
|
|
@@ -44,6 +58,7 @@ const filterOptionOrder: DatastoreStatusFilterValue[] = [
|
|
|
44
58
|
"ready",
|
|
45
59
|
"materializing",
|
|
46
60
|
"notRegistered",
|
|
61
|
+
"notBuilt",
|
|
47
62
|
"offline",
|
|
48
63
|
"unavailable",
|
|
49
64
|
"removed",
|
|
@@ -55,7 +70,7 @@ export const datastoreStatusFilterOptions: { id: "ALL" | DatastoreStatusFilterVa
|
|
|
55
70
|
];
|
|
56
71
|
|
|
57
72
|
export type DatastoreStatusBadgeProps = {
|
|
58
|
-
node:
|
|
73
|
+
node: StatusNode;
|
|
59
74
|
status?: DatastoreStatus;
|
|
60
75
|
};
|
|
61
76
|
|
|
@@ -25,6 +25,7 @@ export type DatastoreRow = {
|
|
|
25
25
|
// Real in the datastore, but no Keycloak node behind it, so it can only be selected.
|
|
26
26
|
isRealmDefault?: boolean;
|
|
27
27
|
isUnlinked?: boolean;
|
|
28
|
+
unbuilt?: boolean;
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
export function realmDefaultRow(datastoreId: Realm): DatastoreRow {
|
|
@@ -105,6 +106,15 @@ export function hiddenRowCount(
|
|
|
105
106
|
return visibleDatastoreRows(rows, { ...visibility, [key]: true }).length - shownCount;
|
|
106
107
|
}
|
|
107
108
|
|
|
109
|
+
export function hiddenRemovedRowCount(rows: DatastoreRow[], visibility: DatastoreRowVisibility): number {
|
|
110
|
+
if (visibility.showArchived) return 0;
|
|
111
|
+
|
|
112
|
+
const shown = new Set(visibleDatastoreRows(rows, visibility));
|
|
113
|
+
return rows.filter(
|
|
114
|
+
(row) => row.removed && !shown.has(row) && matchesVisibility(row, { ...visibility, showArchived: true }),
|
|
115
|
+
).length;
|
|
116
|
+
}
|
|
117
|
+
|
|
108
118
|
// Matches Keycloak's own org lookup (ensureOrganization), which is case-insensitive and trims too.
|
|
109
119
|
export function organizationNameKey(name: string): string {
|
|
110
120
|
return name.trim().toLowerCase();
|
|
@@ -118,6 +128,33 @@ export function organizationNames(rows: DatastoreRow[]): string[] {
|
|
|
118
128
|
return [...new Set(rows.filter((row) => row.isOrganization).map((row) => row.organizationName))].sort();
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
export function removedSubtreeRowIds(rows: DatastoreRow[]): Set<string> {
|
|
132
|
+
const ids = new Set<string>();
|
|
133
|
+
let removedDepth: number | undefined;
|
|
134
|
+
for (const row of rows) {
|
|
135
|
+
if (removedDepth === undefined || row.depth <= removedDepth) removedDepth = row.removed ? row.depth : undefined;
|
|
136
|
+
if (removedDepth !== undefined) ids.add(row.id);
|
|
137
|
+
}
|
|
138
|
+
return ids;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function outsideRemovedSubtrees(rows: DatastoreRow[]): DatastoreRow[] {
|
|
142
|
+
const removed = removedSubtreeRowIds(rows);
|
|
143
|
+
return rows.filter((row) => !removed.has(row.id));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function organizationGroupPaths(rows: DatastoreRow[]): Map<string, string[]> {
|
|
147
|
+
const paths = new Map<string, string[]>();
|
|
148
|
+
for (const row of outsideRemovedSubtrees(rows)) {
|
|
149
|
+
if (row.isOrganization || row.isRealmDefault === true || row.isUnlinked === true) continue;
|
|
150
|
+
|
|
151
|
+
const organizationPaths = paths.get(row.organizationName);
|
|
152
|
+
if (organizationPaths) organizationPaths.push(row.path);
|
|
153
|
+
else paths.set(row.organizationName, [row.path]);
|
|
154
|
+
}
|
|
155
|
+
return paths;
|
|
156
|
+
}
|
|
157
|
+
|
|
121
158
|
export function visibilityForStatusFilter(
|
|
122
159
|
visibility: DatastoreRowVisibility,
|
|
123
160
|
status: DatastoreStatusFilterValue | "ALL",
|
|
@@ -125,11 +162,21 @@ export function visibilityForStatusFilter(
|
|
|
125
162
|
return match(status)
|
|
126
163
|
.with("removed", () => ({ ...visibility, showArchived: true }))
|
|
127
164
|
.with("noDatastore", () => ({ ...visibility, showUnprovisioned: true }))
|
|
128
|
-
.with(
|
|
165
|
+
.with(
|
|
166
|
+
"ALL",
|
|
167
|
+
"ready",
|
|
168
|
+
"materializing",
|
|
169
|
+
"notRegistered",
|
|
170
|
+
"notBuilt",
|
|
171
|
+
"offline",
|
|
172
|
+
"unavailable",
|
|
173
|
+
"checking",
|
|
174
|
+
() => visibility,
|
|
175
|
+
)
|
|
129
176
|
.exhaustive();
|
|
130
177
|
}
|
|
131
178
|
|
|
132
|
-
export function unlinkedDatastoreRow(datastoreId: string): DatastoreRow {
|
|
179
|
+
export function unlinkedDatastoreRow(datastoreId: string, built = true): DatastoreRow {
|
|
133
180
|
return {
|
|
134
181
|
id: `unlinked:${datastoreId}`,
|
|
135
182
|
name: datastoreId,
|
|
@@ -142,6 +189,7 @@ export function unlinkedDatastoreRow(datastoreId: string): DatastoreRow {
|
|
|
142
189
|
removed: false,
|
|
143
190
|
isOrganization: false,
|
|
144
191
|
isUnlinked: true,
|
|
192
|
+
unbuilt: !built,
|
|
145
193
|
};
|
|
146
194
|
}
|
|
147
195
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Blocks,
|
|
7
7
|
Check,
|
|
8
8
|
DatabaseZap,
|
|
9
|
+
Hammer,
|
|
9
10
|
Link2,
|
|
10
11
|
MoreHorizontal,
|
|
11
12
|
Pencil,
|
|
@@ -113,6 +114,8 @@ type UseDatastoreColumnsArgs = {
|
|
|
113
114
|
onDelete: (row: DatastoreRow) => void;
|
|
114
115
|
onRename: (row: DatastoreRow) => void;
|
|
115
116
|
onRegister: (row: DatastoreRow) => void;
|
|
117
|
+
registeringDatastoreIds: ReadonlySet<string>;
|
|
118
|
+
removedSubtreeRowIds: ReadonlySet<string>;
|
|
116
119
|
onManageUsers: (row: DatastoreRow) => void;
|
|
117
120
|
onManageCustomApps: (row: DatastoreRow) => void;
|
|
118
121
|
onBringOnline: (row: DatastoreRow) => void;
|
|
@@ -141,6 +144,8 @@ export function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<Da
|
|
|
141
144
|
onDelete,
|
|
142
145
|
onRename,
|
|
143
146
|
onRegister,
|
|
147
|
+
registeringDatastoreIds,
|
|
148
|
+
removedSubtreeRowIds,
|
|
144
149
|
onManageUsers,
|
|
145
150
|
onManageCustomApps,
|
|
146
151
|
onBringOnline,
|
|
@@ -311,6 +316,8 @@ export function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<Da
|
|
|
311
316
|
const status = hasDatastore ? statusByDatastoreId[datastoreId] : undefined;
|
|
312
317
|
const category = datastoreStatusCategory(datastoreRow, status);
|
|
313
318
|
const notRegistered = category === "notRegistered";
|
|
319
|
+
const buildable = category === "materializing";
|
|
320
|
+
const registering = hasDatastore && registeringDatastoreIds.has(datastoreId);
|
|
314
321
|
const reason = match({ notRegistered, materializing: isMaterializing(status) && !isRealmDefault })
|
|
315
322
|
.with({ notRegistered: true }, () => "Not registered - use Register to fix, then try selecting again.")
|
|
316
323
|
.with({ materializing: true }, () => "Still materializing - this can take a few minutes.")
|
|
@@ -359,12 +366,13 @@ export function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<Da
|
|
|
359
366
|
Bring online
|
|
360
367
|
</Button>
|
|
361
368
|
)}
|
|
362
|
-
{!isUnlinked && hasDatastore && !
|
|
369
|
+
{!isUnlinked && hasDatastore && !removedSubtreeRowIds.has(datastoreRow.id) && notRegistered && (
|
|
363
370
|
<Button
|
|
364
371
|
data-testid={`datastore-register-${datastoreRow.id}`}
|
|
365
372
|
size="sm"
|
|
366
373
|
variant="secondary"
|
|
367
374
|
icon={RefreshCw}
|
|
375
|
+
disabled={registering}
|
|
368
376
|
onClick={() => onRegister(datastoreRow)}
|
|
369
377
|
>
|
|
370
378
|
Register
|
|
@@ -439,6 +447,16 @@ export function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<Da
|
|
|
439
447
|
Provision datastore
|
|
440
448
|
</DropdownItem>
|
|
441
449
|
))}
|
|
450
|
+
{hasDatastore && !removedSubtreeRowIds.has(datastoreRow.id) && buildable && (
|
|
451
|
+
<DropdownItem
|
|
452
|
+
icon={<Hammer className="size-4" />}
|
|
453
|
+
disabled={registering}
|
|
454
|
+
title="Starts a build, or joins the one already running"
|
|
455
|
+
onSelect={() => onRegister(datastoreRow)}
|
|
456
|
+
>
|
|
457
|
+
Build datastore now
|
|
458
|
+
</DropdownItem>
|
|
459
|
+
)}
|
|
442
460
|
<DropdownItem icon={<Plus className="size-4" />} onSelect={() => onAddChild(datastoreRow)}>
|
|
443
461
|
Add datastore
|
|
444
462
|
</DropdownItem>
|
|
@@ -505,6 +523,8 @@ export function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<Da
|
|
|
505
523
|
onRestore,
|
|
506
524
|
onDelete,
|
|
507
525
|
onRegister,
|
|
526
|
+
registeringDatastoreIds,
|
|
527
|
+
removedSubtreeRowIds,
|
|
508
528
|
onRename,
|
|
509
529
|
onManageUsers,
|
|
510
530
|
onManageCustomApps,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
useRenameDatastoreGroupMutation,
|
|
12
12
|
useRestoreDatastoreMutation,
|
|
13
13
|
useUnlinkedDatastoresQuery,
|
|
14
|
+
type GQLRegisterDatastoreMutationVariables,
|
|
14
15
|
} from "@manta-eu/client";
|
|
15
16
|
import { enableDatastoreRemoval } from "@manta-eu/client/shared/constants";
|
|
16
17
|
import { deriveDatastoreId } from "@manta-eu/client/shared/datastore-id";
|
|
@@ -18,7 +19,7 @@ import { parseError } from "@manta-eu/client/shared/error-utils";
|
|
|
18
19
|
import { namesAGroup, pathSegments } from "@manta-eu/client/shared/team";
|
|
19
20
|
import { activeRealm } from "@manta-eu/ldo/realm";
|
|
20
21
|
import { isPresent } from "@manta-eu/lib/present";
|
|
21
|
-
import { useQueryClient } from "@tanstack/react-query";
|
|
22
|
+
import { useMutationState, useQueryClient } from "@tanstack/react-query";
|
|
22
23
|
import type { SortingState } from "@tanstack/react-table";
|
|
23
24
|
import { Archive, Database, EyeOff, type LucideIcon, Plus, RefreshCcw, TriangleAlert } from "lucide-react";
|
|
24
25
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
@@ -34,12 +35,16 @@ import { RenameDatastoreGroupDialog } from "./components/RenameDatastoreGroupDia
|
|
|
34
35
|
import { UnlinkedDatastoresPanel } from "./components/UnlinkedDatastoresPanel";
|
|
35
36
|
import {
|
|
36
37
|
existingOrganizationNameKeys,
|
|
38
|
+
hiddenRemovedRowCount,
|
|
37
39
|
hiddenRowCount,
|
|
38
40
|
matchesProviderFilter,
|
|
39
41
|
nestedDatastoreNamesByRowId,
|
|
42
|
+
organizationGroupPaths,
|
|
40
43
|
organizationNameKey,
|
|
41
44
|
organizationNames,
|
|
45
|
+
outsideRemovedSubtrees,
|
|
42
46
|
realmDefaultRow,
|
|
47
|
+
removedSubtreeRowIds,
|
|
43
48
|
toDatastoreRows,
|
|
44
49
|
unlinkedDatastoreRow,
|
|
45
50
|
visibilityForStatusFilter,
|
|
@@ -75,6 +80,8 @@ import { useMantaClient } from "#providers/index";
|
|
|
75
80
|
|
|
76
81
|
const realmDatastoreId = activeRealm();
|
|
77
82
|
|
|
83
|
+
const registerDatastoreMutationKey = ["RegisterDatastore"];
|
|
84
|
+
|
|
78
85
|
type ActiveTab = "organizations" | "unlinked";
|
|
79
86
|
|
|
80
87
|
const tabLabels: Record<ActiveTab, string> = {
|
|
@@ -233,17 +240,37 @@ export function DatastoresRoute() {
|
|
|
233
240
|
});
|
|
234
241
|
|
|
235
242
|
const registerMutation = useRegisterDatastoreMutation(mantaClient, {
|
|
236
|
-
|
|
243
|
+
mutationKey: registerDatastoreMutationKey,
|
|
244
|
+
onSuccess: async (result, variables) => {
|
|
237
245
|
// "Not registered" is treated as settled, so status polling stopped - force a re-check.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
246
|
+
await refreshDatastores();
|
|
247
|
+
const { success, error, pending } = result.registerDatastore;
|
|
248
|
+
if (!success) {
|
|
249
|
+
toast.error("Couldn't build the datastore", { description: error ?? undefined });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (pending) {
|
|
254
|
+
toast.success(`Datastore "${variables.datastoreId}" is being built`, {
|
|
255
|
+
description: "It is taking longer than usual, and becomes available once its knowledge graph is built.",
|
|
256
|
+
});
|
|
257
|
+
} else {
|
|
258
|
+
toast.success(`Datastore "${variables.datastoreId}" is registered and built`);
|
|
259
|
+
}
|
|
241
260
|
},
|
|
242
261
|
onError: (error) => {
|
|
243
|
-
const info = parseError(error, "Couldn't
|
|
262
|
+
const info = parseError(error, "Couldn't build the datastore");
|
|
244
263
|
toast.error(info.message, { description: info.description });
|
|
245
264
|
},
|
|
246
265
|
});
|
|
266
|
+
const registeringIds = useMutationState({
|
|
267
|
+
filters: { mutationKey: registerDatastoreMutationKey, status: "pending" },
|
|
268
|
+
select: (mutation) => (mutation.state.variables as GQLRegisterDatastoreMutationVariables | undefined)?.datastoreId,
|
|
269
|
+
});
|
|
270
|
+
const registeringDatastoreIds = useMemo(
|
|
271
|
+
() => new Set(registeringIds.filter((id): id is string => id !== undefined)),
|
|
272
|
+
[registeringIds],
|
|
273
|
+
);
|
|
247
274
|
|
|
248
275
|
const renameMutation = useRenameDatastoreGroupMutation(mantaClient, {
|
|
249
276
|
onSuccess: (result) => {
|
|
@@ -313,7 +340,7 @@ export function DatastoresRoute() {
|
|
|
313
340
|
[data],
|
|
314
341
|
);
|
|
315
342
|
const unlinkedRows = useMemo(
|
|
316
|
-
() => (unlinkedData?.unlinkedDatastores ?? []).map((entry) => unlinkedDatastoreRow(entry.id)),
|
|
343
|
+
() => (unlinkedData?.unlinkedDatastores ?? []).map((entry) => unlinkedDatastoreRow(entry.id, entry.built)),
|
|
317
344
|
[unlinkedData],
|
|
318
345
|
);
|
|
319
346
|
const allRows = useMemo(() => [...treeRows, ...unlinkedRows], [treeRows, unlinkedRows]);
|
|
@@ -323,6 +350,9 @@ export function DatastoresRoute() {
|
|
|
323
350
|
() => hiddenRowCount(treeRows, visibility, "showUnprovisioned", rows.length),
|
|
324
351
|
[treeRows, visibility, rows.length],
|
|
325
352
|
);
|
|
353
|
+
const archivedCount = useMemo(() => hiddenRemovedRowCount(treeRows, visibility), [treeRows, visibility]);
|
|
354
|
+
const groupPathsByOrganization = useMemo(() => organizationGroupPaths(treeRows), [treeRows]);
|
|
355
|
+
const removedSubtreeIds = useMemo(() => removedSubtreeRowIds(treeRows), [treeRows]);
|
|
326
356
|
const providerLinkEnabled = linkingQuery.data?.organizationLinking?.linkEnabled;
|
|
327
357
|
const linkingUnreadable = linkingQuery.isError || linkingQuery.data?.organizationLinking === null;
|
|
328
358
|
const tableRows = useMemo(
|
|
@@ -331,7 +361,7 @@ export function DatastoresRoute() {
|
|
|
331
361
|
);
|
|
332
362
|
const unlinkedCount = unlinkedRows.length;
|
|
333
363
|
const existingOrganizationNames = useMemo(() => existingOrganizationNameKeys(allRows), [allRows]);
|
|
334
|
-
const organizationNameOptions = useMemo(() => organizationNames(allRows), [allRows]);
|
|
364
|
+
const organizationNameOptions = useMemo(() => organizationNames(outsideRemovedSubtrees(allRows)), [allRows]);
|
|
335
365
|
// Mirrors provisionDatastore's assertDatastoreIdAvailable (Infisical folder-per-id is global,
|
|
336
366
|
// not scoped to siblings), so a slug collision is caught before the server round-trip.
|
|
337
367
|
const existingDatastoreIds = useMemo(
|
|
@@ -354,7 +384,7 @@ export function DatastoresRoute() {
|
|
|
354
384
|
);
|
|
355
385
|
const suggestedMatchByDatastoreId = useMemo(() => {
|
|
356
386
|
const matches: Record<string, DatastoreRow> = {};
|
|
357
|
-
for (const row of treeRows) {
|
|
387
|
+
for (const row of outsideRemovedSubtrees(treeRows)) {
|
|
358
388
|
if (isPresent(row.datastoreId)) continue;
|
|
359
389
|
const derived = derivedIdForRow(row);
|
|
360
390
|
if (unlinkedIdSet.has(derived)) matches[derived] ??= row;
|
|
@@ -383,7 +413,7 @@ export function DatastoresRoute() {
|
|
|
383
413
|
|
|
384
414
|
const matchableRowIds = useMemo(() => {
|
|
385
415
|
const ids = new Set<string>();
|
|
386
|
-
for (const row of allRows) {
|
|
416
|
+
for (const row of outsideRemovedSubtrees(allRows)) {
|
|
387
417
|
if (!isPresent(row.datastoreId) && unlinkedIdSet.has(derivedIdForRow(row))) ids.add(row.id);
|
|
388
418
|
}
|
|
389
419
|
return ids;
|
|
@@ -410,7 +440,11 @@ export function DatastoresRoute() {
|
|
|
410
440
|
void queryClient.invalidateQueries({ queryKey: useUnlinkedDatastoresQuery.getKey() });
|
|
411
441
|
void queryClient.invalidateQueries({ queryKey: ["DatastoreProvisioningStatuses"] });
|
|
412
442
|
if (result.linkUnlinkedDatastore.success) {
|
|
413
|
-
toast.success(
|
|
443
|
+
toast.success(
|
|
444
|
+
result.linkUnlinkedDatastore.pending
|
|
445
|
+
? `Linked to "${result.linkUnlinkedDatastore.datastoreId}", which is still being built`
|
|
446
|
+
: `Linked to the existing datastore "${result.linkUnlinkedDatastore.datastoreId}"`,
|
|
447
|
+
);
|
|
414
448
|
toastProviderWarning(result.linkUnlinkedDatastore.providerWarning);
|
|
415
449
|
} else {
|
|
416
450
|
toast.error("Couldn't link the existing datastore", {
|
|
@@ -471,7 +505,9 @@ export function DatastoresRoute() {
|
|
|
471
505
|
|
|
472
506
|
const onRegister = useCallback(
|
|
473
507
|
(row: DatastoreRow) => {
|
|
474
|
-
if (isPresent(row.datastoreId))
|
|
508
|
+
if (isPresent(row.datastoreId)) {
|
|
509
|
+
registerMutation.mutate({ datastoreId: row.datastoreId, launchProvisioning: true });
|
|
510
|
+
}
|
|
475
511
|
},
|
|
476
512
|
[registerMutation.mutate],
|
|
477
513
|
);
|
|
@@ -524,6 +560,8 @@ export function DatastoresRoute() {
|
|
|
524
560
|
onRestore,
|
|
525
561
|
onRename: setRenameTarget,
|
|
526
562
|
onRegister,
|
|
563
|
+
registeringDatastoreIds,
|
|
564
|
+
removedSubtreeRowIds: removedSubtreeIds,
|
|
527
565
|
onManageUsers,
|
|
528
566
|
onManageCustomApps,
|
|
529
567
|
onBringOnline,
|
|
@@ -545,7 +583,19 @@ export function DatastoresRoute() {
|
|
|
545
583
|
onToggle: () => toggleVisibility("showArchived"),
|
|
546
584
|
},
|
|
547
585
|
]
|
|
548
|
-
:
|
|
586
|
+
: showArchived || archivedCount > 0
|
|
587
|
+
? [
|
|
588
|
+
{
|
|
589
|
+
key: "archived",
|
|
590
|
+
label: showArchived ? "Hide removed" : `Show removed (${archivedCount})`,
|
|
591
|
+
description:
|
|
592
|
+
"Out of use: a permanent delete stopped partway, or they were removed before removal was turned off.",
|
|
593
|
+
icon: showArchived ? EyeOff : Archive,
|
|
594
|
+
active: showArchived,
|
|
595
|
+
onToggle: () => toggleVisibility("showArchived"),
|
|
596
|
+
},
|
|
597
|
+
]
|
|
598
|
+
: []),
|
|
549
599
|
...(showUnprovisioned || unprovisionedCount > 0
|
|
550
600
|
? [
|
|
551
601
|
{
|
|
@@ -813,6 +863,7 @@ export function DatastoresRoute() {
|
|
|
813
863
|
datastoreId={linkDialogTarget}
|
|
814
864
|
suggestedMatch={suggestedLinkTarget(linkDialogTarget)}
|
|
815
865
|
organizationNames={organizationNameOptions}
|
|
866
|
+
groupPathsByOrganization={groupPathsByOrganization}
|
|
816
867
|
existingOrganizationNames={existingOrganizationNames}
|
|
817
868
|
onClose={() => setLinkDialogTarget(null)}
|
|
818
869
|
/>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GQLIntegration,
|
|
3
|
+
useGoogleMerchantCenterSetupConnectMutation,
|
|
4
|
+
useGoogleMerchantCenterSetupProgressQuery,
|
|
5
|
+
type GQLGoogleMerchantCenterAccount,
|
|
6
|
+
} from "@manta-eu/client";
|
|
7
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
8
|
+
import { googleMerchantCenterTreeItems } from "./google-merchant-center-account-tree";
|
|
9
|
+
import { OAuthSetupConnectForm, type OAuthSetup, type SetupCopy } from "./OAuthSetupConnectForm";
|
|
10
|
+
import type { ConnectFormProps } from "./types";
|
|
11
|
+
import { useMantaClient } from "#providers/index";
|
|
12
|
+
|
|
13
|
+
const copy: SetupCopy = {
|
|
14
|
+
providerName: "Merchant Center",
|
|
15
|
+
identityProviderName: "Google",
|
|
16
|
+
entitySingular: "account",
|
|
17
|
+
entityPlural: "accounts",
|
|
18
|
+
signInDescription:
|
|
19
|
+
"We'll take you to Google sign-in so you can grant Manta access to your Google Merchant Center accounts.",
|
|
20
|
+
pickTitle: "Pick the Merchant Center accounts to connect",
|
|
21
|
+
pickDescription:
|
|
22
|
+
"Choose the accounts whose products Manta should manage. An advanced account only groups its sub-accounts, so connect the sub-accounts. An account whose latest terms aren't accepted yet can be connected once they are accepted in Merchant Center.",
|
|
23
|
+
noEntitiesLabel:
|
|
24
|
+
"We couldn't find any Merchant Center accounts on this Google identity. Sign in with a different account or check your Merchant Center access.",
|
|
25
|
+
noneConnectableLabel:
|
|
26
|
+
"No connectable Merchant Center accounts remain on this Google identity — they are already connected or unavailable. Sign in with a different Google account to connect a new one.",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function useGoogleMerchantCenterSetup(): OAuthSetup<GQLGoogleMerchantCenterAccount> {
|
|
30
|
+
const { mantaClient } = useMantaClient();
|
|
31
|
+
const queryClient = useQueryClient();
|
|
32
|
+
const progressQuery = useGoogleMerchantCenterSetupProgressQuery(mantaClient, undefined, { staleTime: 0 });
|
|
33
|
+
const connectMutation = useGoogleMerchantCenterSetupConnectMutation(mantaClient);
|
|
34
|
+
const progress = progressQuery.data?.googleMerchantCenterSetupProgress;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
progress: progress && { state: progress.state, rows: progress.accounts, lastError: progress.lastError },
|
|
38
|
+
refetchProgress: progressQuery.refetch,
|
|
39
|
+
resetProgress: () =>
|
|
40
|
+
queryClient.setQueryData(useGoogleMerchantCenterSetupProgressQuery.getKey(), {
|
|
41
|
+
googleMerchantCenterSetupProgress: { state: [], accounts: null, lastError: null },
|
|
42
|
+
}),
|
|
43
|
+
connect: async (accountIds) => (await connectMutation.mutateAsync({ accountIds })).googleMerchantCenterSetupConnect,
|
|
44
|
+
isConnecting: connectMutation.isPending,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function GoogleMerchantCenterConnectForm(props: ConnectFormProps) {
|
|
49
|
+
const setup = useGoogleMerchantCenterSetup();
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<OAuthSetupConnectForm
|
|
53
|
+
{...props}
|
|
54
|
+
integration={GQLIntegration.GoogleMerchantCenter}
|
|
55
|
+
oauthStartPath="/oauth/google-merchant-center/start"
|
|
56
|
+
copy={copy}
|
|
57
|
+
setup={setup}
|
|
58
|
+
treeItems={googleMerchantCenterTreeItems}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
package/src/routes/integrations/components/connect-forms/google-merchant-center-account-tree.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { GQLGoogleMerchantCenterAccount } from "@manta-eu/client";
|
|
2
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
3
|
+
import { unavailableReasonLabel } from "./setup-account-unavailable-reason";
|
|
4
|
+
import { buildTreeRows, type TreeSelectItem } from "#components/tree-multi-select/index";
|
|
5
|
+
|
|
6
|
+
function rowHint(
|
|
7
|
+
row: GQLGoogleMerchantCenterAccount,
|
|
8
|
+
alreadyConnected: boolean,
|
|
9
|
+
reconnectable: boolean,
|
|
10
|
+
): string | undefined {
|
|
11
|
+
if (row.advanced === true) return "Advanced account";
|
|
12
|
+
if (row.isConnectable !== true) return unavailableReasonLabel(row.unavailableReason);
|
|
13
|
+
if (reconnectable) return "Reconnect";
|
|
14
|
+
if (alreadyConnected) return "Already connected";
|
|
15
|
+
if (row.testAccount === true) return "Test account";
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function googleMerchantCenterTreeItems(
|
|
20
|
+
rows: GQLGoogleMerchantCenterAccount[],
|
|
21
|
+
alreadyConnectedExternalIds: ReadonlySet<string>,
|
|
22
|
+
reconnectableExternalIds: ReadonlySet<string> = new Set(),
|
|
23
|
+
): TreeSelectItem[] {
|
|
24
|
+
return buildTreeRows(rows).map(({ node, depth }) => {
|
|
25
|
+
const reconnectable = isPresent(node.externalId) && reconnectableExternalIds.has(node.externalId);
|
|
26
|
+
const alreadyConnected =
|
|
27
|
+
!reconnectable && isPresent(node.externalId) && alreadyConnectedExternalIds.has(node.externalId);
|
|
28
|
+
return {
|
|
29
|
+
id: node.id,
|
|
30
|
+
label: node.name ?? node.externalId ?? node.id,
|
|
31
|
+
caption: node.name != null ? (node.externalId ?? undefined) : undefined,
|
|
32
|
+
hint: rowHint(node, alreadyConnected, reconnectable),
|
|
33
|
+
selectable: node.isConnectable === true,
|
|
34
|
+
disabled: alreadyConnected,
|
|
35
|
+
depth,
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
package/src/routes/integrations/components/connect-forms/setup-account-unavailable-reason.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { isPresent } from "@manta-eu/lib/present";
|
|
|
3
3
|
|
|
4
4
|
const unavailableReasonLabels: Record<GQLSetupAccountUnavailableReason, string> = {
|
|
5
5
|
[GQLSetupAccountUnavailableReason.InsufficientPermissions]: "Insufficient permissions",
|
|
6
|
+
[GQLSetupAccountUnavailableReason.TermsNotAccepted]: "Accept the latest terms in Merchant Center",
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
export function unavailableReasonLabel(reason: GQLSetupAccountUnavailableReason | null | undefined): string {
|
|
@@ -11,6 +11,7 @@ export { FigmaConnectForm } from "./components/connect-forms/FigmaConnectForm";
|
|
|
11
11
|
export { GmailConnectForm } from "./components/connect-forms/GmailConnectForm";
|
|
12
12
|
export { GoogleAdsConnectForm } from "./components/connect-forms/GoogleAdsConnectForm";
|
|
13
13
|
export { GoogleAnalyticsConnectForm } from "./components/connect-forms/GoogleAnalyticsConnectForm";
|
|
14
|
+
export { GoogleMerchantCenterConnectForm } from "./components/connect-forms/GoogleMerchantCenterConnectForm";
|
|
14
15
|
export { KlaviyoConnectForm } from "./components/connect-forms/KlaviyoConnectForm";
|
|
15
16
|
export { LinkedinConnectForm } from "./components/connect-forms/LinkedinConnectForm";
|
|
16
17
|
export { MetaAdsConnectForm } from "./components/connect-forms/MetaAdsConnectForm";
|
|
@@ -42,6 +42,7 @@ const oauthAccountNoun = {
|
|
|
42
42
|
[GQLIntegration.Slack]: "Workspace",
|
|
43
43
|
[GQLIntegration.Google]: "Customer",
|
|
44
44
|
[GQLIntegration.GoogleAnalytics]: "Property",
|
|
45
|
+
[GQLIntegration.GoogleMerchantCenter]: "Account",
|
|
45
46
|
[GQLIntegration.Meta]: "Ad account",
|
|
46
47
|
[GQLIntegration.Linkedin]: "Ad account",
|
|
47
48
|
[GQLIntegration.MicrosoftAds]: "Account",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
useCurrentFactValueQuery,
|
|
11
11
|
useExplainFactQuery,
|
|
12
12
|
useGetActionPlansSummaryQuery,
|
|
13
|
+
useManualActionProgressQuery,
|
|
13
14
|
useMemoriesQuery,
|
|
14
15
|
useMemorySchemasQuery,
|
|
15
16
|
useNodePropertiesQuery,
|
|
@@ -36,6 +37,7 @@ export const graphDerivedQueries: QueryWithGetKey[] = [
|
|
|
36
37
|
useCurrentFactValueQuery,
|
|
37
38
|
useExplainFactQuery,
|
|
38
39
|
useGetActionPlansSummaryQuery,
|
|
40
|
+
useManualActionProgressQuery,
|
|
39
41
|
useMemoriesQuery,
|
|
40
42
|
useMemorySchemasQuery,
|
|
41
43
|
useNodePropertiesQuery,
|