@manta-eu/react 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +1 -0
  2. package/dist/components/chat-view/Chat.d.ts +20 -0
  3. package/dist/components/chat-view/Chat.d.ts.map +1 -1
  4. package/dist/components/chat-view/Chat.js +51 -15
  5. package/dist/components/chat-view/Chat.js.map +1 -1
  6. package/dist/components/embedded-chat/index.d.ts +1 -1
  7. package/dist/components/embedded-chat/index.d.ts.map +1 -1
  8. package/dist/components/embedded-chat/index.js +1 -1
  9. package/dist/components/embedded-chat/index.js.map +1 -1
  10. package/dist/components/planning/ActioningView.d.ts.map +1 -1
  11. package/dist/components/planning/ActioningView.js +1 -1
  12. package/dist/components/planning/ActioningView.js.map +1 -1
  13. package/dist/components/planning/ExecutionTopBar.d.ts +1 -0
  14. package/dist/components/planning/ExecutionTopBar.d.ts.map +1 -1
  15. package/dist/components/planning/ExecutionTopBar.js +3 -2
  16. package/dist/components/planning/ExecutionTopBar.js.map +1 -1
  17. package/dist/components/planning/plan-detail-utils.d.ts +1 -0
  18. package/dist/components/planning/plan-detail-utils.d.ts.map +1 -1
  19. package/dist/components/planning/plan-detail-utils.js +7 -2
  20. package/dist/components/planning/plan-detail-utils.js.map +1 -1
  21. package/dist/components/tool-invocation-card/ToolOutputRenderer.d.ts.map +1 -1
  22. package/dist/components/tool-invocation-card/ToolOutputRenderer.js +5 -2
  23. package/dist/components/tool-invocation-card/ToolOutputRenderer.js.map +1 -1
  24. package/dist/graph/EntityCard.d.ts.map +1 -1
  25. package/dist/graph/EntityCard.js +2 -1
  26. package/dist/graph/EntityCard.js.map +1 -1
  27. package/dist/graph/EntityViewWithLinks.d.ts +7 -0
  28. package/dist/graph/EntityViewWithLinks.d.ts.map +1 -0
  29. package/dist/graph/EntityViewWithLinks.js +28 -0
  30. package/dist/graph/EntityViewWithLinks.js.map +1 -0
  31. package/dist/graph/card-links.d.ts +19 -0
  32. package/dist/graph/card-links.d.ts.map +1 -0
  33. package/dist/graph/card-links.js +23 -0
  34. package/dist/graph/card-links.js.map +1 -0
  35. package/dist/graph/nodes/BrandAssetAnalysisView.d.ts.map +1 -1
  36. package/dist/graph/nodes/BrandAssetAnalysisView.js +1 -1
  37. package/dist/graph/nodes/BrandAssetAnalysisView.js.map +1 -1
  38. package/dist/graph/nodes/BrandFontSuggestionView.d.ts.map +1 -1
  39. package/dist/graph/nodes/BrandFontSuggestionView.js +1 -1
  40. package/dist/graph/nodes/BrandFontSuggestionView.js.map +1 -1
  41. package/dist/graph/nodes/EntityCardShell.d.ts +2 -0
  42. package/dist/graph/nodes/EntityCardShell.d.ts.map +1 -1
  43. package/dist/graph/nodes/EntityCardShell.js +13 -3
  44. package/dist/graph/nodes/EntityCardShell.js.map +1 -1
  45. package/dist/graph/nodes/NodePropertyTable.d.ts +2 -1
  46. package/dist/graph/nodes/NodePropertyTable.d.ts.map +1 -1
  47. package/dist/graph/nodes/NodePropertyTable.js +10 -1
  48. package/dist/graph/nodes/NodePropertyTable.js.map +1 -1
  49. package/dist/graph/nodes/node-property-display.d.ts +0 -1
  50. package/dist/graph/nodes/node-property-display.d.ts.map +1 -1
  51. package/dist/graph/nodes/node-property-display.js +2 -6
  52. package/dist/graph/nodes/node-property-display.js.map +1 -1
  53. package/dist/graph/special-nodes/AttributeList.d.ts.map +1 -1
  54. package/dist/graph/special-nodes/AttributeList.js +3 -2
  55. package/dist/graph/special-nodes/AttributeList.js.map +1 -1
  56. package/dist/graph/special-nodes/RelatedSection.d.ts +6 -0
  57. package/dist/graph/special-nodes/RelatedSection.d.ts.map +1 -0
  58. package/dist/graph/special-nodes/RelatedSection.js +16 -0
  59. package/dist/graph/special-nodes/RelatedSection.js.map +1 -0
  60. package/dist/graph/special-nodes/card-facts.d.ts +2 -0
  61. package/dist/graph/special-nodes/card-facts.d.ts.map +1 -1
  62. package/dist/graph/special-nodes/card-facts.js +11 -0
  63. package/dist/graph/special-nodes/card-facts.js.map +1 -1
  64. package/dist/graph/types.d.ts +2 -2
  65. package/dist/graph/types.d.ts.map +1 -1
  66. package/dist/hooks/use-approve-anyway.d.ts.map +1 -1
  67. package/dist/hooks/use-approve-anyway.js +8 -8
  68. package/dist/hooks/use-approve-anyway.js.map +1 -1
  69. package/dist/hooks/use-connection-problems.d.ts.map +1 -1
  70. package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
  71. package/dist/ldo-ui.d.ts +1 -0
  72. package/dist/ldo-ui.d.ts.map +1 -1
  73. package/dist/ldo-ui.js +1 -0
  74. package/dist/ldo-ui.js.map +1 -1
  75. package/dist/lib/chat-types.d.ts +17 -0
  76. package/dist/lib/chat-types.d.ts.map +1 -1
  77. package/dist/lib/chat-types.js.map +1 -1
  78. package/dist/providers/ChatManagerProvider.d.ts +7 -0
  79. package/dist/providers/ChatManagerProvider.d.ts.map +1 -1
  80. package/dist/providers/ChatManagerProvider.js +129 -16
  81. package/dist/providers/ChatManagerProvider.js.map +1 -1
  82. package/dist/providers/chat-connection-context.d.ts +4 -1
  83. package/dist/providers/chat-connection-context.d.ts.map +1 -1
  84. package/dist/providers/chat-connection-context.js.map +1 -1
  85. package/dist/providers/chat-connection-lifecycle.d.ts +1 -0
  86. package/dist/providers/chat-connection-lifecycle.d.ts.map +1 -1
  87. package/dist/providers/chat-connection-lifecycle.js.map +1 -1
  88. package/dist/providers/chat-socket-auth.d.ts +3 -1
  89. package/dist/providers/chat-socket-auth.d.ts.map +1 -1
  90. package/dist/providers/chat-socket-auth.js +27 -9
  91. package/dist/providers/chat-socket-auth.js.map +1 -1
  92. package/dist/providers/chat-startup-timing.d.ts +14 -0
  93. package/dist/providers/chat-startup-timing.d.ts.map +1 -0
  94. package/dist/providers/chat-startup-timing.js +72 -0
  95. package/dist/providers/chat-startup-timing.js.map +1 -0
  96. package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.d.ts.map +1 -1
  97. package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.js +7 -21
  98. package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.js.map +1 -1
  99. package/dist/routes/data-explorer/components/record-detail/ChatPreview.d.ts.map +1 -1
  100. package/dist/routes/data-explorer/components/record-detail/ChatPreview.js +2 -2
  101. package/dist/routes/data-explorer/components/record-detail/ChatPreview.js.map +1 -1
  102. package/dist/routes/data-explorer/components/record-detail/action-properties.d.ts +2 -2
  103. package/dist/routes/data-explorer/components/record-detail/action-properties.d.ts.map +1 -1
  104. package/dist/routes/data-explorer/components/record-detail/action-properties.js +3 -3
  105. package/dist/routes/data-explorer/components/record-detail/action-properties.js.map +1 -1
  106. package/dist/routes/integrations/catalog-shape.d.ts +1 -0
  107. package/dist/routes/integrations/catalog-shape.d.ts.map +1 -1
  108. package/dist/routes/integrations/catalog-shape.js.map +1 -1
  109. package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.d.ts +7 -0
  110. package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.d.ts.map +1 -0
  111. package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.js +76 -0
  112. package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.js.map +1 -0
  113. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts +7 -0
  114. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts.map +1 -1
  115. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js +8 -7
  116. package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js.map +1 -1
  117. package/dist/routes/integrations/components/connect-forms/inferal-locations.d.ts +3 -0
  118. package/dist/routes/integrations/components/connect-forms/inferal-locations.d.ts.map +1 -0
  119. package/dist/routes/integrations/components/connect-forms/inferal-locations.js +13 -0
  120. package/dist/routes/integrations/components/connect-forms/inferal-locations.js.map +1 -0
  121. package/dist/routes/integrations/connect-forms.d.ts +2 -0
  122. package/dist/routes/integrations/connect-forms.d.ts.map +1 -1
  123. package/dist/routes/integrations/connect-forms.js +2 -0
  124. package/dist/routes/integrations/connect-forms.js.map +1 -1
  125. package/dist/routes/integrations/connection-view.d.ts.map +1 -1
  126. package/dist/routes/integrations/connection-view.js +1 -0
  127. package/dist/routes/integrations/connection-view.js.map +1 -1
  128. package/dist/routes/plans/components/ActionPlansTableColumns.d.ts.map +1 -1
  129. package/dist/routes/plans/components/ActionPlansTableColumns.js +3 -3
  130. package/dist/routes/plans/components/ActionPlansTableColumns.js.map +1 -1
  131. package/package.json +7 -7
  132. package/src/components/chat-view/Chat.tsx +62 -15
  133. package/src/components/embedded-chat/index.tsx +9 -2
  134. package/src/components/planning/ActioningView.tsx +1 -0
  135. package/src/components/planning/ExecutionTopBar.tsx +14 -2
  136. package/src/components/planning/plan-detail-utils.tsx +8 -2
  137. package/src/components/tool-invocation-card/ToolOutputRenderer.tsx +5 -2
  138. package/src/graph/EntityCard.tsx +2 -1
  139. package/src/graph/EntityViewWithLinks.tsx +43 -0
  140. package/src/graph/card-links.tsx +43 -0
  141. package/src/graph/nodes/BrandAssetAnalysisView.tsx +1 -0
  142. package/src/graph/nodes/BrandFontSuggestionView.tsx +1 -0
  143. package/src/graph/nodes/EntityCardShell.tsx +37 -3
  144. package/src/graph/nodes/NodePropertyTable.tsx +16 -2
  145. package/src/graph/nodes/node-property-display.ts +2 -7
  146. package/src/graph/special-nodes/AttributeList.tsx +4 -2
  147. package/src/graph/special-nodes/RelatedSection.tsx +29 -0
  148. package/src/graph/special-nodes/card-facts.ts +13 -0
  149. package/src/graph/types.ts +1 -1
  150. package/src/hooks/use-approve-anyway.ts +8 -11
  151. package/src/ldo-ui.ts +1 -0
  152. package/src/lib/chat-types.ts +18 -0
  153. package/src/providers/ChatManagerProvider.tsx +166 -15
  154. package/src/providers/chat-connection-context.ts +4 -0
  155. package/src/providers/chat-connection-lifecycle.ts +1 -0
  156. package/src/providers/chat-socket-auth.ts +31 -12
  157. package/src/providers/chat-startup-timing.ts +89 -0
  158. package/src/routes/data-explorer/components/record-detail/ActionFormDialog.tsx +7 -29
  159. package/src/routes/data-explorer/components/record-detail/ChatPreview.tsx +6 -1
  160. package/src/routes/data-explorer/components/record-detail/action-properties.ts +3 -3
  161. package/src/routes/integrations/catalog-shape.ts +2 -0
  162. package/src/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.tsx +140 -0
  163. package/src/routes/integrations/components/connect-forms/OAuthSetupConnectForm.tsx +27 -5
  164. package/src/routes/integrations/components/connect-forms/inferal-locations.ts +13 -0
  165. package/src/routes/integrations/connect-forms.ts +2 -0
  166. package/src/routes/integrations/connection-view.ts +1 -0
  167. package/src/routes/plans/components/ActionPlansTableColumns.tsx +3 -2
