@manta-eu/react 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/planning/plan-detail-utils.d.ts.map +1 -1
- package/dist/components/planning/plan-detail-utils.js +2 -0
- package/dist/components/planning/plan-detail-utils.js.map +1 -1
- package/dist/graph/EntityCard.d.ts.map +1 -1
- package/dist/graph/EntityCard.js +2 -1
- package/dist/graph/EntityCard.js.map +1 -1
- package/dist/graph/EntityViewWithLinks.d.ts +7 -0
- package/dist/graph/EntityViewWithLinks.d.ts.map +1 -0
- package/dist/graph/EntityViewWithLinks.js +28 -0
- package/dist/graph/EntityViewWithLinks.js.map +1 -0
- package/dist/graph/card-links.d.ts +19 -0
- package/dist/graph/card-links.d.ts.map +1 -0
- package/dist/graph/card-links.js +23 -0
- package/dist/graph/card-links.js.map +1 -0
- package/dist/graph/nodes/BrandAssetAnalysisView.d.ts.map +1 -1
- package/dist/graph/nodes/BrandAssetAnalysisView.js +1 -1
- package/dist/graph/nodes/BrandAssetAnalysisView.js.map +1 -1
- package/dist/graph/nodes/BrandFontSuggestionView.d.ts.map +1 -1
- package/dist/graph/nodes/BrandFontSuggestionView.js +1 -1
- package/dist/graph/nodes/BrandFontSuggestionView.js.map +1 -1
- package/dist/graph/nodes/EntityCardShell.d.ts +2 -0
- package/dist/graph/nodes/EntityCardShell.d.ts.map +1 -1
- package/dist/graph/nodes/EntityCardShell.js +13 -3
- package/dist/graph/nodes/EntityCardShell.js.map +1 -1
- package/dist/graph/nodes/NodePropertyTable.d.ts +2 -1
- package/dist/graph/nodes/NodePropertyTable.d.ts.map +1 -1
- package/dist/graph/nodes/NodePropertyTable.js +10 -1
- package/dist/graph/nodes/NodePropertyTable.js.map +1 -1
- package/dist/graph/nodes/node-property-display.d.ts +0 -1
- package/dist/graph/nodes/node-property-display.d.ts.map +1 -1
- package/dist/graph/nodes/node-property-display.js +2 -6
- package/dist/graph/nodes/node-property-display.js.map +1 -1
- package/dist/graph/special-nodes/AttributeList.d.ts.map +1 -1
- package/dist/graph/special-nodes/AttributeList.js +3 -2
- package/dist/graph/special-nodes/AttributeList.js.map +1 -1
- package/dist/graph/special-nodes/RelatedSection.d.ts +6 -0
- package/dist/graph/special-nodes/RelatedSection.d.ts.map +1 -0
- package/dist/graph/special-nodes/RelatedSection.js +16 -0
- package/dist/graph/special-nodes/RelatedSection.js.map +1 -0
- package/dist/graph/special-nodes/card-facts.d.ts +2 -0
- package/dist/graph/special-nodes/card-facts.d.ts.map +1 -1
- package/dist/graph/special-nodes/card-facts.js +11 -0
- package/dist/graph/special-nodes/card-facts.js.map +1 -1
- package/dist/graph/types.d.ts +2 -2
- package/dist/graph/types.d.ts.map +1 -1
- package/dist/hooks/use-approve-anyway.d.ts.map +1 -1
- package/dist/hooks/use-approve-anyway.js +8 -8
- package/dist/hooks/use-approve-anyway.js.map +1 -1
- package/dist/hooks/use-connection-problems.d.ts.map +1 -1
- package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
- package/dist/ldo-ui.d.ts +1 -0
- package/dist/ldo-ui.d.ts.map +1 -1
- package/dist/ldo-ui.js +1 -0
- package/dist/ldo-ui.js.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.d.ts.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.js +7 -21
- package/dist/routes/data-explorer/components/record-detail/ActionFormDialog.js.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/ChatPreview.d.ts.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/ChatPreview.js +2 -2
- package/dist/routes/data-explorer/components/record-detail/ChatPreview.js.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/action-properties.d.ts +2 -2
- package/dist/routes/data-explorer/components/record-detail/action-properties.d.ts.map +1 -1
- package/dist/routes/data-explorer/components/record-detail/action-properties.js +3 -3
- package/dist/routes/data-explorer/components/record-detail/action-properties.js.map +1 -1
- package/dist/routes/integrations/catalog-shape.d.ts +1 -0
- package/dist/routes/integrations/catalog-shape.d.ts.map +1 -1
- package/dist/routes/integrations/catalog-shape.js.map +1 -1
- package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.d.ts +7 -0
- package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.js +76 -0
- package/dist/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.js.map +1 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts +7 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js +8 -7
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js.map +1 -1
- package/dist/routes/integrations/components/connect-forms/inferal-locations.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/inferal-locations.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/inferal-locations.js +13 -0
- package/dist/routes/integrations/components/connect-forms/inferal-locations.js.map +1 -0
- package/dist/routes/integrations/connect-forms.d.ts +2 -0
- package/dist/routes/integrations/connect-forms.d.ts.map +1 -1
- package/dist/routes/integrations/connect-forms.js +2 -0
- package/dist/routes/integrations/connect-forms.js.map +1 -1
- package/dist/routes/integrations/connection-view.d.ts.map +1 -1
- package/dist/routes/integrations/connection-view.js +1 -0
- package/dist/routes/integrations/connection-view.js.map +1 -1
- package/package.json +7 -7
- package/src/components/planning/plan-detail-utils.tsx +2 -0
- package/src/graph/EntityCard.tsx +2 -1
- package/src/graph/EntityViewWithLinks.tsx +43 -0
- package/src/graph/card-links.tsx +43 -0
- package/src/graph/nodes/BrandAssetAnalysisView.tsx +1 -0
- package/src/graph/nodes/BrandFontSuggestionView.tsx +1 -0
- package/src/graph/nodes/EntityCardShell.tsx +37 -3
- package/src/graph/nodes/NodePropertyTable.tsx +16 -2
- package/src/graph/nodes/node-property-display.ts +2 -7
- package/src/graph/special-nodes/AttributeList.tsx +4 -2
- package/src/graph/special-nodes/RelatedSection.tsx +29 -0
- package/src/graph/special-nodes/card-facts.ts +13 -0
- package/src/graph/types.ts +1 -1
- package/src/hooks/use-approve-anyway.ts +8 -11
- package/src/ldo-ui.ts +1 -0
- package/src/routes/data-explorer/components/record-detail/ActionFormDialog.tsx +7 -29
- package/src/routes/data-explorer/components/record-detail/ChatPreview.tsx +6 -1
- package/src/routes/data-explorer/components/record-detail/action-properties.ts +3 -3
- package/src/routes/integrations/catalog-shape.ts +2 -0
- package/src/routes/integrations/components/connect-forms/MicrosoftAdsConnectForm.tsx +140 -0
- package/src/routes/integrations/components/connect-forms/OAuthSetupConnectForm.tsx +27 -5
- package/src/routes/integrations/components/connect-forms/inferal-locations.ts +13 -0
- package/src/routes/integrations/connect-forms.ts +2 -0
- package/src/routes/integrations/connection-view.ts +1 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { useExecuteAgentActionAdditionMutation } from "@manta-eu/client";
|
|
2
|
+
import { entityActionInput } from "@manta-eu/client/shared/action-document";
|
|
3
|
+
import { BrandAssetDecisionSchema } from "@manta-eu/ldo/schemas/brand";
|
|
2
4
|
import { isPresent } from "@manta-eu/lib/present";
|
|
3
5
|
import { isValidFullIri, namespaces } from "@manta-eu/rdf-lib/namespaces";
|
|
4
6
|
import { useQueryClient } from "@tanstack/react-query";
|
|
@@ -8,13 +10,12 @@ import { useCallback, useMemo, useRef, useState } from "react";
|
|
|
8
10
|
import { toast } from "sonner";
|
|
9
11
|
import type { ManualAction } from "./use-manual-actions";
|
|
10
12
|
import { useSparqlSelect } from "./use-sparql-construct";
|
|
11
|
-
import {
|
|
13
|
+
import { mintSubjectIri } from "#lib/action-document";
|
|
12
14
|
import { useMantaClient } from "#providers/MantaClientProvider";
|
|
13
15
|
import { invalidateActionPlanQueries } from "#shared/query-keys";
|
|
14
16
|
|
|
15
17
|
const df = new DataFactory();
|
|
16
18
|
const assetAnalysis = `${namespaces.brand}AssetAnalysis`;
|
|
17
|
-
const assetDecision = `${namespaces.brand}AssetDecision`;
|
|
18
19
|
const decideAssetShape = `${namespaces.brand}DecideAssetShape`;
|
|
19
20
|
|
|
20
21
|
export type ApproveAnywayHandle = {
|
|
@@ -68,16 +69,12 @@ export function useApproveAnyway(planGraphIri: string): ApproveAnywayHandle {
|
|
|
68
69
|
if (inFlight.current.has(analysisIri)) return;
|
|
69
70
|
inFlight.current.add(analysisIri);
|
|
70
71
|
setApproving(new Set(inFlight.current));
|
|
71
|
-
const subjectIri = mintSubjectIri(assetDecision);
|
|
72
|
-
const properties = buildActionDocument(
|
|
73
|
-
{ subjectIri, rdfType: assetDecision },
|
|
74
|
-
{
|
|
75
|
-
[`${namespaces.brand}decisionOnAsset`]: { "@id": analysisIri },
|
|
76
|
-
[`${namespaces.brand}decision`]: "approved",
|
|
77
|
-
},
|
|
78
|
-
);
|
|
79
72
|
void startAddition({
|
|
80
|
-
input:
|
|
73
|
+
input: entityActionInput(BrandAssetDecisionSchema, decideAssetShape, {
|
|
74
|
+
$id: mintSubjectIri(BrandAssetDecisionSchema["@type"]),
|
|
75
|
+
decisionOnAsset: analysisIri,
|
|
76
|
+
decision: "approved",
|
|
77
|
+
}),
|
|
81
78
|
})
|
|
82
79
|
.then(({ executeActionAddition }) => {
|
|
83
80
|
if (!executeActionAddition.success) throw new Error(executeActionAddition.error ?? "The approval failed");
|
package/src/ldo-ui.ts
CHANGED
|
@@ -344,6 +344,7 @@ export {
|
|
|
344
344
|
export { NodeFrame, type NodeFrameProps } from "./graph/special-nodes/NodeFrame";
|
|
345
345
|
|
|
346
346
|
export { EntityCard, findEntityNode, type EntityCardProps } from "./graph/EntityCard";
|
|
347
|
+
export { EntityViewWithLinks, type EntityViewWithLinksProps } from "./graph/EntityViewWithLinks";
|
|
347
348
|
export { findDetailViewForTypes, findPluralViewForTypes, type DetailView, type PluralView } from "./graph/detail-views";
|
|
348
349
|
export { EntityStripDisplay, type EntityStripDisplayProps } from "./graph/EntityStripDisplay";
|
|
349
350
|
export { stubNodeData } from "./graph/stub-node";
|
|
@@ -59,7 +59,6 @@ export function ActionFormDialog(props: ActionFormDialogProps) {
|
|
|
59
59
|
const baselineRef = useRef<PropertyMap | null>(null);
|
|
60
60
|
const [refusal, setRefusal] = useState<string | null>(null);
|
|
61
61
|
const [formElement, setFormElement] = useState<ShaclFormElement | null>(null);
|
|
62
|
-
const [isReadingForm, setIsReadingForm] = useState(false);
|
|
63
62
|
|
|
64
63
|
const attachForm = useCallback((element: ShaclFormElement | null) => setFormElement(element), []);
|
|
65
64
|
|
|
@@ -77,29 +76,20 @@ export function ActionFormDialog(props: ActionFormDialogProps) {
|
|
|
77
76
|
useEffect(() => {
|
|
78
77
|
if (!formElement || !isUpdate) return;
|
|
79
78
|
|
|
80
|
-
let cancelled = false;
|
|
81
|
-
|
|
82
79
|
// Baseline is the form's own serialization: its editors normalize literals, the triples do not.
|
|
83
|
-
|
|
80
|
+
function onReady() {
|
|
84
81
|
const store = formElement?.toRDF();
|
|
85
|
-
const baseline = store ?
|
|
86
|
-
|
|
87
|
-
if (baseline && !cancelled) baselineRef.current = baseline;
|
|
88
|
-
}
|
|
82
|
+
const baseline = store ? baselineFromStore(store, subjectIri) : null;
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
void captureBaseline();
|
|
84
|
+
if (baseline) baselineRef.current = baseline;
|
|
92
85
|
}
|
|
93
86
|
|
|
94
87
|
onReady();
|
|
95
88
|
formElement.addEventListener("ready", onReady);
|
|
96
|
-
return () =>
|
|
97
|
-
cancelled = true;
|
|
98
|
-
formElement.removeEventListener("ready", onReady);
|
|
99
|
-
};
|
|
89
|
+
return () => formElement.removeEventListener("ready", onReady);
|
|
100
90
|
}, [formElement, isUpdate, subjectIri]);
|
|
101
91
|
|
|
102
|
-
|
|
92
|
+
function submit() {
|
|
103
93
|
const store = formElement?.toRDF();
|
|
104
94
|
const baseline = baselineRef.current;
|
|
105
95
|
|
|
@@ -108,14 +98,7 @@ export function ActionFormDialog(props: ActionFormDialogProps) {
|
|
|
108
98
|
return;
|
|
109
99
|
}
|
|
110
100
|
|
|
111
|
-
|
|
112
|
-
const edited = await propertiesFromStore(store, subjectIri).catch(() => null);
|
|
113
|
-
setIsReadingForm(false);
|
|
114
|
-
|
|
115
|
-
if (!edited) {
|
|
116
|
-
setRefusal("The form could not be read.");
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
101
|
+
const edited = propertiesFromStore(store, subjectIri);
|
|
119
102
|
|
|
120
103
|
if (isUpdate && baseline) {
|
|
121
104
|
const cleared = removedPredicates(baseline, edited);
|
|
@@ -176,12 +159,7 @@ export function ActionFormDialog(props: ActionFormDialogProps) {
|
|
|
176
159
|
<Button variant="tertiary" size="sm" onClick={() => onOpenChange(false)}>
|
|
177
160
|
Cancel
|
|
178
161
|
</Button>
|
|
179
|
-
<Button
|
|
180
|
-
size="sm"
|
|
181
|
-
onClick={() => void submit()}
|
|
182
|
-
isLoading={isPending || isReadingForm}
|
|
183
|
-
disabled={!shapesTurtle}
|
|
184
|
-
>
|
|
162
|
+
<Button size="sm" onClick={submit} isLoading={isPending} disabled={!shapesTurtle}>
|
|
185
163
|
{copy.submitLabel}
|
|
186
164
|
</Button>
|
|
187
165
|
</DialogFooter>
|
|
@@ -7,6 +7,7 @@ import { actionShapesForTypes } from "./action-shapes";
|
|
|
7
7
|
import { Badge } from "#components/badge/index";
|
|
8
8
|
import {
|
|
9
9
|
type ActionShapeSpec,
|
|
10
|
+
EntityViewWithLinks,
|
|
10
11
|
findEntityNode,
|
|
11
12
|
GenericView,
|
|
12
13
|
getEntityView,
|
|
@@ -97,7 +98,11 @@ export function ChatPreview(props: ChatPreviewProps) {
|
|
|
97
98
|
<section>
|
|
98
99
|
<h3 className={cn(sectionLabelClass, "mb-1.5 text-muted-foreground")}>Chat rendering</h3>
|
|
99
100
|
<div className="max-w-md">
|
|
100
|
-
{EntityView ?
|
|
101
|
+
{EntityView ? (
|
|
102
|
+
<EntityViewWithLinks view={EntityView} data={node.data} />
|
|
103
|
+
) : (
|
|
104
|
+
<GenericView data={node.data} defaultExpanded />
|
|
105
|
+
)}
|
|
101
106
|
</div>
|
|
102
107
|
</section>
|
|
103
108
|
|
|
@@ -7,14 +7,14 @@ export type PropertyMap = Record<string, unknown>;
|
|
|
7
7
|
// The form stamps this on every serialization
|
|
8
8
|
const conformsTo = `${namespaces.dcterms}conformsTo`;
|
|
9
9
|
|
|
10
|
-
export
|
|
10
|
+
export function propertiesFromStore(store: ShaclFormStore, subjectIri: string): PropertyMap {
|
|
11
11
|
const quads = [...store].filter((quad) => quad.predicate.value !== conformsTo);
|
|
12
|
-
const { "@id": _id, "@type": _type, ...properties } =
|
|
12
|
+
const { "@id": _id, "@type": _type, ...properties } = documentFromQuads(quads, subjectIri);
|
|
13
13
|
|
|
14
14
|
return properties;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export
|
|
17
|
+
export function baselineFromStore(store: ShaclFormStore, subjectIri: string): PropertyMap | null {
|
|
18
18
|
if (store.size === 0) return null;
|
|
19
19
|
|
|
20
20
|
return propertiesFromStore(store, subjectIri);
|
|
@@ -22,6 +22,8 @@ export type OAuthConnectionMethod = ConnectionMethodBase & {
|
|
|
22
22
|
source: "oauth";
|
|
23
23
|
integration: GQLIntegration;
|
|
24
24
|
callbackProvider?: string;
|
|
25
|
+
// Further oauth_provider values whose callback resumes this same method, e.g. a second identity provider.
|
|
26
|
+
alternateCallbackProviders?: readonly string[];
|
|
25
27
|
resumesSetupAfterOAuth?: boolean;
|
|
26
28
|
connectedAction?: ComponentType;
|
|
27
29
|
};
|
|
@@ -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 } =
|
|
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", () =>
|
|
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",
|