@manta-eu/react 0.1.7 → 0.1.8
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/README.md +2 -0
- package/dist/components/chart/AreaChart.d.ts.map +1 -1
- package/dist/components/chart/AreaChart.js +4 -6
- package/dist/components/chart/BarChart.d.ts.map +1 -1
- package/dist/components/chart/BarChart.js +5 -6
- package/dist/components/chart/ComposedChart.d.ts.map +1 -1
- package/dist/components/chart/ComposedChart.js +15 -7
- package/dist/components/chart/LineChart.d.ts.map +1 -1
- package/dist/components/chart/LineChart.js +4 -6
- package/dist/components/chart/annotations.d.ts +4 -0
- package/dist/components/chart/annotations.d.ts.map +1 -1
- package/dist/components/chart/annotations.js +10 -2
- package/dist/components/chart/chart-schemas.d.ts +1960 -54
- package/dist/components/chart/chart-schemas.d.ts.map +1 -1
- package/dist/components/chart/chart-schemas.js +137 -15
- package/dist/components/chart/series.d.ts +28 -0
- package/dist/components/chart/series.d.ts.map +1 -0
- package/dist/components/chart/series.js +61 -0
- package/dist/components/chart/stacks.d.ts +9 -0
- package/dist/components/chart/stacks.d.ts.map +1 -0
- package/dist/components/chart/stacks.js +11 -0
- package/dist/components/chat-view/ChatHeaders.d.ts.map +1 -1
- package/dist/components/chat-view/ChatHeaders.js +2 -2
- package/dist/components/layouts/Sidebar.d.ts.map +1 -1
- package/dist/components/layouts/Sidebar.js +14 -3
- package/dist/components/planning/ActioningView.d.ts.map +1 -1
- package/dist/components/planning/ActioningView.js +7 -49
- package/dist/components/planning/ExecutionSectionItem.d.ts +6 -4
- package/dist/components/planning/ExecutionSectionItem.d.ts.map +1 -1
- package/dist/components/planning/ExecutionSectionItem.js +2 -2
- package/dist/components/planning/ExecutionSections.d.ts +6 -4
- package/dist/components/planning/ExecutionSections.d.ts.map +1 -1
- package/dist/components/planning/ExecutionSections.js +2 -2
- package/dist/components/planning/ManualActionCard.d.ts +14 -8
- package/dist/components/planning/ManualActionCard.d.ts.map +1 -1
- package/dist/components/planning/ManualActionCard.js +44 -24
- package/dist/components/planning/ManualActionList.d.ts +22 -0
- package/dist/components/planning/ManualActionList.d.ts.map +1 -0
- package/dist/components/planning/ManualActionList.js +35 -0
- package/dist/components/planning/manual-action-targets.d.ts +4 -0
- package/dist/components/planning/manual-action-targets.d.ts.map +1 -0
- package/dist/components/planning/manual-action-targets.js +19 -0
- package/dist/components/sidebar/index.d.ts +2 -0
- package/dist/components/sidebar/index.d.ts.map +1 -1
- package/dist/components/sidebar/index.js +1 -0
- package/dist/components/sidebar/nav-user.d.ts +1 -1
- package/dist/components/sidebar/nav-user.d.ts.map +1 -1
- package/dist/components/sidebar/nav-user.js +3 -3
- package/dist/components/sidebar/user-avatar.d.ts +7 -0
- package/dist/components/sidebar/user-avatar.d.ts.map +1 -0
- package/dist/components/sidebar/user-avatar.js +9 -0
- package/dist/globals.css +6 -13
- package/dist/hooks/use-connection-problems.d.ts.map +1 -1
- package/dist/hooks/use-manual-actions.d.ts +13 -3
- package/dist/hooks/use-manual-actions.d.ts.map +1 -1
- package/dist/hooks/use-manual-actions.js +16 -10
- package/dist/hooks/useAuth.d.ts +1 -0
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +7 -1
- package/dist/hooks/useConversations.d.ts +1 -0
- package/dist/hooks/useConversations.d.ts.map +1 -1
- package/dist/hooks/useResolveManualAction.d.ts +14 -0
- package/dist/hooks/useResolveManualAction.d.ts.map +1 -0
- package/dist/hooks/useResolveManualAction.js +47 -0
- package/dist/hooks/useValidateActionPlan.d.ts.map +1 -1
- package/dist/ldo-ui.d.ts +2 -1
- package/dist/ldo-ui.d.ts.map +1 -1
- package/dist/ldo-ui.js +2 -1
- package/dist/lib/build-config.d.ts +2 -0
- package/dist/lib/build-config.d.ts.map +1 -1
- package/dist/lib/build-config.js +2 -0
- package/dist/lib/chat-types.d.ts +1 -0
- package/dist/lib/chat-types.d.ts.map +1 -1
- package/dist/lib/datastore-route-table.d.ts +2 -0
- package/dist/lib/datastore-route-table.d.ts.map +1 -1
- package/dist/lib/datastore-route-table.js +3 -1
- package/dist/lib/datastore-routes.d.ts +7 -1
- package/dist/lib/datastore-routes.d.ts.map +1 -1
- package/dist/lib/datastore-routes.js +7 -2
- package/dist/lib/decode-chart-entities.d.ts.map +1 -1
- package/dist/lib/decode-chart-entities.js +5 -1
- package/dist/lib/manual-actions.d.ts +1 -0
- package/dist/lib/manual-actions.d.ts.map +1 -1
- package/dist/lib/manual-actions.js +4 -1
- package/dist/lib/structured-data/types.d.ts +7 -2
- package/dist/lib/structured-data/types.d.ts.map +1 -1
- package/dist/providers/ChatManagerProvider.d.ts.map +1 -1
- package/dist/providers/ChatManagerProvider.js +18 -16
- package/dist/providers/NavExtrasProvider.d.ts +23 -0
- package/dist/providers/NavExtrasProvider.d.ts.map +1 -0
- package/dist/providers/NavExtrasProvider.js +10 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/routes/explain/ExplainEmpty.d.ts.map +1 -1
- package/dist/routes/explain/ExplainEmpty.js +2 -1
- package/dist/routes/integrations/catalog-shape.d.ts +2 -1
- package/dist/routes/integrations/catalog-shape.d.ts.map +1 -1
- package/dist/routes/integrations/components/AddIntegrationModal.d.ts.map +1 -1
- package/dist/routes/integrations/components/AddIntegrationModal.js +1 -1
- package/dist/routes/integrations/components/MantaSyncPluginDownload.d.ts +13 -0
- package/dist/routes/integrations/components/MantaSyncPluginDownload.d.ts.map +1 -0
- package/dist/routes/integrations/components/MantaSyncPluginDownload.js +23 -0
- package/dist/routes/integrations/components/connect-forms/ConnectFormHeader.d.ts +1 -1
- package/dist/routes/integrations/components/connect-forms/ConnectFormHeader.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/ConnectWizardShell.d.ts +1 -1
- package/dist/routes/integrations/components/connect-forms/ConnectWizardShell.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/ConnectionStringInput.d.ts +11 -0
- package/dist/routes/integrations/components/connect-forms/ConnectionStringInput.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/ConnectionStringInput.js +49 -0
- package/dist/routes/integrations/components/connect-forms/DashConnectForm.d.ts +7 -0
- package/dist/routes/integrations/components/connect-forms/DashConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/DashConnectForm.js +87 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.d.ts +20 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionConnectForm.js +144 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionFieldInputs.d.ts +8 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionFieldInputs.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/DataConnectionFieldInputs.js +44 -0
- package/dist/routes/integrations/components/connect-forms/FigmaConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/FigmaConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/FigmaConnectForm.js +20 -0
- package/dist/routes/integrations/components/connect-forms/GmailConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/GmailConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/GmailConnectForm.js +11 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAdsConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAdsConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAdsConnectForm.js +37 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAnalyticsConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAnalyticsConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/GoogleAnalyticsConnectForm.js +37 -0
- package/dist/routes/integrations/components/connect-forms/ImportStartedNotice.d.ts +21 -0
- package/dist/routes/integrations/components/connect-forms/ImportStartedNotice.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/ImportStartedNotice.js +25 -0
- package/dist/routes/integrations/components/connect-forms/InferalCredentialConnectForm.d.ts +45 -0
- package/dist/routes/integrations/components/connect-forms/InferalCredentialConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/InferalCredentialConnectForm.js +187 -0
- package/dist/routes/integrations/components/connect-forms/KlaviyoConnectForm.d.ts +6 -0
- package/dist/routes/integrations/components/connect-forms/KlaviyoConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/KlaviyoConnectForm.js +68 -0
- package/dist/routes/integrations/components/connect-forms/LinkedinConnectForm.d.ts +10 -0
- package/dist/routes/integrations/components/connect-forms/LinkedinConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/LinkedinConnectForm.js +124 -0
- package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.d.ts +14 -0
- package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/MetaAdsConnectForm.js +183 -0
- package/dist/routes/integrations/components/connect-forms/OAuthConnectLink.d.ts +10 -0
- package/dist/routes/integrations/components/connect-forms/OAuthConnectLink.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/OAuthConnectLink.js +17 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts +45 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSetupConnectForm.js +136 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSingleAccountConnectForm.d.ts +23 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSingleAccountConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/OAuthSingleAccountConnectForm.js +63 -0
- package/dist/routes/integrations/components/connect-forms/ShopifyConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/ShopifyConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/ShopifyConnectForm.js +14 -0
- package/dist/routes/integrations/components/connect-forms/SlackConnectForm.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/SlackConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/SlackConnectForm.js +10 -0
- package/dist/routes/integrations/components/connect-forms/TriplewhaleConnectForm.d.ts +5 -0
- package/dist/routes/integrations/components/connect-forms/TriplewhaleConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/TriplewhaleConnectForm.js +35 -0
- package/dist/routes/integrations/components/connect-forms/WoocommerceConnectForm.d.ts +5 -0
- package/dist/routes/integrations/components/connect-forms/WoocommerceConnectForm.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/WoocommerceConnectForm.js +47 -0
- package/dist/routes/integrations/components/connect-forms/connection-string-fields.d.ts +13 -0
- package/dist/routes/integrations/components/connect-forms/connection-string-fields.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/connection-string-fields.js +110 -0
- package/dist/routes/integrations/components/connect-forms/dash-folder-tree.d.ts +6 -0
- package/dist/routes/integrations/components/connect-forms/dash-folder-tree.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/dash-folder-tree.js +14 -0
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts +35 -0
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/data-connection-fields.js +88 -0
- package/dist/routes/integrations/components/connect-forms/google-ads-account-tree.d.ts +11 -0
- package/dist/routes/integrations/components/connect-forms/google-ads-account-tree.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/google-ads-account-tree.js +36 -0
- package/dist/routes/integrations/components/connect-forms/google-analytics-property-tree.d.ts +6 -0
- package/dist/routes/integrations/components/connect-forms/google-analytics-property-tree.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/google-analytics-property-tree.js +34 -0
- package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.d.ts +12 -0
- package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/oauth-setup-phase.js +33 -0
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.d.ts +3 -0
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/setup-account-unavailable-reason.js +8 -0
- package/dist/routes/integrations/components/connect-forms/types.d.ts +5 -1
- package/dist/routes/integrations/components/connect-forms/types.d.ts.map +1 -1
- package/dist/routes/integrations/components/connect-forms/useConnectWizard.d.ts +19 -0
- package/dist/routes/integrations/components/connect-forms/useConnectWizard.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/useConnectWizard.js +28 -0
- package/dist/routes/integrations/components/connect-forms/useConnectedExternalIds.d.ts +4 -0
- package/dist/routes/integrations/components/connect-forms/useConnectedExternalIds.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/useConnectedExternalIds.js +17 -0
- package/dist/routes/integrations/components/connect-forms/useWindowFocusPoll.d.ts +7 -0
- package/dist/routes/integrations/components/connect-forms/useWindowFocusPoll.d.ts.map +1 -0
- package/dist/routes/integrations/components/connect-forms/useWindowFocusPoll.js +18 -0
- package/dist/routes/integrations/connect-forms.d.ts +16 -0
- package/dist/routes/integrations/connect-forms.d.ts.map +1 -0
- package/dist/routes/integrations/connect-forms.js +14 -0
- package/dist/routes/integrations/filter-connected-providers.d.ts +3 -0
- package/dist/routes/integrations/filter-connected-providers.d.ts.map +1 -0
- package/dist/routes/integrations/filter-connected-providers.js +15 -0
- package/dist/routes/integrations/overview.d.ts +3 -0
- package/dist/routes/integrations/overview.d.ts.map +1 -0
- package/dist/routes/integrations/overview.js +2 -0
- package/dist/routes/integrations/use-connected-account-entities.d.ts +10 -0
- package/dist/routes/integrations/use-connected-account-entities.d.ts.map +1 -0
- package/dist/routes/integrations/use-connected-account-entities.js +29 -0
- package/dist/routes/memories/components/EmptyStates.d.ts.map +1 -1
- package/dist/routes/memories/components/EmptyStates.js +2 -1
- package/dist/routes/plans/components/actionPlansTableConfig.d.ts +12 -12
- package/package.json +47 -8
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { withDatastoreQueryParam } from "@manta-eu/client/shared/datastore-path";
|
|
3
|
+
import { ConnectWizardShell } from "./ConnectWizardShell.js";
|
|
4
|
+
import { useMantaClient } from "#providers/index";
|
|
5
|
+
export function GmailConnectForm(props) {
|
|
6
|
+
const { onBack, provider } = props;
|
|
7
|
+
const { datastoreId } = useMantaClient();
|
|
8
|
+
return (_jsx(ConnectWizardShell, { provider: provider, totalSteps: 1, currentStep: 1, stepTitle: "Connect your Gmail account", stepDescription: "You'll be redirected to Google to grant access, then brought back here.", onBack: onBack, onContinue: () => {
|
|
9
|
+
window.location.href = withDatastoreQueryParam("/oauth/gmail/start", datastoreId);
|
|
10
|
+
}, continueLabel: "Continue with Google", children: _jsx("p", { className: "typography-caption text-muted-foreground", children: "Manta requests read-only Gmail access (gmail.readonly) to ingest inbound email into your knowledge graph. You can revoke it any time from your Google account." }) }));
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleAdsConnectForm.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/GoogleAdsConnectForm.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAqChD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,2CAa3D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { GQLIntegration, useGoogleSetupConnectMutation, useGoogleSetupProgressQuery, } from "@manta-eu/client";
|
|
3
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { googleAdsTreeItems } from "./google-ads-account-tree.js";
|
|
5
|
+
import { OAuthSetupConnectForm } from "./OAuthSetupConnectForm.js";
|
|
6
|
+
import { useMantaClient } from "#providers/index";
|
|
7
|
+
const copy = {
|
|
8
|
+
providerName: "Google Ads",
|
|
9
|
+
identityProviderName: "Google",
|
|
10
|
+
entitySingular: "account",
|
|
11
|
+
entityPlural: "accounts",
|
|
12
|
+
signInDescription: "We'll take you to Google sign-in so you can grant Manta access to your Google Ads accounts.",
|
|
13
|
+
pickTitle: "Pick the Google Ads accounts to connect",
|
|
14
|
+
pickDescription: "Choose the accounts whose campaigns Manta should manage. Manager accounts hold no campaigns, so only their client accounts can be connected.",
|
|
15
|
+
noEntitiesLabel: "We couldn't find any Google Ads accounts on this Google identity. Sign in with a different account or check your Google Ads access.",
|
|
16
|
+
noneConnectableLabel: "No connectable Google Ads accounts remain on this Google identity — they are already connected, managers, or unavailable. Sign in with a different Google account to connect a new one.",
|
|
17
|
+
};
|
|
18
|
+
function useGoogleAdsSetup() {
|
|
19
|
+
const { mantaClient } = useMantaClient();
|
|
20
|
+
const queryClient = useQueryClient();
|
|
21
|
+
const progressQuery = useGoogleSetupProgressQuery(mantaClient, undefined, { staleTime: 0 });
|
|
22
|
+
const connectMutation = useGoogleSetupConnectMutation(mantaClient);
|
|
23
|
+
const progress = progressQuery.data?.googleSetupProgress;
|
|
24
|
+
return {
|
|
25
|
+
progress: progress && { state: progress.state, rows: progress.accounts, lastError: progress.lastError },
|
|
26
|
+
refetchProgress: progressQuery.refetch,
|
|
27
|
+
resetProgress: () => queryClient.setQueryData(useGoogleSetupProgressQuery.getKey(), {
|
|
28
|
+
googleSetupProgress: { state: [], accounts: null, lastError: null },
|
|
29
|
+
}),
|
|
30
|
+
connect: (accountIds) => connectMutation.mutateAsync({ accountIds }),
|
|
31
|
+
isConnecting: connectMutation.isPending,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function GoogleAdsConnectForm(props) {
|
|
35
|
+
const setup = useGoogleAdsSetup();
|
|
36
|
+
return (_jsx(OAuthSetupConnectForm, { ...props, integration: GQLIntegration.Google, oauthStartPath: "/oauth/google-ads/start", copy: copy, setup: setup, treeItems: googleAdsTreeItems }));
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GoogleAnalyticsConnectForm.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/GoogleAnalyticsConnectForm.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAsChD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,gBAAgB,2CAajE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { GQLIntegration, useGoogleAnalyticsSetupConnectMutation, useGoogleAnalyticsSetupProgressQuery, } from "@manta-eu/client";
|
|
3
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { googleAnalyticsTreeItems } from "./google-analytics-property-tree.js";
|
|
5
|
+
import { OAuthSetupConnectForm } from "./OAuthSetupConnectForm.js";
|
|
6
|
+
import { useMantaClient } from "#providers/index";
|
|
7
|
+
const copy = {
|
|
8
|
+
providerName: "Google Analytics",
|
|
9
|
+
identityProviderName: "Google",
|
|
10
|
+
entitySingular: "property",
|
|
11
|
+
entityPlural: "properties",
|
|
12
|
+
signInDescription: "We'll take you to Google sign-in so you can grant Manta access to your Google Analytics properties.",
|
|
13
|
+
pickTitle: "Pick the GA4 properties to connect",
|
|
14
|
+
pickDescription: "Choose the properties whose reports Manta should pull. Accounts only group their properties, so only properties can be connected.",
|
|
15
|
+
noEntitiesLabel: "We couldn't find any Google Analytics properties on this Google identity. Sign in with a different account or check your Google Analytics access.",
|
|
16
|
+
noneConnectableLabel: "No connectable Google Analytics properties remain on this Google identity — they are already connected or unavailable. Sign in with a different Google account to connect a new one.",
|
|
17
|
+
};
|
|
18
|
+
function useGoogleAnalyticsSetup() {
|
|
19
|
+
const { mantaClient } = useMantaClient();
|
|
20
|
+
const queryClient = useQueryClient();
|
|
21
|
+
const progressQuery = useGoogleAnalyticsSetupProgressQuery(mantaClient, undefined, { staleTime: 0 });
|
|
22
|
+
const connectMutation = useGoogleAnalyticsSetupConnectMutation(mantaClient);
|
|
23
|
+
const progress = progressQuery.data?.googleAnalyticsSetupProgress;
|
|
24
|
+
return {
|
|
25
|
+
progress: progress && { state: progress.state, rows: progress.properties, lastError: progress.lastError },
|
|
26
|
+
refetchProgress: progressQuery.refetch,
|
|
27
|
+
resetProgress: () => queryClient.setQueryData(useGoogleAnalyticsSetupProgressQuery.getKey(), {
|
|
28
|
+
googleAnalyticsSetupProgress: { state: [], properties: null, lastError: null },
|
|
29
|
+
}),
|
|
30
|
+
connect: (propertyIds) => connectMutation.mutateAsync({ propertyIds }),
|
|
31
|
+
isConnecting: connectMutation.isPending,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function GoogleAnalyticsConnectForm(props) {
|
|
35
|
+
const setup = useGoogleAnalyticsSetup();
|
|
36
|
+
return (_jsx(OAuthSetupConnectForm, { ...props, integration: GQLIntegration.GoogleAnalytics, oauthStartPath: "/oauth/google-analytics/start", copy: copy, setup: setup, treeItems: googleAnalyticsTreeItems }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ProviderPresentation } from "../../types.js";
|
|
2
|
+
import type { ConnectSuccessStep } from "./OAuthSingleAccountConnectForm.js";
|
|
3
|
+
import type { ImportNotice } from "./types.js";
|
|
4
|
+
export declare function importStartedTitle(providerName: string): string;
|
|
5
|
+
export declare function importStartedSuccessStep(providerName: string, notice: ImportNotice): ConnectSuccessStep;
|
|
6
|
+
type ImportStartedNoticeProps = {
|
|
7
|
+
providerName: string;
|
|
8
|
+
notice: ImportNotice;
|
|
9
|
+
};
|
|
10
|
+
export declare function ImportStartedNotice(props: ImportStartedNoticeProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
type ImportStartedStepProps = {
|
|
12
|
+
provider: ProviderPresentation<string>;
|
|
13
|
+
notice: ImportNotice;
|
|
14
|
+
totalSteps: number;
|
|
15
|
+
onDone: () => void;
|
|
16
|
+
status?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
};
|
|
19
|
+
export declare function ImportStartedStep(props: ImportStartedStepProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=ImportStartedNotice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImportStartedNotice.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/ImportStartedNotice.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAM5C,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAE/D;AAMD,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,kBAAkB,CAMvG;AAED,KAAK,wBAAwB,GAAG;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,2CAYlE;AAED,KAAK,sBAAsB,GAAG;IAC5B,QAAQ,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,2CA2B9D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ConnectWizardShell } from "./ConnectWizardShell.js";
|
|
3
|
+
import { Button } from "#components/button/index";
|
|
4
|
+
const importTimingNote = "The first import can take a while, depending on how much data there is. You can close this dialog and keep using Manta. Your data will appear once the import finishes.";
|
|
5
|
+
export function importStartedTitle(providerName) {
|
|
6
|
+
return `Importing your ${providerName} data`;
|
|
7
|
+
}
|
|
8
|
+
function ImportTimingNote() {
|
|
9
|
+
return _jsx("p", { className: "typography-caption text-muted-foreground", children: importTimingNote });
|
|
10
|
+
}
|
|
11
|
+
export function importStartedSuccessStep(providerName, notice) {
|
|
12
|
+
return {
|
|
13
|
+
title: importStartedTitle(providerName),
|
|
14
|
+
description: notice.description,
|
|
15
|
+
content: _jsx(ImportTimingNote, {}),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function ImportStartedNotice(props) {
|
|
19
|
+
const { providerName, notice } = props;
|
|
20
|
+
return (_jsxs("div", { className: "grid gap-4", children: [_jsxs("div", { className: "grid gap-1", children: [_jsx("h2", { className: "typography-large-body-semibold text-default-foreground", children: importStartedTitle(providerName) }), _jsx("p", { className: "typography-caption text-muted-foreground", children: notice.description })] }), _jsx(ImportTimingNote, {})] }));
|
|
21
|
+
}
|
|
22
|
+
export function ImportStartedStep(props) {
|
|
23
|
+
const { provider, notice, totalSteps, onDone, status = "Connected", title = importStartedTitle(provider.name), } = props;
|
|
24
|
+
return (_jsx(ConnectWizardShell, { provider: provider, status: status, totalSteps: totalSteps, currentStep: totalSteps, stepTitle: title, stepDescription: notice.description, primaryAction: _jsx(Button, { variant: "secondary", onClick: onDone, children: "Done" }), children: _jsx(ImportTimingNote, {}) }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { GQLInferalLocation, type GQLInferalProviderInput } from "@manta-eu/client";
|
|
2
|
+
import type { ConnectFormProps, ProviderSetupInstructions, StepConfig } from "./types.js";
|
|
3
|
+
export type CredentialField<Key extends string = string> = {
|
|
4
|
+
key: Key;
|
|
5
|
+
label: string;
|
|
6
|
+
placeholder: string;
|
|
7
|
+
requiredMessage: string;
|
|
8
|
+
secret?: boolean;
|
|
9
|
+
validate?: {
|
|
10
|
+
check: (value: string) => boolean;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export type CredentialStep<Key extends string = string> = {
|
|
15
|
+
title: string;
|
|
16
|
+
description: string;
|
|
17
|
+
fields: readonly CredentialField<Key>[];
|
|
18
|
+
};
|
|
19
|
+
export type InferalCredentialFormConfig<Key extends string = string> = {
|
|
20
|
+
providerName: string;
|
|
21
|
+
fieldIdPrefix: string;
|
|
22
|
+
identifier: CredentialField<Key> & {
|
|
23
|
+
stepTitle: string;
|
|
24
|
+
stepDescription: string;
|
|
25
|
+
};
|
|
26
|
+
secrets: CredentialStep<Key>;
|
|
27
|
+
settings?: CredentialStep<Key>;
|
|
28
|
+
streamNamePlaceholder: string;
|
|
29
|
+
locationStepDescription: string;
|
|
30
|
+
buildProviderInput: (credentials: Record<Key, string>) => GQLInferalProviderInput;
|
|
31
|
+
};
|
|
32
|
+
export declare function fieldId(prefix: string, key: string): string;
|
|
33
|
+
type Values = {
|
|
34
|
+
streamName: string;
|
|
35
|
+
location: GQLInferalLocation;
|
|
36
|
+
[credential: string]: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function shouldAutofillStreamName(changedField: string | undefined, identifierKey: string, streamNameDirty: boolean): boolean;
|
|
39
|
+
export declare function createSteps<Key extends string>(config: InferalCredentialFormConfig<Key>, locationLabels: Readonly<Record<GQLInferalLocation, string>>, setup?: ProviderSetupInstructions): StepConfig<Values>[];
|
|
40
|
+
export type InferalCredentialConnectFormProps<Key extends string> = ConnectFormProps & {
|
|
41
|
+
config: InferalCredentialFormConfig<Key>;
|
|
42
|
+
};
|
|
43
|
+
export declare function InferalCredentialConnectForm<Key extends string>(props: InferalCredentialConnectFormProps<Key>): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=InferalCredentialConnectForm.d.ts.map
|
package/dist/routes/integrations/components/connect-forms/InferalCredentialConnectForm.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InferalCredentialConnectForm.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/InferalCredentialConnectForm.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAElB,KAAK,uBAAuB,EAC7B,MAAM,kBAAkB,CAAC;AAU1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMvF,MAAM,MAAM,eAAe,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI;IACzD,GAAG,EAAE,GAAG,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,cAAc,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,SAAS,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI;IACrE,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;IAClF,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAE7B,QAAQ,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC/B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,kBAAkB,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,uBAAuB,CAAC;CACnF,CAAC;AAKF,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D;AAcD,KAAK,MAAM,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAC;IAAC,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AASjG,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,OAAO,GACvB,OAAO,CAET;AA6DD,wBAAgB,WAAW,CAAC,GAAG,SAAS,MAAM,EAC5C,MAAM,EAAE,2BAA2B,CAAC,GAAG,CAAC,EACxC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,EAC5D,KAAK,CAAC,EAAE,yBAAyB,GAChC,UAAU,CAAC,MAAM,CAAC,EAAE,CAqFtB;AAED,MAAM,MAAM,iCAAiC,CAAC,GAAG,SAAS,MAAM,IAAI,gBAAgB,GAAG;IACrF,MAAM,EAAE,2BAA2B,CAAC,GAAG,CAAC,CAAC;CAC1C,CAAC;AAEF,wBAAgB,4BAA4B,CAAC,GAAG,SAAS,MAAM,EAAE,KAAK,EAAE,iCAAiC,CAAC,GAAG,CAAC,2CAmG7G"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
3
|
+
import { GQLInferalLocation, useCreateInferalIntegrationMutation, } from "@manta-eu/client";
|
|
4
|
+
import { parseError } from "@manta-eu/client/shared/error-utils";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { Fragment, useEffect, useMemo, useState } from "react";
|
|
7
|
+
import { Controller, useForm } from "react-hook-form";
|
|
8
|
+
import { toast } from "sonner";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { ConnectWizardShell } from "./ConnectWizardShell.js";
|
|
11
|
+
import { FieldInstructions } from "./FieldInstructions.js";
|
|
12
|
+
import { ImportStartedStep } from "./ImportStartedNotice.js";
|
|
13
|
+
import { useConnectWizard } from "./useConnectWizard.js";
|
|
14
|
+
import { Input } from "#components/input/index";
|
|
15
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "#components/select/index";
|
|
16
|
+
import { useMantaClient } from "#providers/index";
|
|
17
|
+
const locationSchema = z.enum(GQLInferalLocation, { message: "Region is required" });
|
|
18
|
+
// Kebab-casing the field key keeps the rendered input ids identical to the hand-written forms.
|
|
19
|
+
export function fieldId(prefix, key) {
|
|
20
|
+
return `${prefix}-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`;
|
|
21
|
+
}
|
|
22
|
+
const streamNameSchema = z
|
|
23
|
+
.string()
|
|
24
|
+
.trim()
|
|
25
|
+
.min(1, "Stream name is required")
|
|
26
|
+
.max(64, "64 characters or fewer")
|
|
27
|
+
.regex(/^[a-zA-Z0-9_:-]+$/, "Use letters, numbers, dashes (-), underscores (_), or colons (:). No spaces.");
|
|
28
|
+
function blankValues(fields) {
|
|
29
|
+
return { ...Object.fromEntries(fields.map((field) => [field.key, ""])), streamName: "" };
|
|
30
|
+
}
|
|
31
|
+
// The stream name tracks the identifier only until the user types their own.
|
|
32
|
+
export function shouldAutofillStreamName(changedField, identifierKey, streamNameDirty) {
|
|
33
|
+
return changedField === identifierKey && !streamNameDirty;
|
|
34
|
+
}
|
|
35
|
+
function credentialSchema(fields) {
|
|
36
|
+
return z.object(Object.fromEntries(fields.map((f) => [f.key, credentialFieldSchema(f)])));
|
|
37
|
+
}
|
|
38
|
+
function credentialFieldSchema(field) {
|
|
39
|
+
const required = z.string().trim().min(1, field.requiredMessage);
|
|
40
|
+
const { validate } = field;
|
|
41
|
+
return validate ? required.refine(validate.check, validate.message) : required;
|
|
42
|
+
}
|
|
43
|
+
// The credential fields are only known at runtime, so the built schema carries no usable output
|
|
44
|
+
// type; naming it keeps `useForm` inferring the same Values the hand-written forms declared.
|
|
45
|
+
// A schema built from runtime descriptors has no usable static output type, so this one assertion
|
|
46
|
+
// stands in for it; every other type in the file is inferred from the caller's key union.
|
|
47
|
+
function formSchema(credentialFields) {
|
|
48
|
+
return credentialSchema(credentialFields).extend({
|
|
49
|
+
streamName: streamNameSchema,
|
|
50
|
+
location: locationSchema,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Object.fromEntries cannot carry the key union through, so the completeness check below is what
|
|
54
|
+
// makes the assertion true rather than merely asserted.
|
|
55
|
+
function pickCredentials(values, fields) {
|
|
56
|
+
const missing = fields.filter((field) => !isPresent(values[field.key])).map((field) => field.key);
|
|
57
|
+
if (isPresent(missing)) {
|
|
58
|
+
throw new Error(`Missing credential ${missing.join(", ")} — the form declared it but collected no value`);
|
|
59
|
+
}
|
|
60
|
+
return Object.fromEntries(fields.map((field) => [field.key, values[field.key]]));
|
|
61
|
+
}
|
|
62
|
+
function renderField(form, prefix, field) {
|
|
63
|
+
return (_jsx(Input, { id: fieldId(prefix, field.key), label: field.label, type: field.secret ? "password" : undefined, placeholder: field.placeholder, autoComplete: "off", error: form.formState.errors[field.key]?.message?.toString(), ...form.register(field.key) }, field.key));
|
|
64
|
+
}
|
|
65
|
+
function renderFields(form, prefix, fields) {
|
|
66
|
+
const [only] = fields;
|
|
67
|
+
if (fields.length === 1 && only)
|
|
68
|
+
return renderField(form, prefix, only);
|
|
69
|
+
return _jsx("div", { className: "grid gap-3", children: fields.map((field) => renderField(form, prefix, field)) });
|
|
70
|
+
}
|
|
71
|
+
export function createSteps(config, locationLabels, setup) {
|
|
72
|
+
const { fieldIdPrefix, identifier, secrets, settings } = config;
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
id: "identifier",
|
|
76
|
+
title: identifier.stepTitle,
|
|
77
|
+
description: identifier.stepDescription,
|
|
78
|
+
schema: credentialSchema([identifier]),
|
|
79
|
+
supporting: setup?.identifier ? _jsx(FieldInstructions, { instruction: setup.identifier }) : undefined,
|
|
80
|
+
continueLabel: "Continue",
|
|
81
|
+
render: (form) => renderField(form, fieldIdPrefix, identifier),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "secrets",
|
|
85
|
+
title: secrets.title,
|
|
86
|
+
description: secrets.description,
|
|
87
|
+
schema: credentialSchema(secrets.fields),
|
|
88
|
+
supporting: setup?.apiKey ? _jsx(FieldInstructions, { instruction: setup.apiKey }) : undefined,
|
|
89
|
+
continueLabel: "Continue",
|
|
90
|
+
render: (form) => renderFields(form, fieldIdPrefix, secrets.fields),
|
|
91
|
+
},
|
|
92
|
+
...(settings
|
|
93
|
+
? [
|
|
94
|
+
{
|
|
95
|
+
id: "settings",
|
|
96
|
+
title: settings.title,
|
|
97
|
+
description: settings.description,
|
|
98
|
+
schema: credentialSchema(settings.fields),
|
|
99
|
+
supporting: setup?.settings ? _jsx(FieldInstructions, { instruction: setup.settings }) : undefined,
|
|
100
|
+
continueLabel: "Continue",
|
|
101
|
+
render: (form) => renderFields(form, fieldIdPrefix, settings.fields),
|
|
102
|
+
},
|
|
103
|
+
]
|
|
104
|
+
: []),
|
|
105
|
+
{
|
|
106
|
+
id: "stream-name",
|
|
107
|
+
title: "Name this integration",
|
|
108
|
+
description: "Used to identify this connection. Letters, numbers, dashes, and underscores.",
|
|
109
|
+
schema: z.object({ streamName: streamNameSchema }),
|
|
110
|
+
continueLabel: "Continue",
|
|
111
|
+
render: (form) => (_jsx(Input, { id: fieldId(fieldIdPrefix, "streamName"), label: "Stream name", placeholder: config.streamNamePlaceholder, autoComplete: "off", error: form.formState.errors.streamName?.message?.toString(), ...form.register("streamName") })),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: "location",
|
|
115
|
+
title: "Choose a stream location",
|
|
116
|
+
description: config.locationStepDescription,
|
|
117
|
+
schema: z.object({ location: locationSchema }),
|
|
118
|
+
continueLabel: "Connect",
|
|
119
|
+
render: (form) => (_jsx(Controller, { control: form.control, name: "location", render: ({ field, fieldState }) => (_jsxs(Select, { value: field.value ?? "", onValueChange: field.onChange, children: [_jsx(SelectTrigger, { label: "Location", id: fieldId(fieldIdPrefix, "location"), className: "w-full h-10", error: fieldState.error?.message, children: _jsx(SelectValue, { placeholder: "Select a region" }) }), _jsx(SelectContent, { children: Object.entries(locationLabels).map(([value, label]) => (_jsx(SelectItem, { value: value, children: label }, value))) })] })) })),
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
export function InferalCredentialConnectForm(props) {
|
|
124
|
+
const { onBack, onChanged, onDismiss, provider, inferalConfig, config, importNotice } = props;
|
|
125
|
+
if (!inferalConfig) {
|
|
126
|
+
throw new Error(`${config.providerName} is missing its Inferal connection configuration`);
|
|
127
|
+
}
|
|
128
|
+
const { deriveStreamName, locationLabels, setupInstructions } = inferalConfig;
|
|
129
|
+
const { providerName, identifier, secrets, settings, buildProviderInput } = config;
|
|
130
|
+
const credentialFields = useMemo(() => [identifier, ...secrets.fields, ...(settings?.fields ?? [])], [identifier, secrets, settings]);
|
|
131
|
+
const steps = useMemo(() => createSteps(config, locationLabels, setupInstructions), [config, locationLabels, setupInstructions]);
|
|
132
|
+
const fullSchema = useMemo(() => formSchema(credentialFields), [credentialFields]);
|
|
133
|
+
const { mantaClient } = useMantaClient();
|
|
134
|
+
const createMutation = useCreateInferalIntegrationMutation(mantaClient);
|
|
135
|
+
const [connected, setConnected] = useState(false);
|
|
136
|
+
const form = useForm({
|
|
137
|
+
resolver: zodResolver(fullSchema),
|
|
138
|
+
defaultValues: blankValues(credentialFields),
|
|
139
|
+
mode: "onChange",
|
|
140
|
+
});
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
const subscription = form.watch((values, { name }) => {
|
|
143
|
+
if (!shouldAutofillStreamName(name, identifier.key, Boolean(form.formState.dirtyFields.streamName)))
|
|
144
|
+
return;
|
|
145
|
+
form.setValue("streamName", deriveStreamName(values[identifier.key] ?? ""));
|
|
146
|
+
});
|
|
147
|
+
return () => subscription.unsubscribe();
|
|
148
|
+
}, [deriveStreamName, form, identifier.key]);
|
|
149
|
+
async function onValid(values) {
|
|
150
|
+
const credentials = pickCredentials(values, credentialFields);
|
|
151
|
+
try {
|
|
152
|
+
const response = await createMutation.mutateAsync({
|
|
153
|
+
input: {
|
|
154
|
+
provider: buildProviderInput(credentials),
|
|
155
|
+
stream: { name: values.streamName, location: values.location },
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
const result = response.createInferalIntegration;
|
|
159
|
+
onChanged();
|
|
160
|
+
if (isPresent(result.error)) {
|
|
161
|
+
toast.error(`Failed to connect ${providerName}`, { description: result.error });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
toast.success(`${providerName} connected`, { description: values.streamName });
|
|
165
|
+
if (importNotice)
|
|
166
|
+
setConnected(true);
|
|
167
|
+
else
|
|
168
|
+
onDismiss();
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
onChanged();
|
|
172
|
+
const info = parseError(err, `Failed to connect ${providerName}`);
|
|
173
|
+
toast.error(info.message, { description: info.description });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const { stepIndex, currentStep, canAdvance, handleContinue, handleBack } = useConnectWizard({
|
|
177
|
+
steps,
|
|
178
|
+
form,
|
|
179
|
+
fullSchema,
|
|
180
|
+
onSubmit: onValid,
|
|
181
|
+
onBack,
|
|
182
|
+
});
|
|
183
|
+
if (connected && importNotice) {
|
|
184
|
+
return (_jsx(ImportStartedStep, { provider: provider, notice: importNotice, totalSteps: steps.length + 1, onDone: onDismiss }));
|
|
185
|
+
}
|
|
186
|
+
return (_jsx(ConnectWizardShell, { provider: provider, totalSteps: importNotice ? steps.length + 1 : steps.length, currentStep: stepIndex + 1, stepTitle: currentStep.title, stepDescription: currentStep.description, supportingInfo: currentStep.supporting, onBack: handleBack, onContinue: handleContinue, continueLabel: currentStep.continueLabel, continueDisabled: !canAdvance, isSubmitting: createMutation.isPending, children: _jsx(Fragment, { children: currentStep.render(form) }, currentStep.id) }));
|
|
187
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type InferalCredentialFormConfig } from "./InferalCredentialConnectForm.js";
|
|
2
|
+
import type { ConnectFormProps } from "./types.js";
|
|
3
|
+
export declare function extractKlaviyoMetricId(value: string): string;
|
|
4
|
+
export declare const klaviyoConfig: InferalCredentialFormConfig<"companyId" | "apiKey" | "conversionMetricId">;
|
|
5
|
+
export declare function KlaviyoConnectForm(props: ConnectFormProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
//# sourceMappingURL=KlaviyoConnectForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KlaviyoConnectForm.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/KlaviyoConnectForm.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAChH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAIhD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK5D;AASD,eAAO,MAAM,aAAa,EAAE,2BAA2B,CAAC,WAAW,GAAG,QAAQ,GAAG,oBAAoB,CAkDpG,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,gBAAgB,2CAEzD"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { InferalCredentialConnectForm } from "./InferalCredentialConnectForm.js";
|
|
3
|
+
// The setup steps send the user to klaviyo.com/metric/<ID>/<name>, so the whole URL is the likeliest
|
|
4
|
+
// paste. Keep the ID and drop the rest rather than handing the relay something it cannot use.
|
|
5
|
+
export function extractKlaviyoMetricId(value) {
|
|
6
|
+
const trimmed = value.trim();
|
|
7
|
+
const fromUrl = /\/metric\/([^/?#]+)/.exec(trimmed);
|
|
8
|
+
return fromUrl?.[1] ?? trimmed;
|
|
9
|
+
}
|
|
10
|
+
// Klaviyo publishes no format for a metric ID — its own schema types one as an unconstrained string —
|
|
11
|
+
// so this rejects only what an ID certainly is not: a leftover path, a query string, or a stray word.
|
|
12
|
+
function looksLikeMetricId(value) {
|
|
13
|
+
return /^[A-Za-z0-9_-]+$/.test(extractKlaviyoMetricId(value));
|
|
14
|
+
}
|
|
15
|
+
// companyId identifies the integration on our side only — Klaviyo's relay contract never receives it.
|
|
16
|
+
export const klaviyoConfig = {
|
|
17
|
+
providerName: "Klaviyo",
|
|
18
|
+
fieldIdPrefix: "klaviyo",
|
|
19
|
+
identifier: {
|
|
20
|
+
key: "companyId",
|
|
21
|
+
label: "Company ID",
|
|
22
|
+
placeholder: "Your Klaviyo company ID",
|
|
23
|
+
requiredMessage: "Company ID is required",
|
|
24
|
+
stepTitle: "Find your Klaviyo company ID",
|
|
25
|
+
stepDescription: "We use this to name the stream, so it is unique to this Klaviyo account.",
|
|
26
|
+
},
|
|
27
|
+
secrets: {
|
|
28
|
+
title: "Create a private API key",
|
|
29
|
+
description: "Manta needs read access to campaigns, flows, profiles, lists, and segments.",
|
|
30
|
+
fields: [
|
|
31
|
+
{
|
|
32
|
+
key: "apiKey",
|
|
33
|
+
label: "Private API key",
|
|
34
|
+
placeholder: "pk_...",
|
|
35
|
+
requiredMessage: "Private API key is required",
|
|
36
|
+
secret: true,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
settings: {
|
|
41
|
+
title: "Choose your conversion metric",
|
|
42
|
+
description: "Klaviyo attributes campaign and flow revenue to one metric of your choosing, and picks none by default.",
|
|
43
|
+
fields: [
|
|
44
|
+
{
|
|
45
|
+
key: "conversionMetricId",
|
|
46
|
+
label: "Conversion metric ID",
|
|
47
|
+
placeholder: "RESQ6t",
|
|
48
|
+
requiredMessage: "Conversion metric ID is required",
|
|
49
|
+
validate: {
|
|
50
|
+
check: looksLikeMetricId,
|
|
51
|
+
message: "Paste the metric ID, or the whole klaviyo.com/metric/... URL",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
streamNamePlaceholder: "klaviyo-abc123",
|
|
57
|
+
locationStepDescription: "Pick the region closest to your account for the lowest latency.",
|
|
58
|
+
buildProviderInput: (credentials) => ({
|
|
59
|
+
klaviyo: {
|
|
60
|
+
companyId: credentials.companyId,
|
|
61
|
+
apiKey: credentials.apiKey,
|
|
62
|
+
conversionMetricId: extractKlaviyoMetricId(credentials.conversionMetricId),
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
};
|
|
66
|
+
export function KlaviyoConnectForm(props) {
|
|
67
|
+
return _jsx(InferalCredentialConnectForm, { ...props, config: klaviyoConfig });
|
|
68
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConnectFormProps } from "./types.js";
|
|
2
|
+
type LinkedinPhase = "intro" | "waiting" | "selecting" | "finished" | "failed";
|
|
3
|
+
type DeriveLinkedinPhaseInput = {
|
|
4
|
+
states?: string[];
|
|
5
|
+
hasSeenActiveState: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function deriveLinkedinPhase({ states, hasSeenActiveState }: DeriveLinkedinPhaseInput): LinkedinPhase;
|
|
8
|
+
export declare function LinkedinConnectForm(props: ConnectFormProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=LinkedinConnectForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LinkedinConnectForm.d.ts","sourceRoot":"","sources":["../../../../../src/routes/integrations/components/connect-forms/LinkedinConnectForm.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAchD,KAAK,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE/E,KAAK,wBAAwB,GAAG;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CAAC;AAOF,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,EAAE,wBAAwB,GAAG,aAAa,CAe3G;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,gBAAgB,2CA4K1D"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLinkedinSetupConnectMutation, useLinkedinSetupProgressQuery } from "@manta-eu/client";
|
|
3
|
+
import { parseError } from "@manta-eu/client/shared/error-utils";
|
|
4
|
+
import { humanCount } from "@manta-eu/lib/humanize";
|
|
5
|
+
import { isPresent } from "@manta-eu/lib/present";
|
|
6
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
7
|
+
import { ArrowLeft } from "lucide-react";
|
|
8
|
+
import { useEffect, useRef, useState } from "react";
|
|
9
|
+
import { toast } from "sonner";
|
|
10
|
+
import { match } from "ts-pattern";
|
|
11
|
+
import { ConnectFormHeader } from "./ConnectFormHeader.js";
|
|
12
|
+
import { ImportStartedNotice } from "./ImportStartedNotice.js";
|
|
13
|
+
import { OAuthConnectLink } from "./OAuthConnectLink.js";
|
|
14
|
+
import { useWindowFocusPoll } from "./useWindowFocusPoll.js";
|
|
15
|
+
import { Banner } from "#components/banner/index";
|
|
16
|
+
import { Button } from "#components/button/index";
|
|
17
|
+
import { Checkbox } from "#components/checkbox/index";
|
|
18
|
+
import { Loader } from "#components/loader/index";
|
|
19
|
+
import { useMantaClient } from "#providers/index";
|
|
20
|
+
const selectingState = "SELECTING_ACCOUNTS";
|
|
21
|
+
const finishedState = "FINISHED";
|
|
22
|
+
const notFoundState = "NOT_FOUND";
|
|
23
|
+
const failedStates = new Set(["FATALLY_FAILED", "TIMED_OUT"]);
|
|
24
|
+
const pollIntervalMs = 4000;
|
|
25
|
+
function isActiveState(states) {
|
|
26
|
+
return (states ?? []).some((state) => state !== finishedState && state !== notFoundState && !failedStates.has(state));
|
|
27
|
+
}
|
|
28
|
+
// hasSeenActiveState blocks acting on leftover terminal progress from a past session.
|
|
29
|
+
export function deriveLinkedinPhase({ states, hasSeenActiveState }) {
|
|
30
|
+
const set = new Set(states ?? []);
|
|
31
|
+
return match({
|
|
32
|
+
selecting: set.has(selectingState),
|
|
33
|
+
finished: hasSeenActiveState && set.has(finishedState),
|
|
34
|
+
failed: hasSeenActiveState && [...failedStates].some((value) => set.has(value)),
|
|
35
|
+
active: isActiveState(states),
|
|
36
|
+
})
|
|
37
|
+
.returnType()
|
|
38
|
+
.with({ selecting: true }, () => "selecting")
|
|
39
|
+
.with({ finished: true }, () => "finished")
|
|
40
|
+
.with({ failed: true }, () => "failed")
|
|
41
|
+
.with({ active: true }, () => "waiting")
|
|
42
|
+
.otherwise(() => "intro");
|
|
43
|
+
}
|
|
44
|
+
export function LinkedinConnectForm(props) {
|
|
45
|
+
const { onBack, onChanged, onDismiss, provider, resumedFromOAuth = false, importNotice } = props;
|
|
46
|
+
const { mantaClient } = useMantaClient();
|
|
47
|
+
const queryClient = useQueryClient();
|
|
48
|
+
const [hasSeenActiveState, setHasSeenActiveState] = useState(resumedFromOAuth);
|
|
49
|
+
const [selectedIds, setSelectedIds] = useState(new Set());
|
|
50
|
+
const hasReportedConnected = useRef(false);
|
|
51
|
+
const connect = useLinkedinSetupConnectMutation(mantaClient);
|
|
52
|
+
const progressQuery = useLinkedinSetupProgressQuery(mantaClient, undefined, { staleTime: 0 });
|
|
53
|
+
const progress = progressQuery.data?.linkedinSetupProgress;
|
|
54
|
+
const accounts = progress?.accounts ?? [];
|
|
55
|
+
const phase = deriveLinkedinPhase({ states: progress?.state, hasSeenActiveState });
|
|
56
|
+
useWindowFocusPoll(progressQuery.refetch, {
|
|
57
|
+
enabled: phase === "waiting",
|
|
58
|
+
intervalMs: pollIntervalMs,
|
|
59
|
+
});
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (isActiveState(progress?.state))
|
|
62
|
+
setHasSeenActiveState(true);
|
|
63
|
+
}, [progress]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (phase === "finished" && !hasReportedConnected.current) {
|
|
66
|
+
hasReportedConnected.current = true;
|
|
67
|
+
onChanged();
|
|
68
|
+
toast.success("LinkedIn Ads connected");
|
|
69
|
+
}
|
|
70
|
+
}, [phase, onChanged]);
|
|
71
|
+
function resetProgress() {
|
|
72
|
+
hasReportedConnected.current = false;
|
|
73
|
+
setSelectedIds(new Set());
|
|
74
|
+
queryClient.removeQueries({ queryKey: useLinkedinSetupProgressQuery.getKey() });
|
|
75
|
+
}
|
|
76
|
+
function handleOpened() {
|
|
77
|
+
resetProgress();
|
|
78
|
+
setHasSeenActiveState(false);
|
|
79
|
+
}
|
|
80
|
+
function handleReset() {
|
|
81
|
+
resetProgress();
|
|
82
|
+
setHasSeenActiveState(false);
|
|
83
|
+
}
|
|
84
|
+
function toggleAccount(id, checked) {
|
|
85
|
+
setSelectedIds((current) => {
|
|
86
|
+
const next = new Set(current);
|
|
87
|
+
if (checked)
|
|
88
|
+
next.add(id);
|
|
89
|
+
else
|
|
90
|
+
next.delete(id);
|
|
91
|
+
return next;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function submitAccounts() {
|
|
95
|
+
try {
|
|
96
|
+
await connect.mutateAsync({ accountIds: [...selectedIds] });
|
|
97
|
+
void progressQuery.refetch();
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const info = parseError(err, "Couldn't connect the selected accounts");
|
|
101
|
+
toast.error(info.message, { description: info.description });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function renderBody() {
|
|
105
|
+
if (phase === "failed") {
|
|
106
|
+
return (_jsx(FailedStep, { message: progress?.lastError?.message ?? "We couldn't finish connecting LinkedIn. Please try again.", title: progress?.lastError?.title, onRetry: handleReset }));
|
|
107
|
+
}
|
|
108
|
+
if (phase === "finished") {
|
|
109
|
+
return (_jsxs("div", { className: "grid gap-4", children: [importNotice ? (_jsx(ImportStartedNotice, { providerName: provider.name, notice: importNotice })) : (_jsx("p", { className: "typography-caption text-muted-foreground", children: "Your LinkedIn Ads accounts are connected." })), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { onClick: onDismiss, children: "Done" }) })] }));
|
|
110
|
+
}
|
|
111
|
+
if (phase === "selecting") {
|
|
112
|
+
return (_jsxs("div", { className: "grid gap-4", children: [_jsx("p", { className: "typography-caption text-muted-foreground", children: "Choose the LinkedIn Ads accounts Manta should connect." }), _jsx("div", { className: "grid gap-2 max-h-72 overflow-y-auto", children: isPresent(accounts) ? (accounts.map((account) => (_jsx(Checkbox, { checked: selectedIds.has(account.id), onCheckedChange: (checked) => toggleAccount(account.id, checked === true), label: account.name }, account.id)))) : (_jsx("p", { className: "typography-caption text-muted-foreground", children: "No ad accounts were found for this LinkedIn login." })) }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsx(Button, { variant: "tertiary", onClick: onBack, icon: ArrowLeft, children: "Back" }), _jsxs(Button, { onClick: () => void submitAccounts(), disabled: selectedIds.size === 0, isLoading: connect.isPending, children: ["Connect ", humanCount(selectedIds.size, "account")] })] })] }));
|
|
113
|
+
}
|
|
114
|
+
if (phase === "waiting") {
|
|
115
|
+
return (_jsxs("div", { className: "grid gap-4", children: [_jsxs("div", { className: "flex items-center gap-3", role: "status", "aria-live": "polite", children: [_jsx(Loader, {}), _jsx("p", { className: "typography-caption text-muted-foreground", children: "Loading your LinkedIn ad accounts\u2026" })] }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsx(Button, { variant: "tertiary", onClick: onBack, icon: ArrowLeft, children: "Back" }), _jsx(Button, { variant: "secondary", onClick: handleReset, children: "Start over" })] })] }));
|
|
116
|
+
}
|
|
117
|
+
return (_jsxs("div", { className: "grid gap-4", children: [_jsx("p", { className: "typography-caption text-muted-foreground", children: "Sign in with LinkedIn to let Manta read your Ads performance. You'll be taken to LinkedIn to authorize access." }), _jsxs("div", { className: "flex items-center justify-between pt-2", children: [_jsx(Button, { variant: "tertiary", onClick: onBack, icon: ArrowLeft, children: "Back" }), _jsx(OAuthConnectLink, { href: "/oauth/linkedin/start", label: "Continue with LinkedIn", onOpened: handleOpened })] })] }));
|
|
118
|
+
}
|
|
119
|
+
return (_jsxs("div", { className: "grid gap-6", children: [_jsx(ConnectFormHeader, { provider: provider, status: phase === "finished" ? "Connected" : "Not connected" }), renderBody()] }));
|
|
120
|
+
}
|
|
121
|
+
function FailedStep(props) {
|
|
122
|
+
const { message, title, onRetry } = props;
|
|
123
|
+
return (_jsxs("div", { className: "grid gap-4", children: [_jsx(Banner, { type: "error", size: "loose", label: title ?? "LinkedIn connection failed", description: message }), _jsx("div", { className: "flex justify-end", children: _jsx(Button, { onClick: onRetry, children: "Try again" }) })] }));
|
|
124
|
+
}
|