@@ -0,0 +1,140 @@
1
+ import {
2
+ GQLInferalLocation,
3
+ GQLIntegration,
4
+ useMicrosoftAdsSetupConnectMutation,
5
+ useMicrosoftAdsSetupProgressQuery,
6
+ type GQLMicrosoftAdsAccount,
7
+ } from "@manta-eu/client";
8
+ import { useQueryClient } from "@tanstack/react-query";
9
+ import { useState } from "react";
10
+ import { inferalLocationLabels } from "./inferal-locations";
11
+ import { OAuthSetupConnectForm, type OAuthSetup, type SetupCopy } from "./OAuthSetupConnectForm";
12
+ import type { ConnectFormProps } from "./types";
13
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "#components/select/index";
14
+ import type { TreeSelectItem } from "#components/tree-multi-select/index";
15
+ import { useMantaClient } from "#providers/index";
16
+
17
+ const copy: SetupCopy = {
18
+ providerName: "Microsoft Ads",
19
+ identityProviderName: "Microsoft",
20
+ entitySingular: "account",
21
+ entityPlural: "accounts",
22
+ signInDescription:
23
+ "Sign in the way you sign in to Microsoft Advertising: with a Microsoft account, or with Google if your Advertising login is a Google account.",
24
+ pickTitle: "Pick the Microsoft Ads accounts to connect",
25
+ pickDescription:
26
+ "Manta loads each account's Search campaigns, ad groups, ads and keywords, plus daily campaign, keyword and search-query reports.",
27
+ noEntitiesLabel:
28
+ "We couldn't find any Microsoft Advertising accounts for this Microsoft sign-in. Sign in with a different user or check your account access.",
29
+ noneConnectableLabel:
30
+ "Every Microsoft Advertising account on this sign-in is already connected. Sign in as a different user to connect another one.",
31
+ };
32
+
33
+ export const defaultMicrosoftAdsLocation = GQLInferalLocation.Enam;
34
+
35
+ // Microsoft Advertising refuses a Microsoft token for a login that is a Google account (GoogleAccountIsRequired).
36
+ const googleSignIn = [{ label: "Continue with Google", href: "/oauth/microsoft-ads-google/start" }] as const;
37
+
38
+ const inferalLocations: ReadonlySet<string> = new Set(Object.values(GQLInferalLocation));
39
+
40
+ function isInferalLocation(value: string): value is GQLInferalLocation {
41
+ return inferalLocations.has(value);
42
+ }
43
+
44
+ function accountHint(
45
+ account: GQLMicrosoftAdsAccount,
46
+ alreadyConnected: boolean,
47
+ reconnectable: boolean,
48
+ ): string | undefined {
49
+ if (!account.connectable) return "Unavailable";
50
+ if (reconnectable) return "Reconnect";
51
+ if (alreadyConnected) return "Already connected";
52
+ return undefined;
53
+ }
54
+
55
+ export function microsoftAdsTreeItems(
56
+ accounts: GQLMicrosoftAdsAccount[],
57
+ alreadyConnectedExternalIds: ReadonlySet<string>,
58
+ reconnectableExternalIds: ReadonlySet<string> = new Set(),
59
+ ): TreeSelectItem[] {
60
+ return accounts.map((account) => {
61
+ const reconnectable = reconnectableExternalIds.has(account.id);
62
+ const alreadyConnected = !reconnectable && alreadyConnectedExternalIds.has(account.id);
63
+ return {
64
+ id: account.id,
65
+ label: account.name ?? account.number ?? account.id,
66
+ caption: account.number ?? account.id,
67
+ hint: accountHint(account, alreadyConnected, reconnectable),
68
+ selectable: account.connectable,
69
+ disabled: alreadyConnected,
70
+ depth: 0,
71
+ };
72
+ });
73
+ }
74
+
75
+ function useMicrosoftAdsSetup(location: GQLInferalLocation): OAuthSetup<GQLMicrosoftAdsAccount> {
76
+ const { mantaClient } = useMantaClient();
77
+ const queryClient = useQueryClient();
78
+ const progressQuery = useMicrosoftAdsSetupProgressQuery(mantaClient, undefined, { staleTime: 0 });
79
+ const connectMutation = useMicrosoftAdsSetupConnectMutation(mantaClient);
80
+ const progress = progressQuery.data?.microsoftAdsSetupProgress;
81
+
82
+ return {
83
+ progress: progress && { state: progress.state, rows: progress.accounts, lastError: progress.lastError },
84
+ refetchProgress: progressQuery.refetch,
85
+ resetProgress: () =>
86
+ queryClient.setQueryData(useMicrosoftAdsSetupProgressQuery.getKey(), {
87
+ microsoftAdsSetupProgress: { state: [], accounts: null, lastError: null },
88
+ }),
89
+ connect: async (accountIds) =>
90
+ (await connectMutation.mutateAsync({ accountIds, location })).microsoftAdsSetupConnect,
91
+ isConnecting: connectMutation.isPending,
92
+ };
93
+ }
94
+
95
+ type LocationPickerProps = {
96
+ value: GQLInferalLocation;
97
+ onChange: (location: GQLInferalLocation) => void;
98
+ };
99
+
100
+ function LocationPicker(props: LocationPickerProps) {
101
+ const { value, onChange } = props;
102
+
103
+ return (
104
+ <div className="grid gap-1">
105
+ <Select value={value} onValueChange={(next) => isInferalLocation(next) && onChange(next)}>
106
+ <SelectTrigger label="Stream location" id="microsoft-ads-location" className="w-full h-10">
107
+ <SelectValue placeholder="Select a region" />
108
+ </SelectTrigger>
109
+ <SelectContent>
110
+ {Object.entries(inferalLocationLabels).map(([location, label]) => (
111
+ <SelectItem key={location} value={location}>
112
+ {label}
113
+ </SelectItem>
114
+ ))}
115
+ </SelectContent>
116
+ </Select>
117
+ <p className="typography-caption text-muted-foreground">
118
+ Where the account's data stream runs. Pick the region closest to your account.
119
+ </p>
120
+ </div>
121
+ );
122
+ }
123
+
124
+ export function MicrosoftAdsConnectForm(props: ConnectFormProps) {
125
+ const [location, setLocation] = useState<GQLInferalLocation>(defaultMicrosoftAdsLocation);
126
+ const setup = useMicrosoftAdsSetup(location);
127
+
128
+ return (
129
+ <OAuthSetupConnectForm
130
+ {...props}
131
+ integration={GQLIntegration.MicrosoftAds}
132
+ oauthStartPath="/oauth/microsoft-ads/start"
133
+ copy={copy}
134
+ setup={setup}
135
+ treeItems={microsoftAdsTreeItems}
136
+ pickExtras={<LocationPicker value={location} onChange={setLocation} />}
137
+ alternateSignIns={googleSignIn}
138
+ />
139
+ );
140
+ }
@@ -2,7 +2,7 @@ import { GQLGoogleSetupStateEnum, type GQLIntegration } from "@manta-eu/client";
2
2
  import { parseError } from "@manta-eu/client/shared/error-utils";
