@manta-eu/react 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +4 -8
  2. package/dist/components/audience-phase-badge/index.d.ts +1 -1
  3. package/dist/components/audience-phase-badge/index.d.ts.map +1 -1
  4. package/dist/components/audience-phase-badge/index.js +3 -0
  5. package/dist/components/authenticated-chat/index.d.ts.map +1 -1
  6. package/dist/components/authenticated-chat/index.js +0 -5
  7. package/dist/components/chat-view/Chat.d.ts +6 -0
  8. package/dist/components/chat-view/Chat.d.ts.map +1 -1
  9. package/dist/components/chat-view/Chat.js +6 -6
  10. package/dist/components/chat-view/ChatHeaders.d.ts.map +1 -1
  11. package/dist/components/chat-view/ChatHeaders.js +3 -2
  12. package/dist/components/embedded-chat/EmbeddedDatastorePicker.d.ts +1 -0
  13. package/dist/components/embedded-chat/EmbeddedDatastorePicker.d.ts.map +1 -1
  14. package/dist/components/embedded-chat/EmbeddedDatastorePicker.js +9 -3
  15. package/dist/components/embedded-chat/index.d.ts.map +1 -1
  16. package/dist/components/embedded-chat/index.js +1 -8
  17. package/dist/components/layouts/Sidebar.d.ts.map +1 -1
  18. package/dist/components/layouts/Sidebar.js +11 -4
  19. package/dist/components/planning/ActioningView.d.ts.map +1 -1
  20. package/dist/components/planning/ActioningView.js +11 -6
  21. package/dist/components/planning/ExecutionSectionItem.d.ts +2 -3
  22. package/dist/components/planning/ExecutionSectionItem.d.ts.map +1 -1
  23. package/dist/components/planning/ExecutionSectionItem.js +3 -6
  24. package/dist/components/planning/ExecutionSections.d.ts +2 -3
  25. package/dist/components/planning/ExecutionSections.d.ts.map +1 -1
  26. package/dist/components/planning/ExecutionSections.js +2 -2
  27. package/dist/components/planning/ManualActionCard.d.ts +3 -4
  28. package/dist/components/planning/ManualActionCard.d.ts.map +1 -1
  29. package/dist/components/planning/ManualActionCard.js +24 -6
  30. package/dist/components/rdf-insights/CampaignChannelMix.js +1 -1
  31. package/dist/components/rdf-insights/CampaignGrid.d.ts.map +1 -1
  32. package/dist/components/rdf-insights/CampaignGrid.js +2 -2
  33. package/dist/components/rdf-insights/DailyPerformance.js +2 -2
  34. package/dist/components/rdf-insights/KpiDashboard.d.ts +2 -2
  35. package/dist/components/rdf-insights/KpiDashboard.d.ts.map +1 -1
  36. package/dist/components/rdf-insights/KpiDashboard.js +33 -53
  37. package/dist/components/rdf-insights/MetaDailyInsights.js +2 -2
  38. package/dist/components/rdf-insights/ProductCard.d.ts.map +1 -1
  39. package/dist/components/rdf-insights/ProductCard.js +1 -1
  40. package/dist/components/rdf-insights/ProductVariantsTable.js +1 -1
  41. package/dist/components/rdf-insights/useExtractedInsights.d.ts +1 -1
  42. package/dist/components/rdf-insights/useExtractedInsights.js +3 -3
  43. package/dist/datastore-picker/datastore-picker-rows.d.ts +1 -0
  44. package/dist/datastore-picker/datastore-picker-rows.d.ts.map +1 -1
  45. package/dist/datastore-picker/datastore-picker-rows.js +6 -2
  46. package/dist/graph/nodes/GoogleCampaignView.js +2 -2
  47. package/dist/hooks/use-action-plans-list.js +1 -1
  48. package/dist/hooks/use-manual-actions.d.ts +11 -1
  49. package/dist/hooks/use-manual-actions.d.ts.map +1 -1
  50. package/dist/hooks/use-manual-actions.js +47 -100
  51. package/dist/hooks/useAuth.d.ts +5 -1
  52. package/dist/hooks/useAuth.d.ts.map +1 -1
  53. package/dist/hooks/useAuth.js +4 -0
  54. package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
  55. package/dist/ldo-ui.d.ts +1 -1
  56. package/dist/ldo-ui.d.ts.map +1 -1
  57. package/dist/lib/manual-actions.js +1 -1
  58. package/dist/lib/rdf/index.d.ts +4 -2
  59. package/dist/lib/rdf/index.d.ts.map +1 -1
  60. package/dist/lib/rdf/index.js +48 -18
  61. package/dist/lib/rdf/insights.d.ts +6 -1
  62. package/dist/lib/rdf/insights.d.ts.map +1 -1
  63. package/dist/lib/rdf/insights.js +15 -3
  64. package/dist/lib/rdf/types.d.ts +21 -8
  65. package/dist/lib/rdf/types.d.ts.map +1 -1
  66. package/dist/providers/AuthContext.d.ts +1 -1
  67. package/dist/providers/AuthContext.d.ts.map +1 -1
  68. package/dist/providers/index.d.ts +4 -1
  69. package/dist/providers/index.d.ts.map +1 -1
  70. package/dist/providers/index.js +2 -1
  71. package/dist/routes/datastores/components/DatastoreFormDialog.d.ts +4 -2
  72. package/dist/routes/datastores/components/DatastoreFormDialog.d.ts.map +1 -1
  73. package/dist/routes/datastores/components/DatastoreFormDialog.js +7 -4
  74. package/dist/routes/datastores/components/DatastoresToolbar.d.ts +3 -1
  75. package/dist/routes/datastores/components/DatastoresToolbar.d.ts.map +1 -1
  76. package/dist/routes/datastores/components/DatastoresToolbar.js +18 -5
  77. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.d.ts.map +1 -1
  78. package/dist/routes/datastores/components/LinkUnlinkedDatastoreDialog.js +6 -2
  79. package/dist/routes/datastores/components/ProvisionDatastoreDialog.d.ts +7 -0
  80. package/dist/routes/datastores/components/ProvisionDatastoreDialog.d.ts.map +1 -0
  81. package/dist/routes/datastores/components/ProvisionDatastoreDialog.js +10 -0
  82. package/dist/routes/datastores/flatten.d.ts +4 -0
  83. package/dist/routes/datastores/flatten.d.ts.map +1 -1
  84. package/dist/routes/datastores/flatten.js +11 -0
  85. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts +4 -1
  86. package/dist/routes/datastores/hooks/useDatastoreColumns.d.ts.map +1 -1
  87. package/dist/routes/datastores/hooks/useDatastoreColumns.js +6 -2
  88. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.d.ts +3 -0
  89. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.d.ts.map +1 -0
  90. package/dist/routes/datastores/hooks/useTenantOrganizationCheck.js +21 -0
  91. package/dist/routes/datastores/index.d.ts.map +1 -1
  92. package/dist/routes/datastores/index.js +44 -6
  93. package/dist/routes/datastores/link-provider.d.ts +11 -0
  94. package/dist/routes/datastores/link-provider.d.ts.map +1 -0
  95. package/dist/routes/datastores/link-provider.js +34 -0
  96. package/dist/routes/integrations/components/connect-forms/types.d.ts +1 -0
  97. package/dist/routes/integrations/components/connect-forms/types.d.ts.map +1 -1
  98. package/dist/routes/integrations/connection-view.d.ts.map +1 -1
  99. package/dist/routes/integrations/connection-view.js +19 -6
  100. package/dist/routes/integrations/types.d.ts +2 -0
  101. package/dist/routes/integrations/types.d.ts.map +1 -1
  102. package/dist/routes/memories/components/ChangeScopeDialog.d.ts +1 -1
  103. package/dist/routes/memories/components/ChangeScopeDialog.d.ts.map +1 -1
  104. package/dist/routes/memories/components/ChangeScopeDialog.js +3 -3
  105. package/dist/routes/memories/components/EmptyStates.d.ts +1 -2
  106. package/dist/routes/memories/components/EmptyStates.d.ts.map +1 -1
  107. package/dist/routes/memories/components/EmptyStates.js +2 -2
  108. package/dist/routes/memories/components/MemoriesPanel.d.ts +1 -1
  109. package/dist/routes/memories/components/MemoriesPanel.d.ts.map +1 -1
  110. package/dist/routes/memories/components/MemoriesPanel.js +9 -9
  111. package/dist/routes/memories/components/MemoryActions.d.ts +1 -1
  112. package/dist/routes/memories/components/MemoryActions.d.ts.map +1 -1
  113. package/dist/routes/memories/components/MemoryActions.js +4 -4
  114. package/dist/routes/memories/components/MemoryFields.d.ts +1 -1
  115. package/dist/routes/memories/components/MemoryFields.d.ts.map +1 -1
  116. package/dist/routes/memories/components/MemoryFields.js +2 -2
  117. package/dist/routes/memories/components/MemoryFormDialog.d.ts +1 -1
  118. package/dist/routes/memories/components/MemoryFormDialog.d.ts.map +1 -1
  119. package/dist/routes/memories/components/MemoryFormDialog.js +2 -2
  120. package/dist/routes/memories/components/TeamPicker.d.ts +1 -1
  121. package/dist/routes/memories/components/TeamPicker.d.ts.map +1 -1
  122. package/dist/routes/memories/components/TeamPicker.js +2 -2
  123. package/dist/routes/memories/hooks/useMemoryColumns.d.ts +1 -1
  124. package/dist/routes/memories/hooks/useMemoryColumns.d.ts.map +1 -1
  125. package/dist/routes/memories/hooks/useMemoryColumns.js +3 -3
  126. package/dist/routes/memories/index.d.ts.map +1 -1
  127. package/dist/routes/memories/index.js +21 -21
  128. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/providers/AuthContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,aAAa,EACX,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,eAAe,GACf,UAAU,GACV,uBAAuB,GACvB,oBAAoB,GACpB,eAAe,CAClB,GAAG;IACF,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,eAAO,MAAM,WAAW,uDAAyD,CAAC;AAElF,wBAAgB,cAAc,IAAI,gBAAgB,CAQjD"}