3
3
  import { isPresent } from "@manta-eu/lib/present";
4
4
  import { ArrowLeft } from "lucide-react";
5
- import { useEffect, useRef, useState } from "react";
5
+ import { useEffect, useRef, useState, type ReactNode } from "react";
6
6
  import { toast } from "sonner";
7
7
  import { match } from "ts-pattern";
8
8
  import { ConnectFormHeader } from "./ConnectFormHeader";
@@ -66,11 +66,19 @@ export type OAuthSetupConnectFormProps<Row extends SetupRow> = ConnectFormProps
66
66
  ) => TreeSelectItem[];
67
67
  treeOptions?: SetupTreeOptions;
68
68
  resumesUnfinishedSetup?: boolean;
69
+ // Rendered under the list while picking, for a choice the connect needs besides the ids.
70
+ pickExtras?: ReactNode;
71
+ // Other identity providers that lead into the same setup, offered beside the main sign-in.
72
+ alternateSignIns?: readonly AlternateSignIn[];
69
73
  };
70
74
 
75
+ export type AlternateSignIn = { label: string; href: string };
76
+
71
77
  export function OAuthSetupConnectForm<Row extends SetupRow>(props: OAuthSetupConnectFormProps<Row>) {
72
78
  const { onBack, onChanged, onDismiss, provider, resumedFromOAuth = false, importNotice } = props;
73
- const { integration, oauthStartPath, copy, setup, treeItems, treeOptions, resumesUnfinishedSetup } = props;
79
+ const { integration, oauthStartPath, copy, setup, treeItems, treeOptions, resumesUnfinishedSetup, pickExtras } =
80
+ props;
81
+ const { alternateSignIns } = props;
74
82
  const { providerName } = copy;
75
83
  const [hasStartedOAuth, setHasStartedOAuth] = useState(resumedFromOAuth);
76
84
  const [hasSeenActiveState, setHasSeenActiveState] = useState(resumedFromOAuth);
@@ -151,7 +159,14 @@ export function OAuthSetupConnectForm<Row extends SetupRow>(props: OAuthSetupCon
151
159
 
152
160
  <div className="min-h-40">
153
161
  {match(phase)
154
- .with("idle", () => <IdleView copy={copy} oauthStartPath={oauthStartPath} onOpened={handleOpened} />)
162
+ .with("idle", () => (
163
+ <IdleView
164
+ copy={copy}
165
+ oauthStartPath={oauthStartPath}
166
+ alternateSignIns={alternateSignIns}
167
+ onOpened={handleOpened}
168
+ />
169
+ ))
155
170
  .with("awaitingOAuth", () => <AwaitingOAuthView copy={copy} />)
156
171
  .with("loadingAccounts", () => <LoadingEntitiesView copy={copy} />)
157
172
  .with("pickingAccount", () => (
@@ -161,6 +176,7 @@ export function OAuthSetupConnectForm<Row extends SetupRow>(props: OAuthSetupCon
161
176
  selectedIds={selectedIds}
162
177
  onToggle={toggleId}
163
178
  treeOptions={treeOptions}
179
+ extras={pickExtras}
164
180
  />
165
181
  ))
166
182
  .with("submitting", () => <SubmittingView copy={copy} />)
@@ -221,11 +237,12 @@ type CopyProps = {
221
237
 
222
238
  type IdleViewProps = CopyProps & {
223
239
  oauthStartPath: string;
240
+ alternateSignIns?: readonly AlternateSignIn[];
224
241
  onOpened: () => void;
225
242
  };
226
243
 
227
244
  function IdleView(props: IdleViewProps) {
228
- const { copy, oauthStartPath, onOpened } = props;
245
+ const { copy, oauthStartPath, alternateSignIns, onOpened } = props;
229
246
 
230
247
  return (
231
248
  <div className="grid gap-4">
@@ -240,6 +257,9 @@ function IdleView(props: IdleViewProps) {
240
257
  label={`Continue with ${copy.identityProviderName}`}
241
258
  onOpened={onOpened}
242
259
  />
260
+ {alternateSignIns?.map(({ label, href }) => (
261
+ <OAuthConnectLink key={href} href={href} label={label} variant="secondary" onOpened={onOpened} />
262
+ ))}
243
263
  </div>
244
264
  );
245
265
  }
@@ -283,10 +303,11 @@ type PickingEntityViewProps = CopyProps & {
283
303
  selectedIds: ReadonlySet<string>;
284
304
  onToggle: (id: string, checked: boolean) => void;
285
305
  treeOptions?: SetupTreeOptions;
306
+ extras?: ReactNode;
286
307
  };
287
308
 
288
309
  function PickingEntityView(props: PickingEntityViewProps) {
289
- const { copy, items, selectedIds, onToggle, treeOptions } = props;
310
+ const { copy, items, selectedIds, onToggle, treeOptions, extras } = props;
290
311
 
291
312
  if (!isPresent(items)) {
292
313
  return <Banner type="warning" label={copy.noEntitiesLabel} />;
@@ -310,6 +331,7 @@ function PickingEntityView(props: PickingEntityViewProps) {
310
331
  searchPlaceholder={`Search ${copy.entityPlural}...`}
311
332
  {...treeOptions}
312
333
  />
334
+ {extras}
313
335
  </div>
314
336
  );
315
337
  }
@@ -0,0 +1,13 @@
1
+ import { GQLInferalLocation } from "@manta-eu/client";
2
+
3
+ export const inferalLocationLabels: Readonly<Record<GQLInferalLocation, string>> = {
4
+ [GQLInferalLocation.Wnam]: "Western North America",
5
+ [GQLInferalLocation.Enam]: "Eastern North America",
6
+ [GQLInferalLocation.Sam]: "South America",
7
+ [GQLInferalLocation.Weur]: "Western Europe",
8
+ [GQLInferalLocation.Eeur]: "Eastern Europe",
9
+ [GQLInferalLocation.Apac]: "Asia Pacific",
10
+ [GQLInferalLocation.Oc]: "Oceania",
11
+ [GQLInferalLocation.Afr]: "Africa",
12
+ [GQLInferalLocation.Me]: "Middle East",
13
+ };
@@ -14,6 +14,7 @@ export { GoogleAnalyticsConnectForm } from "./components/connect-forms/GoogleAna
14
14
  export { KlaviyoConnectForm } from "./components/connect-forms/KlaviyoConnectForm";
15
15
  export { LinkedinConnectForm } from "./components/connect-forms/LinkedinConnectForm";
16
16
  export { MetaAdsConnectForm } from "./components/connect-forms/MetaAdsConnectForm";
17
+ export { MicrosoftAdsConnectForm } from "./components/connect-forms/MicrosoftAdsConnectForm";
17
18
  export { ShopifyConnectForm } from "./components/connect-forms/ShopifyConnectForm";
18
19
  export { SlackConnectForm } from "./components/connect-forms/SlackConnectForm";
19
20
  export { TriplewhaleConnectForm } from "./components/connect-forms/TriplewhaleConnectForm";
@@ -25,3 +26,4 @@ export {
25
26
  type MantaSyncPluginLinkProps,
26
27
  type MantaSyncPluginStepProps,
27
28
  } from "./components/MantaSyncPluginDownload";
29
+ export { inferalLocationLabels } from "./components/connect-forms/inferal-locations";
@@ -44,6 +44,7 @@ const oauthAccountNoun = {
44
44
  [GQLIntegration.GoogleAnalytics]: "Property",
45
45
  [GQLIntegration.Meta]: "Ad account",
46
46
  [GQLIntegration.Linkedin]: "Ad account",
47
+ [GQLIntegration.MicrosoftAds]: "Account",
47
48
  [GQLIntegration.Snapchat]: "Ad account",
48
49
  [GQLIntegration.Shopify]: "Shop",
49
50
  [GQLIntegration.Woocommerce]: "Store",
@@ -16,6 +16,7 @@ import {
16
16
  IntegrationIcon,
17
17
  integrationToChannel,
18
18
  isTerminalActionPlanState,
19
+ needsApproval,
19
20
  planStateVariant,
20
21
  statusLabel,
21
22
  } from "#components/planning/plan-detail-utils";
@@ -38,9 +39,9 @@ function planStateLabel(plan: ActionPlan, pendingApprovalCount: number): string
38
39
  return match({
39
40
  state: plan.state,
40
41
  hasCurrentFacts: plan.hasCurrentFacts,
41
- hasPendingApproval: pendingApprovalCount > 0,
42
+ needsApproval: needsApproval(plan.state, pendingApprovalCount),
42
43
  })
43
- .with({ state: GQLActionPlanState.Running, hasPendingApproval: true }, () => "Needs approval")
44
+ .with({ needsApproval: true }, () => "Needs approval")
44
45
  .with({ state: GQLActionPlanState.Draft, hasCurrentFacts: false }, () => "Not currently executable")
45
46
  .with({ state: GQLActionPlanState.Draft }, () => "Ready to launch")
46
47
  .otherwise(({ state: matchedState }) => statusLabel(matchedState));