1
+ {"version":3,"file":"AuthContext.d.ts","sourceRoot":"","sources":["../../src/providers/AuthContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAE9C,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,aAAa,EACX,WAAW,GACX,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,eAAe,GACf,UAAU,GACV,uBAAuB,GACvB,kBAAkB,GAClB,yBAAyB,GACzB,oBAAoB,GACpB,eAAe,CAClB,GAAG;IACF,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC;AAEF,eAAO,MAAM,WAAW,uDAAyD,CAAC;AAElF,wBAAgB,cAAc,IAAI,gBAAgB,CAQjD"}
@@ -19,5 +19,8 @@ export type ProvidersProps = {
19
19
  knowledgeGraphPollIntervalMs?: number;
20
20
  };
21
21
  export declare function Providers(props: ProvidersProps): import("react/jsx-runtime").JSX.Element;
22
- export declare function EmbeddedChatProviders(props: ProvidersProps): import("react/jsx-runtime").JSX.Element;
22
+ export type EmbeddedChatProvidersProps = Omit<ProvidersProps, "mantaBaseUrl"> & {
23
+ mantaBaseUrl?: string;
24
+ };
25
+ export declare function EmbeddedChatProviders(props: EmbeddedChatProvidersProps): import("react/jsx-runtime").JSX.Element;
23
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B,CAAC,EAAE,MAAM,CAAC;CACvC,CAAC;AAsCF,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CAc9C;AAGD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,cAAc,2CAE1D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAWvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,yBAAyB,CAAC;AACxC,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B,CAAC,EAAE,MAAM,CAAC;CACvC,CAAC;AAsCF,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CAc9C;AAED,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAG1G,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,2CAItE"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { defaultDeployment } from "@manta-eu/auth/default-deployment";
2
3
  import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
3
4
  import { NuqsAdapter } from "nuqs/adapters/react-router/v7";
4
5
  import { useState } from "react";
@@ -43,5 +44,5 @@ export function Providers(props) {
43
44
  }
44
45
  // IMPORTANT: Avoid redirects from embedded manta client
45
46
  export function EmbeddedChatProviders(props) {
46
- return _jsx(CoreProviders, { ...props, onUnauthorized: () => { } });
47
+ return (_jsx(CoreProviders, { ...props, mantaBaseUrl: props.mantaBaseUrl ?? defaultDeployment.apiUrl, onUnauthorized: () => { } }));
47
48
  }
@@ -1,3 +1,4 @@
1
+ import type { TenantOrganizationCheck } from "../link-provider.js";
1
2
  export type DatastoreFormTarget = {
2
3
  kind: "organization";
3
4
  } | {
@@ -6,15 +7,16 @@ export type DatastoreFormTarget = {
6
7
  parentName: string;
7
8
  organizationName: string;
8
9
  };
9
- type DatastoreFormDialogProps = {
10
+ export type DatastoreFormDialogProps = {
10
11
  target: DatastoreFormTarget;
11
12
  onSubmit: (values: {
12
13
  name: string;
13
14
  }) => void;
14
15
  onCancel: () => void;
15
16
  isSubmitting?: boolean;
17
+ onNameChange?: (name: string) => void;
18
+ nameCheck?: TenantOrganizationCheck;
16
19
  };
17
20
  export declare function provisioningNotice(target: DatastoreFormTarget): string;
18
21
  export declare function DatastoreFormDialog(props: DatastoreFormDialogProps): import("react/jsx-runtime").JSX.Element;
19
- export {};
20
22
  //# sourceMappingURL=DatastoreFormDialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DatastoreFormDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/DatastoreFormDialog.tsx"],"names":[],"mappings":"AAcA,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,KAAK,wBAAwB,GAAG;IAC9B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAKtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAmElE"}
1
+ {"version":3,"file":"DatastoreFormDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/DatastoreFormDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAahE,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAAG;IACrC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC7C,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,SAAS,CAAC,EAAE,uBAAuB,CAAC;CACrC,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAKtE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAsElE"}
@@ -11,10 +11,10 @@ export function provisioningNotice(target) {
11
11
  .exhaustive();
12
12
  }
13
13
  export function DatastoreFormDialog(props) {
14
- const { target, onSubmit, onCancel, isSubmitting = false } = props;
14
+ const { target, onSubmit, onCancel, isSubmitting = false, onNameChange, nameCheck } = props;
15
15
  const [name, setName] = useState("");
16
16
  const isOrganization = target.kind === "organization";
17
- const canSubmit = !!name.trim() && !isSubmitting;
17
+ const canSubmit = !!name.trim() && !isSubmitting && nameCheck?.blocked !== true;
18
18
  function submit() {
19
19
  if (!canSubmit)
20
20
  return;
@@ -24,10 +24,13 @@ export function DatastoreFormDialog(props) {
24
24
  .with({ kind: "organization" }, () => "Sets up a new top-level organization with its own datastore and a fresh Manta you can build on.")
25
25
  .with({ kind: "datastore", parentPath: "/" }, (datastore) => `Adds a top-level datastore in ${datastore.organizationName}.`)
26
26
  .with({ kind: "datastore" }, (datastore) => `Adds a datastore nested under ${datastore.parentName}, in ${datastore.organizationName}.`)
27
- .exhaustive() })] }), _jsx(Input, { autoFocus: true, label: isOrganization ? "Organization name" : "Datastore name", placeholder: isOrganization ? "e.g. Other Reseller Co" : "e.g. Acme Corp", value: name, disabled: isSubmitting, onChange: (event) => setName(event.target.value), onKeyDown: (event) => {
27
+ .exhaustive() })] }), _jsx(Input, { autoFocus: true, label: isOrganization ? "Organization name" : "Datastore name", placeholder: isOrganization ? "e.g. Other Reseller Co" : "e.g. Acme Corp", value: name, disabled: isSubmitting, onChange: (event) => {
28
+ setName(event.target.value);
29
+ onNameChange?.(event.target.value);
30
+ }, onKeyDown: (event) => {
28
31
  if (event.key === "Enter")
29
32
  submit();
30
33
  if (event.key === "Escape")
31
34
  onCancel();
32
- } }), _jsx("p", { role: "status", "aria-live": "polite", className: "typography-caption text-muted-foreground min-h-5", children: isSubmitting ? provisioningNotice(target) : "" }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "tertiary", onClick: onCancel, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { onClick: submit, disabled: !canSubmit, isLoading: isSubmitting, label: isOrganization ? "Add organization" : "Add datastore" })] })] }) }));
35
+ } }), _jsx("p", { role: "status", "aria-live": "polite", className: "typography-caption text-muted-foreground min-h-5", children: isSubmitting ? provisioningNotice(target) : (nameCheck?.hint ?? "") }), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "tertiary", onClick: onCancel, disabled: isSubmitting, children: "Cancel" }), _jsx(Button, { onClick: submit, disabled: !canSubmit, isLoading: isSubmitting, label: isOrganization ? "Add organization" : "Add datastore" })] })] }) }));
33
36
  }
@@ -1,6 +1,6 @@
1
1
  import type { Table } from "@tanstack/react-table";
2
2
  import type { ReactNode } from "react";
3
- import type { DatastoreRow } from "../flatten.js";
3
+ import type { DatastoreRow, ProviderFilter } from "../flatten.js";
4
4
  import type { StatusFilter } from "../hooks/useDatastoreTable.js";
5
5
  type DatastoresToolbarProps = {
6
6
  tabs?: ReactNode;
@@ -9,6 +9,8 @@ type DatastoresToolbarProps = {
9
9
  searchPlaceholder?: string;
10
10
  statusFilter: StatusFilter;
11
11
  onStatusFilterChange: (status: StatusFilter) => void;
12
+ providerFilter?: ProviderFilter;
13
+ onProviderFilterChange?: (filter: ProviderFilter) => void;
12
14
  table: Table<DatastoreRow>;
13
15
  columnLabels?: Record<string, string>;
14
16
  columnDefaultOrder?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"DatastoresToolbar.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/DatastoresToolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAQ/D,KAAK,sBAAsB,GAAG;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,oBAAoB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IACrD,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,2CA6F9D"}
1
+ {"version":3,"file":"DatastoresToolbar.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/DatastoresToolbar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAc/D,KAAK,sBAAsB,GAAG;IAC5B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,YAAY,CAAC;IAC3B,oBAAoB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IACrD,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,sBAAsB,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IAC1D,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,2CAuI9D"}
@@ -8,22 +8,35 @@ import { Dropdown, DropdownContent, DropdownGroup, DropdownItem, DropdownTrigger
8
8
  import { FilterSection, FilterSheet } from "#components/filter-sheet/index";
9
9
  import { Input } from "#components/input/index";
10
10
  import { TableColumnRearrangePopover } from "#components/table/index";
11
+ const providerFilterOptions = [
12
+ { id: "ALL", label: "All" },
13
+ { id: "mirrored", label: "Linked to identity provider" },
14
+ { id: "unmirrored", label: "Not linked to identity provider" },
15
+ ];
11
16
  export function DatastoresToolbar(props) {
12
- const { tabs, searchQuery, onSearchQueryChange, searchPlaceholder = "Search by name or ID...", statusFilter, onStatusFilterChange, table, columnLabels = datastoreColumnLabels, columnDefaultOrder = defaultColumnOrder, } = props;
17
+ const { tabs, searchQuery, onSearchQueryChange, searchPlaceholder = "Search by name or ID...", statusFilter, onStatusFilterChange, providerFilter, onProviderFilterChange, table, columnLabels = datastoreColumnLabels, columnDefaultOrder = defaultColumnOrder, } = props;
13
18
  const allStatusesSelected = statusFilter === "ALL";
14
19
  const selectedStatusLabel = datastoreStatusFilterOptions.find((option) => option.id === statusFilter)?.label;
15
- const hasActiveFilters = isPresent(searchQuery) || !allStatusesSelected;
16
- const activeFilterCount = (isPresent(searchQuery) ? 1 : 0) + (allStatusesSelected ? 0 : 1);
20
+ const providerFiltered = isPresent(providerFilter) && providerFilter !== "ALL";
21
+ const selectedProviderLabel = providerFilterOptions.find((option) => option.id === providerFilter)?.label;
22
+ const hasActiveFilters = isPresent(searchQuery) || !allStatusesSelected || providerFiltered;
23
+ const activeFilterCount = (isPresent(searchQuery) ? 1 : 0) + (allStatusesSelected ? 0 : 1) + (providerFiltered ? 1 : 0);
17
24
  function handleClearFilters() {
18
25
  onSearchQueryChange("");
19
26
  onStatusFilterChange("ALL");
27
+ onProviderFilterChange?.("ALL");
20
28
  }
21
29
  function statusDropdown(className) {
22
30
  return (_jsxs(Dropdown, { children: [_jsx(DropdownTrigger, { asChild: true, children: _jsxs(Button, { variant: "tertiary", size: "base", rounded: "lg", className: className, children: [_jsx("span", { children: allStatusesSelected ? "Status" : selectedStatusLabel }), _jsx(ChevronDown, { className: "size-4" })] }) }), _jsx(DropdownContent, { align: "start", className: "w-48", children: _jsx(DropdownGroup, { label: "Filter by status", children: datastoreStatusFilterOptions.map((option) => (_jsx(DropdownItem, { selected: option.id === statusFilter, onClick: () => onStatusFilterChange(option.id), children: option.label }, option.id))) }) })] }));
23
31
  }
32
+ function providerDropdown(className) {
33
+ if (!isPresent(providerFilter) || !onProviderFilterChange)
34
+ return null;
35
+ return (_jsxs(Dropdown, { children: [_jsx(DropdownTrigger, { asChild: true, children: _jsxs(Button, { variant: "tertiary", size: "base", rounded: "lg", className: className, children: [_jsx("span", { children: providerFiltered ? selectedProviderLabel : "Identity provider" }), _jsx(ChevronDown, { className: "size-4" })] }) }), _jsx(DropdownContent, { align: "start", className: "w-64", children: _jsx(DropdownGroup, { label: "Filter by identity provider", children: providerFilterOptions.map((option) => (_jsx(DropdownItem, { selected: option.id === providerFilter, onClick: () => onProviderFilterChange(option.id), children: option.label }, option.id))) }) })] }));
36
+ }
24
37
  function filterControls() {
25
- return (_jsxs(_Fragment, { children: [statusDropdown(), hasActiveFilters && (_jsx(Button, { variant: "ghost", size: "base", rounded: "lg", onClick: handleClearFilters, icon: X, children: "Clear filters" }))] }));
38
+ return (_jsxs(_Fragment, { children: [statusDropdown(), providerDropdown(), hasActiveFilters && (_jsx(Button, { variant: "ghost", size: "base", rounded: "lg", onClick: handleClearFilters, icon: X, children: "Clear filters" }))] }));
26
39
  }
27
- const filterSheetContent = _jsx(FilterSection, { title: "Status", children: statusDropdown("w-full justify-between") });
40
+ const filterSheetContent = (_jsxs(_Fragment, { children: [_jsx(FilterSection, { title: "Status", children: statusDropdown("w-full justify-between") }), isPresent(providerFilter) && (_jsx(FilterSection, { title: "Identity provider", children: providerDropdown("w-full justify-between") }))] }));
28
41
  return (_jsxs("div", { className: "flex min-w-0 flex-col gap-3 3xl:flex-row 3xl:items-center", children: [isPresent(tabs) && _jsx("div", { className: "min-w-0", children: tabs }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 flex-1 min-w-0", children: [_jsx(Input, { containerClassName: "min-w-0 flex-1 sm:w-64 sm:flex-none", type: "search", variant: "search", placeholder: searchPlaceholder, value: searchQuery, onChange: (event) => onSearchQueryChange(event.target.value) }), _jsx("div", { className: "sm:hidden", children: _jsx(FilterSheet, { activeCount: activeFilterCount, onReset: handleClearFilters, children: filterSheetContent }) }), _jsx("div", { className: "hidden sm:contents", children: filterControls() }), _jsx("div", { className: "ml-auto hidden shrink-0 sm:block", children: _jsx(TableColumnRearrangePopover, { enableReset: true, table: table, columnLabels: columnLabels, defaultOrder: columnDefaultOrder }) })] })] }));
29
42
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LinkUnlinkedDatastoreDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/LinkUnlinkedDatastoreDialog.tsx"],"names":[],"mappings":"AAqBA,MAAM,MAAM,mBAAmB,GAAG;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,yBAAyB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAIF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,2CAkIlF"}
1
+ {"version":3,"file":"LinkUnlinkedDatastoreDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/LinkUnlinkedDatastoreDialog.tsx"],"names":[],"mappings":"AAuBA,MAAM,MAAM,mBAAmB,GAAG;IAChC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,yBAAyB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvC,cAAc,CAAC,EAAE,mBAAmB,CAAC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAIF,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,gCAAgC,2CA0IlF"}
@@ -6,6 +6,8 @@ import { useQueryClient } from "@tanstack/react-query";
6
6
  import { useState } from "react";
7
7
  import { toast } from "sonner";
8
8
  import { organizationNameKey } from "../flatten.js";
9
+ import { useTenantOrganizationCheck } from "../hooks/useTenantOrganizationCheck.js";
10
+ import { toastProviderWarning } from "../link-provider.js";
9
11
  import { Button } from "#components/button/index";
10
12
  import { Input } from "#components/input/index";
11
13
  import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "#components/modal/index";
@@ -27,6 +29,7 @@ export function LinkUnlinkedDatastoreDialog(props) {
27
29
  void queryClient.invalidateQueries({ queryKey: useUnlinkedDatastoresQuery.getKey() });
28
30
  void queryClient.invalidateQueries({ queryKey: ["DatastoreProvisioningStatuses"] });
29
31
  toast.success(`Linked to the existing datastore '${datastoreId}'`);
32
+ toastProviderWarning(result.linkUnlinkedDatastore.providerWarning);
30
33
  onClose();
31
34
  }
32
35
  else {
@@ -40,7 +43,8 @@ export function LinkUnlinkedDatastoreDialog(props) {
40
43
  toast.error(info.message, { description: info.description });
41
44
  },
42
45
  });
43
- const canSubmit = mode === "new" ? !!organizationName.trim() : !!existingOrganizationName;
46
+ const tenantCheck = useTenantOrganizationCheck(mode === "new" ? organizationName : "", existingOrganizationNames);
47
+ const canSubmit = mode === "new" ? !!organizationName.trim() && !tenantCheck.blocked : !!existingOrganizationName;
44
48
  const atExistingOrganizationRoot = mode === "existing" && !name.trim();
45
49
  const suggestedParentPath = suggestedMatch &&
46
50
  existingOrganizationName === suggestedMatch.organizationName &&
@@ -79,5 +83,5 @@ export function LinkUnlinkedDatastoreDialog(props) {
79
83
  };
80
84
  linkMutation.mutate({ input });
81
85
  }
82
- return (_jsx(Dialog, { open: true, onOpenChange: (nextOpen) => !nextOpen && onClose(), children: _jsxs(DialogContent, { className: "flex flex-col overflow-hidden", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { text: "Link datastore" }), _jsx(DialogDescription, { children: `Link the existing datastore '${datastoreId}' to an organization or group.` })] }), _jsxs(RadioGroup, { value: mode, onValueChange: (v) => setMode(v), className: "flex gap-4 items-center", children: [_jsx(RadioGroupItem, { value: "new", label: "New organization" }), _jsx(RadioGroupItem, { value: "existing", label: "Existing organization", disabled: !isPresent(organizationNames) })] }), mode === "new" ? (_jsx(Input, { autoFocus: true, label: "Organization name", placeholder: "e.g. Acme Corp", value: organizationName, onChange: (event) => setOrganizationName(event.target.value) })) : (_jsxs(_Fragment, { children: [_jsxs(Select, { value: existingOrganizationName, onValueChange: setExistingOrganizationName, children: [_jsx(SelectTrigger, { label: "Organization", id: "link-organization", children: _jsx(SelectValue, { placeholder: "Select an organization\u2026" }) }), _jsx(SelectContent, { children: organizationNames.map((orgName) => (_jsx(SelectItem, { value: orgName, children: orgName }, orgName))) })] }), _jsx(Input, { label: "Datastore name", placeholder: "Leave empty to link to the organization root", value: name, onChange: (event) => setName(event.target.value) })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: "Cancel" }), _jsx(Button, { onClick: submit, disabled: !canSubmit || linkMutation.isPending, children: "Link" })] })] }) }));
86
+ return (_jsx(Dialog, { open: true, onOpenChange: (nextOpen) => !nextOpen && onClose(), children: _jsxs(DialogContent, { className: "flex flex-col overflow-hidden", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { text: "Link datastore" }), _jsx(DialogDescription, { children: `Link the existing datastore '${datastoreId}' to an organization or group.` })] }), _jsxs(RadioGroup, { value: mode, onValueChange: (v) => setMode(v), className: "flex gap-4 items-center", children: [_jsx(RadioGroupItem, { value: "new", label: "New organization" }), _jsx(RadioGroupItem, { value: "existing", label: "Existing organization", disabled: !isPresent(organizationNames) })] }), mode === "new" ? (_jsxs(_Fragment, { children: [_jsx(Input, { autoFocus: true, label: "Organization name", placeholder: "e.g. Acme Corp", value: organizationName, onChange: (event) => setOrganizationName(event.target.value) }), isPresent(tenantCheck.hint) ? (_jsx("span", { className: "typography-caption text-muted-foreground", children: tenantCheck.hint })) : null] })) : (_jsxs(_Fragment, { children: [_jsxs(Select, { value: existingOrganizationName, onValueChange: setExistingOrganizationName, children: [_jsx(SelectTrigger, { label: "Organization", id: "link-organization", children: _jsx(SelectValue, { placeholder: "Select an organization\u2026" }) }), _jsx(SelectContent, { children: organizationNames.map((orgName) => (_jsx(SelectItem, { value: orgName, children: orgName }, orgName))) })] }), _jsx(Input, { label: "Datastore name", placeholder: "Leave empty to link to the organization root", value: name, onChange: (event) => setName(event.target.value) })] })), _jsxs(DialogFooter, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: "Cancel" }), _jsx(Button, { onClick: submit, disabled: !canSubmit || linkMutation.isPending, children: "Link" })] })] }) }));
83
87
  }
@@ -0,0 +1,7 @@
1
+ import { type DatastoreFormDialogProps } from "./DatastoreFormDialog.js";
2
+ type ProvisionDatastoreDialogProps = Omit<DatastoreFormDialogProps, "onNameChange" | "nameCheck"> & {
3
+ existingOrganizationNames: Set<string>;
4
+ };
5
+ export declare function ProvisionDatastoreDialog(props: ProvisionDatastoreDialogProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=ProvisionDatastoreDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProvisionDatastoreDialog.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/components/ProvisionDatastoreDialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAuB,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAE3F,KAAK,6BAA6B,GAAG,IAAI,CAAC,wBAAwB,EAAE,cAAc,GAAG,WAAW,CAAC,GAAG;IAClG,yBAAyB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACxC,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,6BAA6B,2CAS5E"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import { useTenantOrganizationCheck } from "../hooks/useTenantOrganizationCheck.js";
4
+ import { DatastoreFormDialog } from "./DatastoreFormDialog.js";
5
+ export function ProvisionDatastoreDialog(props) {
6
+ const { existingOrganizationNames, ...dialogProps } = props;
7
+ const [name, setName] = useState("");
8
+ const nameCheck = useTenantOrganizationCheck(dialogProps.target.kind === "organization" ? name : "", existingOrganizationNames);
9
+ return _jsx(DatastoreFormDialog, { ...dialogProps, onNameChange: setName, nameCheck: nameCheck });
10
+ }
@@ -14,10 +14,14 @@ export type DatastoreRow = {
14
14
  provisionedAt?: string;
15
15
  provisionedBy?: string;
16
16
  isOrganization: boolean;
17
+ mirrored?: boolean;
17
18
  isRealmDefault?: boolean;
18
19
  isUnlinked?: boolean;
19
20
  };
20
21
  export declare function realmDefaultRow(datastoreId: Realm): DatastoreRow;
22
+ export declare function isUnmirroredOrganization(row: DatastoreRow): boolean;
23
+ export type ProviderFilter = "ALL" | "mirrored" | "unmirrored";
24
+ export declare function matchesProviderFilter(row: DatastoreRow, filter: ProviderFilter): boolean;
21
25
  export type DatastoreRowVisibility = {
22
26
  showArchived: boolean;
23
27
  showUnprovisioned: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../../src/routes/datastores/flatten.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAmB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGlE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAG3E,MAAM,MAAM,YAAY,GAAG;IAEzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,cAAc,EAAE,OAAO,CAAC;IAExB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAAC,WAAW,EAAE,KAAK,GAAG,YAAY,CAchE;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAcF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,sBAAsB,GAAG,YAAY,EAAE,CAG7G;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,YAAY,EAAE,EACpB,UAAU,EAAE,sBAAsB,EAClC,GAAG,EAAE,MAAM,sBAAsB,EACjC,UAAU,EAAE,MAAM,GACjB,MAAM,CAIR;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAE9E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,CAEhE;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,sBAAsB,EAClC,MAAM,EAAE,0BAA0B,GAAG,KAAK,GACzC,sBAAsB,CAMxB;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CActE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,YAAY,EAAE,CAe7E"}
1
+ {"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../../src/routes/datastores/flatten.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAmB,KAAK,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAGlE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAG3E,MAAM,MAAM,YAAY,GAAG;IAEzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAE1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAAC,WAAW,EAAE,KAAK,GAAG,YAAY,CAchE;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAEnE;AAED,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,GAAG,YAAY,CAAC;AAE/D,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAMxF;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAcF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,EAAE,sBAAsB,GAAG,YAAY,EAAE,CAG7G;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,YAAY,EAAE,EACpB,UAAU,EAAE,sBAAsB,EAClC,GAAG,EAAE,MAAM,sBAAsB,EACjC,UAAU,EAAE,MAAM,GACjB,MAAM,CAIR;AAGD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAE9E;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,EAAE,CAEhE;AAED,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,sBAAsB,EAClC,MAAM,EAAE,0BAA0B,GAAG,KAAK,GACzC,sBAAsB,CAMxB;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,CActE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,YAAY,EAAE,CAgB7E"}
@@ -16,6 +16,16 @@ export function realmDefaultRow(datastoreId) {
16
16
  isRealmDefault: true,
17
17
  };
18
18
  }
19
+ export function isUnmirroredOrganization(row) {
20
+ return row.isOrganization && row.mirrored === false;
21
+ }
22
+ export function matchesProviderFilter(row, filter) {
23
+ return match(filter)
24
+ .with("ALL", () => true)
25
+ .with("mirrored", () => row.isOrganization && row.mirrored === true)
26
+ .with("unmirrored", () => isUnmirroredOrganization(row))
27
+ .exhaustive();
28
+ }
19
29
  function matchesVisibility(row, visibility) {
20
30
  return (visibility.showArchived || !row.removed) && (visibility.showUnprovisioned || isPresent(row.datastoreId));
21
31
  }
@@ -82,5 +92,6 @@ export function toDatastoreRows(nodes) {
82
92
  provisionedAt: node.provisionedAt ?? undefined,
83
93
  provisionedBy: node.provisionedBy ?? undefined,
84
94
  isOrganization: node.isOrganization,
95
+ mirrored: node.mirrored ?? undefined,
85
96
  }));
86
97
  }
@@ -1,5 +1,5 @@
1
1
  import type { ColumnDef } from "@tanstack/react-table";
2
- import type { DatastoreRow } from "../flatten.js";
2
+ import { type DatastoreRow } from "../flatten.js";
3
3
  import { type DatastoreStatus } from "#components/datastore-management/tree";
4
4
  export declare const datastoreColumnIds: {
5
5
  readonly name: "name";
@@ -41,6 +41,9 @@ type UseDatastoreColumnsArgs = {
41
41
  onManageUsers: (row: DatastoreRow) => void;
42
42
  onBringOnline: (row: DatastoreRow) => void;
43
43
  isBringingOnline?: boolean;
44
+ onLinkProvider: (row: DatastoreRow) => void;
45
+ isLinkingProvider?: boolean;
46
+ providerLinkEnabled?: boolean;
44
47
  };
45
48
  export declare function useDatastoreColumns(args: UseDatastoreColumnsArgs): ColumnDef<DatastoreRow>[];
46
49
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"useDatastoreColumns.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/hooks/useDatastoreColumns.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAuBvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,EAA4C,KAAK,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAYvH,eAAO,MAAM,kBAAkB;;;;;;;;;CASrB,CAAC;AASX,eAAO,MAAM,qBAAqB;;;;;;;CAOkB,CAAC;AAErD,eAAO,MAAM,kBAAkB,+HAAoC,CAAC;AAEpE,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGvD,CAAC;AAEF,eAAO,MAAM,mBAAmB,6DAK/B,CAAC;AAcF,KAAK,uBAAuB,GAAG;IAC7B,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3D,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,cAAc,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,CAAC,YAAY,CAAC,EAAE,CA0U5F"}
1
+ {"version":3,"file":"useDatastoreColumns.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/hooks/useDatastoreColumns.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAuBvD,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAGzE,OAAO,EAA4C,KAAK,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAYvH,eAAO,MAAM,kBAAkB;;;;;;;;;CASrB,CAAC;AASX,eAAO,MAAM,qBAAqB;;;;;;;CAOkB,CAAC;AAErD,eAAO,MAAM,kBAAkB,+HAAoC,CAAC;AAEpE,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGvD,CAAC;AAEF,eAAO,MAAM,mBAAmB,6DAK/B,CAAC;AAcF,KAAK,uBAAuB,GAAG;IAC7B,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IACrD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC3D,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACzC,cAAc,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACvC,QAAQ,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACtC,UAAU,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,aAAa,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC3C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,cAAc,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,CAAC,YAAY,CAAC,EAAE,CA4V5F"}
@@ -6,6 +6,7 @@ import { Check, DatabaseZap, Link2, MoreHorizontal, Pencil, Plus, Power, Refresh
6
6
  import { useMemo } from "react";
7
7
  import { match } from "ts-pattern";
8
8
  import { DatastoreStatusBadge, datastoreStatusFilterValue, } from "../components/StatusBadge.js";
9
+ import { isUnmirroredOrganization } from "../flatten.js";
9
10
  import { Badge } from "#components/badge/index";
10
11
  import { Button } from "#components/button/index";
11
12
  import { datastoreStatusCategory, isMaterializing } from "#components/datastore-management/tree";
@@ -54,7 +55,7 @@ const columnMinWidths = {
54
55
  [datastoreColumnIds.actions]: "min-w-[140px]",
55
56
  };
56
57
  export function useDatastoreColumns(args) {
57
- const { statusByDatastoreId, currentDatastoreId, isSwitching, indented, matchableRowIds, suggestedMatchByDatastoreId, onSelect, onAddChild, onLink, onProvision, onLinkExisting, onRemove, onRestore, onRename, onRegister, onManageUsers, onBringOnline, isBringingOnline, } = args;
58
+ const { statusByDatastoreId, currentDatastoreId, isSwitching, indented, matchableRowIds, suggestedMatchByDatastoreId, onSelect, onAddChild, onLink, onProvision, onLinkExisting, onRemove, onRestore, onRename, onRegister, onManageUsers, onBringOnline, isBringingOnline, onLinkProvider, isLinkingProvider, providerLinkEnabled, } = args;
58
59
  return useMemo(() => [
59
60
  {
60
61
  id: datastoreColumnIds.name,
@@ -136,7 +137,7 @@ export function useDatastoreColumns(args) {
136
137
  .with({ materializing: true }, () => "Still materializing - this can take a few minutes.")
137
138
  .otherwise(() => undefined);
138
139
  const canSelect = hasDatastore && !reason && !current && !isSwitching;
139
- return (_jsxs("div", { className: cn(columnMinWidths[datastoreColumnIds.actions], "flex items-center justify-end gap-1.5"), children: [isUnlinked && (_jsx(Button, { "data-testid": `datastore-link-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: Link2, onClick: () => onLink(datastoreRow), children: "Link" })), hasDatastore && !removed && category === "offline" && (_jsx(Button, { "data-testid": `datastore-bring-online-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: Power, disabled: isBringingOnline, onClick: () => onBringOnline(datastoreRow), children: "Bring online" })), !isUnlinked && hasDatastore && !removed && notRegistered && (_jsx(Button, { "data-testid": `datastore-register-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: RefreshCw, onClick: () => onRegister(datastoreRow), children: "Register" })), !isUnlinked && hasDatastore && current && (_jsx(Button, { "data-testid": `datastore-select-${datastoreRow.id}`, size: "sm", variant: "secondary", disabled: true, children: "Selected" })), !isUnlinked && hasDatastore && !current && !removed && (_jsx(Button, { "data-testid": `datastore-select-${datastoreRow.id}`, size: "sm", variant: "secondary", disabled: !canSelect, title: reason, onClick: () => onSelect(datastoreRow), children: "Select" })), isRealmDefault || isUnlinked ? (
140
+ return (_jsxs("div", { className: cn(columnMinWidths[datastoreColumnIds.actions], "flex items-center justify-end gap-1.5"), children: [providerLinkEnabled === true && isUnmirroredOrganization(datastoreRow) && !removed && (_jsx(Button, { "data-testid": `datastore-link-provider-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: Link2, disabled: isLinkingProvider, onClick: () => onLinkProvider(datastoreRow), children: "Link to identity provider" })), isUnlinked && (_jsx(Button, { "data-testid": `datastore-link-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: Link2, onClick: () => onLink(datastoreRow), children: "Link" })), hasDatastore && !removed && category === "offline" && (_jsx(Button, { "data-testid": `datastore-bring-online-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: Power, disabled: isBringingOnline, onClick: () => onBringOnline(datastoreRow), children: "Bring online" })), !isUnlinked && hasDatastore && !removed && notRegistered && (_jsx(Button, { "data-testid": `datastore-register-${datastoreRow.id}`, size: "sm", variant: "secondary", icon: RefreshCw, onClick: () => onRegister(datastoreRow), children: "Register" })), !isUnlinked && hasDatastore && current && (_jsx(Button, { "data-testid": `datastore-select-${datastoreRow.id}`, size: "sm", variant: "secondary", disabled: true, children: "Selected" })), !isUnlinked && hasDatastore && !current && !removed && (_jsx(Button, { "data-testid": `datastore-select-${datastoreRow.id}`, size: "sm", variant: "secondary", disabled: !canSelect, title: reason, onClick: () => onSelect(datastoreRow), children: "Select" })), isRealmDefault || isUnlinked ? (
140
141
  // Same-sized spacer keeps Select/Selected aligned with rows that have the ellipsis.
141
142
  _jsx("div", { className: "size-6", "aria-hidden": "true" })) : (_jsxs(Dropdown, { children: [_jsx(DropdownTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": `Actions for ${name}`, className: "shrink-0 rounded-sm-v4 p-1 text-muted-foreground hover:bg-faint hover:text-default-foreground", children: _jsx(MoreHorizontal, { className: "size-4" }) }) }), _jsxs(DropdownContent, { align: "end", className: "min-w-52", children: [_jsx(DropdownItem, { icon: _jsx(Pencil, { className: "size-4" }), onSelect: () => onRename(datastoreRow), children: "Rename" }), isPresent(datastoreRow.organizationAlias) && (_jsx(DropdownItem, { icon: _jsx(Users, { className: "size-4" }), onSelect: () => onManageUsers(datastoreRow), children: "Manage users" })), !hasDatastore &&
142
143
  (matchableRowIds.has(datastoreRow.id) ? (_jsx(DropdownItem, { icon: _jsx(Link2, { className: "size-4" }), onSelect: () => onLinkExisting(datastoreRow), children: "Link existing datastore" })) : (_jsx(DropdownItem, { icon: _jsx(DatabaseZap, { className: "size-4" }), onSelect: () => onProvision(datastoreRow), children: "Provision datastore" }))), _jsx(DropdownItem, { icon: _jsx(Plus, { className: "size-4" }), onSelect: () => onAddChild(datastoreRow), children: "Add datastore" }), enableDatastoreRemoval && hasDatastore && !removed && (_jsxs(_Fragment, { children: [_jsx(DropdownSeparator, {}), _jsx(DropdownItem, { className: "text-error-foreground", icon: _jsx(Trash2, { className: "size-4" }), onSelect: () => onRemove(datastoreRow), children: "Remove datastore" })] })), enableDatastoreRemoval && hasDatastore && removed && (_jsxs(_Fragment, { children: [_jsx(DropdownSeparator, {}), _jsx(DropdownItem, { icon: _jsx(RotateCcw, { className: "size-4" }), onSelect: () => onRestore(datastoreRow), children: "Restore datastore" })] }))] })] }))] }));
@@ -161,5 +162,8 @@ export function useDatastoreColumns(args) {
161
162
  onManageUsers,
162
163
  onBringOnline,
163
164
  isBringingOnline,
165
+ onLinkProvider,
166
+ isLinkingProvider,
167
+ providerLinkEnabled,
164
168
  ]);
165
169
  }
@@ -0,0 +1,3 @@
1
+ import { type TenantOrganizationCheck } from "../link-provider.js";
2
+ export declare function useTenantOrganizationCheck(organizationName: string, existingOrganizationNames: Set<string>): TenantOrganizationCheck;
3
+ //# sourceMappingURL=useTenantOrganizationCheck.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTenantOrganizationCheck.d.ts","sourceRoot":"","sources":["../../../../src/routes/datastores/hooks/useTenantOrganizationCheck.ts"],"names":[],"mappings":"AAIA,OAAO,EAA2B,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAKzF,wBAAgB,0BAA0B,CACxC,gBAAgB,EAAE,MAAM,EACxB,yBAAyB,EAAE,GAAG,CAAC,MAAM,CAAC,GACrC,uBAAuB,CAgBzB"}
@@ -0,0 +1,21 @@
1
+ import { useOrganizationLinkingQuery, useTenantOrganizationQuery } from "@manta-eu/client";
2
+ import { isPresent } from "@manta-eu/lib/present";
3
+ import { useDebounceValue } from "usehooks-ts";
4
+ import { organizationNameKey } from "../flatten.js";
5
+ import { tenantOrganizationCheck } from "../link-provider.js";
6
+ import { useAuthContext } from "#providers/AuthContext";
7
+ import { useMantaClient } from "#providers/index";
8
+ // An empty name checks nothing, so a dialog passes "" while it is not creating an organization.
9
+ export function useTenantOrganizationCheck(organizationName, existingOrganizationNames) {
10
+ const { mantaClient } = useMantaClient();
11
+ const { isOperator } = useAuthContext();
12
+ const linkingQuery = useOrganizationLinkingQuery(mantaClient, undefined, { enabled: !!isOperator });
13
+ const linking = linkingQuery.data?.organizationLinking;
14
+ const trimmed = organizationName.trim();
15
+ const [debounced] = useDebounceValue(trimmed, 300);
16
+ const knownName = existingOrganizationNames.has(organizationNameKey(debounced));
17
+ const lookupName = linking?.linkEnabled === true && !knownName ? debounced : "";
18
+ const lookup = useTenantOrganizationQuery(mantaClient, { organizationName: lookupName }, { enabled: isPresent(lookupName), retry: false });
19
+ const tenant = isPresent(lookupName) && lookupName === trimmed ? lookup.data?.tenantOrganization : undefined;
20
+ return tenantOrganizationCheck(tenant, linking?.pushEnabled === true);
21
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/datastores/index.tsx"],"names":[],"mappings":"AAqFA,wBAAgB,eAAe,4CAsqB9B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/datastores/index.tsx"],"names":[],"mappings":"AA4FA,wBAAgB,eAAe,4CA4tB9B"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useBringDatastoreOnlineMutation, useLinkUnlinkedDatastoreMutation, useDatastoreTreeQuery, useProvisionDatastoreMutation, useRegisterDatastoreMutation, useRemoveDatastoreMutation, useRenameDatastoreGroupMutation, useRestoreDatastoreMutation, useUnlinkedDatastoresQuery, } from "@manta-eu/client";
2
+ import { useBringDatastoreOnlineMutation, useOrganizationLinkingQuery, useLinkOrganizationToProviderMutation, useLinkUnlinkedDatastoreMutation, useDatastoreTreeQuery, useProvisionDatastoreMutation, useRegisterDatastoreMutation, useRemoveDatastoreMutation, useRenameDatastoreGroupMutation, useRestoreDatastoreMutation, useUnlinkedDatastoresQuery, } from "@manta-eu/client";
3
3
  import { enableDatastoreRemoval } from "@manta-eu/client/shared/constants";
4
4
  import { deriveDatastoreId } from "@manta-eu/client/shared/datastore-id";
5
5
  import { parseError } from "@manta-eu/client/shared/error-utils";
@@ -11,15 +11,17 @@ import { Archive, Database, EyeOff, Plus, RefreshCcw, TriangleAlert } from "luci
11
11
  import { useCallback, useEffect, useMemo, useState } from "react";
12
12
  import { useNavigate, useSearchParams } from "react-router";
13
13
  import { toast } from "sonner";
14
- import { DatastoreFormDialog } from "./components/DatastoreFormDialog.js";
15
14
  import { DatastoresToolbar } from "./components/DatastoresToolbar.js";
16
15
  import { DatastoreTableSkeleton } from "./components/DatastoreTableSkeleton.js";
17
16
  import { LinkUnlinkedDatastoreDialog } from "./components/LinkUnlinkedDatastoreDialog.js";
17
+ import { ProvisionDatastoreDialog } from "./components/ProvisionDatastoreDialog.js";
18
18
  import { RenameDatastoreGroupDialog } from "./components/RenameDatastoreGroupDialog.js";
19
19
  import { UnlinkedDatastoresPanel } from "./components/UnlinkedDatastoresPanel.js";
20
- import { existingOrganizationNameKeys, hiddenRowCount, organizationNameKey, organizationNames, realmDefaultRow, toDatastoreRows, unlinkedDatastoreRow, visibilityForStatusFilter, visibleDatastoreRows, } from "./flatten.js";
20
+ import { existingOrganizationNameKeys, hiddenRowCount, matchesProviderFilter, organizationNameKey, organizationNames, realmDefaultRow, toDatastoreRows, unlinkedDatastoreRow, visibilityForStatusFilter, visibleDatastoreRows, } from "./flatten.js";
21
21
  import { datastoreColumnIds, unlinkedColumnLabels, unlinkedColumnOrder, useDatastoreColumns, } from "./hooks/useDatastoreColumns.js";
22
22
  import { useDatastoreTable } from "./hooks/useDatastoreTable.js";
23
+ import { linkProviderOutcome, toastProviderWarning } from "./link-provider.js";
24
+ import { Banner } from "#components/banner/index";
23
25
  import { Button } from "#components/button/index";
24
26
  import { collectDatastoreIds } from "#components/datastore-management/tree";
25
27
  import { useDatastoreStatusByDatastoreId } from "#components/datastore-management/use-datastore-status";
@@ -60,6 +62,7 @@ export function DatastoresRoute() {
60
62
  showUnprovisioned: false,
61
63
  });
62
64
  const { showArchived, showUnprovisioned } = visibility;
65
+ const [providerFilter, setProviderFilter] = useState("ALL");
63
66
  const [searchParams, setSearchParams] = useSearchParams();
64
67
  const tabParam = searchParams.get("tab");
65
68
  const activeTab = tabParam && activeTabs.includes(tabParam) ? tabParam : "organizations";
@@ -112,6 +115,7 @@ export function DatastoresRoute() {
112
115
  else {
113
116
  toast.success(`Datastore "${name}" is ready`);
114
117
  }
118
+ toastProviderWarning(result.provisionDatastore.providerWarning);
115
119
  },
116
120
  onError: (error) => {
117
121
  const info = parseError(error, "Couldn't create the datastore");
@@ -190,6 +194,30 @@ export function DatastoresRoute() {
190
194
  toast.error(info.message, { description: info.description });
191
195
  },
192
196
  });
197
+ const linkingQuery = useOrganizationLinkingQuery(mantaClient, undefined, { enabled: !!isOperator });
198
+ const linkProviderMutation = useLinkOrganizationToProviderMutation(mantaClient, {
199
+ onSuccess: (result) => {
200
+ const { reason } = result.linkOrganizationToProvider;
201
+ const outcome = linkProviderOutcome(result.linkOrganizationToProvider);
202
+ if (outcome === "linkDisabled") {
203
+ void queryClient.invalidateQueries({ queryKey: useOrganizationLinkingQuery.getKey() });
204
+ toast.error("Organizations can't be linked to the identity provider on this realm", {
205
+ description: reason ?? "Ask an administrator to enable organization linking.",
206
+ });
207
+ return;
208
+ }
209
+ invalidateTree();
210
+ if (outcome === "failed") {
211
+ toast.error("Couldn't link the organization", { description: reason ?? undefined });
212
+ return;
213
+ }
214
+ toast.success("Organization linked to the identity provider");
215
+ },
216
+ onError: (error) => {
217
+ const info = parseError(error, "Couldn't link the organization");
218
+ toast.error(info.message, { description: info.description });
219
+ },
220
+ });
193
221
  const treeRows = useMemo(() => [
194
222
  ...(realmDatastoreId ? [realmDefaultRow(realmDatastoreId)] : []),
195
223
  ...toDatastoreRows(data?.datastoreTree ?? []),
@@ -198,6 +226,9 @@ export function DatastoresRoute() {
198
226
  const allRows = useMemo(() => [...treeRows, ...unlinkedRows], [treeRows, unlinkedRows]);
199
227
  const rows = useMemo(() => visibleDatastoreRows(treeRows, visibility), [treeRows, visibility]);
200
228
  const unprovisionedCount = useMemo(() => hiddenRowCount(treeRows, visibility, "showUnprovisioned", rows.length), [treeRows, visibility, rows.length]);
229
+ const providerLinkEnabled = linkingQuery.data?.organizationLinking?.linkEnabled;
230
+ const linkingUnreadable = linkingQuery.isError || linkingQuery.data?.organizationLinking === null;
231
+ const tableRows = useMemo(() => (providerLinkEnabled === true ? rows.filter((row) => matchesProviderFilter(row, providerFilter)) : rows), [rows, providerFilter, providerLinkEnabled]);
201
232
  const unlinkedCount = unlinkedRows.length;
202
233
  const existingOrganizationNames = useMemo(() => existingOrganizationNameKeys(allRows), [allRows]);
203
234
  const organizationNameOptions = useMemo(() => organizationNames(allRows), [allRows]);
@@ -259,6 +290,7 @@ export function DatastoresRoute() {
259
290
  void queryClient.invalidateQueries({ queryKey: ["DatastoreProvisioningStatuses"] });
260
291
  if (result.linkUnlinkedDatastore.success) {
261
292
  toast.success(`Linked to the existing datastore "${result.linkUnlinkedDatastore.datastoreId}"`);
293
+ toastProviderWarning(result.linkUnlinkedDatastore.providerWarning);
262
294
  }
263
295
  else {
264
296
  toast.error("Couldn't link the existing datastore", {
@@ -316,6 +348,9 @@ export function DatastoresRoute() {
316
348
  if (isPresent(row.datastoreId))
317
349
  bringOnlineMutation.mutate({ datastoreId: row.datastoreId });
318
350
  }, [bringOnlineMutation.mutate]);
351
+ const onLinkProvider = useCallback((row) => {
352
+ linkProviderMutation.mutate({ organizationId: row.organizationId });
353
+ }, [linkProviderMutation.mutate]);
319
354
  const columns = useDatastoreColumns({
320
355
  statusByDatastoreId,
321
356
  currentDatastoreId: datastore?.id ?? realmDatastoreId,
@@ -335,6 +370,9 @@ export function DatastoresRoute() {
335
370
  onManageUsers,
336
371
  onBringOnline,
337
372
  isBringingOnline: bringOnlineMutation.isPending,
373
+ onLinkProvider,
374
+ isLinkingProvider: linkProviderMutation.isPending,
375
+ providerLinkEnabled,
338
376
  });
339
377
  const filterToggles = [
340
378
  ...(enableDatastoreRemoval
@@ -363,7 +401,7 @@ export function DatastoresRoute() {
363
401
  : []),
364
402
  ];
365
403
  const treeColumns = useMemo(() => columns.filter((column) => column.id !== datastoreColumnIds.suggestedMatch), [columns]);
366
- const { table, searchQuery, onSearchQueryChange, statusFilter, onStatusFilterChange } = useDatastoreTable("datastores", rows, treeColumns, sorting, setSorting);
404
+ const { table, searchQuery, onSearchQueryChange, statusFilter, onStatusFilterChange } = useDatastoreTable("datastores", tableRows, treeColumns, sorting, setSorting);
367
405
  const unlinkedColumns = useMemo(() => unlinkedColumnOrder.flatMap((id) => columns.filter((column) => column.id === id)), [columns]);
368
406
  const { table: unlinkedTable, searchQuery: unlinkedSearchQuery, onSearchQueryChange: onUnlinkedSearchQueryChange, statusFilter: unlinkedStatusFilter, onStatusFilterChange: onUnlinkedStatusFilterChange, } = useDatastoreTable("datastores-unlinked", unlinkedRows, unlinkedColumns, unlinkedSorting, setUnlinkedSorting);
369
407
  const scopeTabs = activeTabs.map((tab) => ({
@@ -449,10 +487,10 @@ export function DatastoresRoute() {
449
487
  };
450
488
  provisionMutation.mutate({ input }, { onSuccess: (result) => result.provisionDatastore.success && setFormTarget(null) });
451
489
  }
452
- return (_jsxs(RouteLayout, { user: user, datastore: datastore, datastores: datastores, isOperator: isOperator, canSwitchDatastore: canSwitchDatastore, logout: logout, children: [_jsx(PageHeader, { title: "Datastores", icon: Database, actions: _jsxs(_Fragment, { children: [_jsx(Button, { iconOnly: true, variant: "tertiary", onClick: handleRefresh, disabled: isFetching, icon: RefreshCcw, "aria-label": "Refresh datastores" }), activeTab === "organizations" && (_jsxs(_Fragment, { children: [filterToggles.map((toggle) => (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "secondary", "aria-pressed": toggle.active, onClick: toggle.onToggle, icon: toggle.icon, children: toggle.label }) }), _jsx(TooltipContent, { children: toggle.description })] }, toggle.key))), _jsx(Button, { icon: Plus, onClick: () => setFormTarget({ kind: "organization" }), children: "Add organization" })] }))] }) }), _jsxs(Tabs, { value: activeTab, onValueChange: (tab) => setSearchParams({ tab }, { replace: true }), className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [_jsxs(TabsContent, { value: "organizations", className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [_jsx("div", { className: "border-b border-muted-border px-5 py-3 shrink-0", children: _jsx(DatastoresToolbar, { tabs: tabsList, searchQuery: searchQuery, onSearchQueryChange: onSearchQueryChange, statusFilter: statusFilter, onStatusFilterChange: handleStatusFilterChange, table: table }) }), _jsxs("div", { className: "flex-1 overflow-auto p-4", children: [isLoading && _jsx(DatastoreTableSkeleton, {}), !isLoading && !isPresent(rows) && (_jsx(EmptyState, { icon: Database, title: "No organizations yet", description: "Provision an organization to give it its first datastore.", children: _jsx(Button, { icon: Plus, onClick: () => setFormTarget({ kind: "organization" }), children: "Add organization" }) })), !isLoading && isPresent(rows) && (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { className: "rounded-xl border border-muted-border bg-card overflow-hidden", children: _jsx(DataTable, { table: table, noResultsMessage: "No datastores match your filters." }) }), _jsx(TablePagination, { table: table, total: rows.length })] }))] })] }), _jsx(TabsContent, { value: "unlinked", className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: _jsx(UnlinkedDatastoresPanel, { tabs: tabsList, table: unlinkedTable, rows: unlinkedRows, searchQuery: unlinkedSearchQuery, onSearchQueryChange: onUnlinkedSearchQueryChange, statusFilter: unlinkedStatusFilter, onStatusFilterChange: onUnlinkedStatusFilterChange, columnLabels: unlinkedColumnLabels, columnDefaultOrder: unlinkedColumnOrder, isLoading: isUnlinkedLoading, errorMessage: unlinkedError != null
490
+ return (_jsxs(RouteLayout, { user: user, datastore: datastore, datastores: datastores, isOperator: isOperator, canSwitchDatastore: canSwitchDatastore, logout: logout, children: [_jsx(PageHeader, { title: "Datastores", icon: Database, actions: _jsxs(_Fragment, { children: [_jsx(Button, { iconOnly: true, variant: "tertiary", onClick: handleRefresh, disabled: isFetching, icon: RefreshCcw, "aria-label": "Refresh datastores" }), activeTab === "organizations" && (_jsxs(_Fragment, { children: [filterToggles.map((toggle) => (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "secondary", "aria-pressed": toggle.active, onClick: toggle.onToggle, icon: toggle.icon, children: toggle.label }) }), _jsx(TooltipContent, { children: toggle.description })] }, toggle.key))), _jsx(Button, { icon: Plus, onClick: () => setFormTarget({ kind: "organization" }), children: "Add organization" })] }))] }) }), _jsxs(Tabs, { value: activeTab, onValueChange: (tab) => setSearchParams({ tab }, { replace: true }), className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [_jsxs(TabsContent, { value: "organizations", className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [_jsx("div", { className: "border-b border-muted-border px-5 py-3 shrink-0", children: _jsx(DatastoresToolbar, { tabs: tabsList, searchQuery: searchQuery, onSearchQueryChange: onSearchQueryChange, statusFilter: statusFilter, onStatusFilterChange: handleStatusFilterChange, ...(providerLinkEnabled === true ? { providerFilter, onProviderFilterChange: setProviderFilter } : {}), table: table }) }), _jsxs("div", { className: "flex-1 overflow-auto p-4", children: [linkingUnreadable && (_jsx(Banner, { className: "mb-3", label: "Couldn't read whether organizations link to the identity provider, so linking is hidden until it can be." })), isLoading && _jsx(DatastoreTableSkeleton, {}), !isLoading && !isPresent(rows) && (_jsx(EmptyState, { icon: Database, title: "No organizations yet", description: "Provision an organization to give it its first datastore.", children: _jsx(Button, { icon: Plus, onClick: () => setFormTarget({ kind: "organization" }), children: "Add organization" }) })), !isLoading && isPresent(rows) && (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { className: "rounded-xl border border-muted-border bg-card overflow-hidden", children: _jsx(DataTable, { table: table, noResultsMessage: "No datastores match your filters." }) }), _jsx(TablePagination, { table: table, total: rows.length })] }))] })] }), _jsx(TabsContent, { value: "unlinked", className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: _jsx(UnlinkedDatastoresPanel, { tabs: tabsList, table: unlinkedTable, rows: unlinkedRows, searchQuery: unlinkedSearchQuery, onSearchQueryChange: onUnlinkedSearchQueryChange, statusFilter: unlinkedStatusFilter, onStatusFilterChange: onUnlinkedStatusFilterChange, columnLabels: unlinkedColumnLabels, columnDefaultOrder: unlinkedColumnOrder, isLoading: isUnlinkedLoading, errorMessage: unlinkedError != null
453
491
  ? (parseError(unlinkedError, "Couldn't list unlinked datastores").description ??
454
492
  "Couldn't list unlinked datastores")
455
- : null }) })] }), formTarget && (_jsx(DatastoreFormDialog, { target: formTarget, onSubmit: submitForm, onCancel: () => setFormTarget(null), isSubmitting: provisionMutation.isPending })), linkDialogTarget && (_jsx(LinkUnlinkedDatastoreDialog, { datastoreId: linkDialogTarget, suggestedMatch: suggestedLinkTarget(linkDialogTarget), organizationNames: organizationNameOptions, existingOrganizationNames: existingOrganizationNames, onClose: () => setLinkDialogTarget(null) })), renameTarget && (_jsx(RenameDatastoreGroupDialog, { target: renameTarget, onSubmit: (name) => {
493
+ : null }) })] }), formTarget && (_jsx(ProvisionDatastoreDialog, { target: formTarget, existingOrganizationNames: existingOrganizationNames, onSubmit: submitForm, onCancel: () => setFormTarget(null), isSubmitting: provisionMutation.isPending })), linkDialogTarget && (_jsx(LinkUnlinkedDatastoreDialog, { datastoreId: linkDialogTarget, suggestedMatch: suggestedLinkTarget(linkDialogTarget), organizationNames: organizationNameOptions, existingOrganizationNames: existingOrganizationNames, onClose: () => setLinkDialogTarget(null) })), renameTarget && (_jsx(RenameDatastoreGroupDialog, { target: renameTarget, onSubmit: (name) => {
456
494
  renameMutation.mutate({
457
495
  input: {
458
496
  id: renameTarget.id,
@@ -0,0 +1,11 @@
1
+ import type { GQLLinkOrganizationResponse, GQLTenantOrganization } from "@manta-eu/client";
2
+ export type LinkProviderOutcome = "linkDisabled" | "failed" | "linked";
3
+ export declare function linkProviderOutcome(response: Pick<GQLLinkOrganizationResponse, "linkEnabled" | "linked" | "mirrored">): LinkProviderOutcome;
4
+ export declare function tenantOrganizationHint(tenant: Pick<GQLTenantOrganization, "slug" | "exists" | "mirroredBy">, pushEnabled: boolean): string;
5
+ export type TenantOrganizationCheck = {
6
+ hint?: string;
7
+ blocked: boolean;
8
+ };
9
+ export declare function tenantOrganizationCheck(tenant: Pick<GQLTenantOrganization, "slug" | "exists" | "mirroredBy"> | null | undefined, pushEnabled: boolean): TenantOrganizationCheck;
10
+ export declare function toastProviderWarning(providerWarning: string | null | undefined): void;
11
+ //# sourceMappingURL=link-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-provider.d.ts","sourceRoot":"","sources":["../../../src/routes/datastores/link-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAI3F,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAGvE,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,IAAI,CAAC,2BAA2B,EAAE,aAAa,GAAG,QAAQ,GAAG,UAAU,CAAC,GACjF,mBAAmB,CAGrB;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC,EACrE,WAAW,EAAE,OAAO,GACnB,MAAM,CAeR;AAED,MAAM,MAAM,uBAAuB,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAE1E,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,CAAC,GAAG,IAAI,GAAG,SAAS,EACxF,WAAW,EAAE,OAAO,GACnB,uBAAuB,CAGzB;AAED,wBAAgB,oBAAoB,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,QAG9E"}