Skip to content

Commit 11e1280

Browse files
authored
feat(UI-1429): add default-oauth to salesforce integration (#1086)
1 parent 12ecfa2 commit 11e1280

File tree

14 files changed

+207
-119
lines changed

14 files changed

+207
-119
lines changed
Lines changed: 79 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { useEffect } from "react";
1+
import React, { useEffect, useState } from "react";
22

33
import { useTranslation } from "react-i18next";
4+
import { SingleValue } from "react-select";
45

6+
import { formsPerIntegrationsMapping } from "@src/constants";
7+
import { salesforceIntegrationAuthMethods } from "@src/constants/lists/connections";
58
import { ConnectionAuthType } from "@src/enums";
69
import { Integrations } from "@src/enums/components";
710
import { useConnectionForm } from "@src/hooks";
8-
import { salesforceIntegrationSchema } from "@validations";
11+
import { SelectOption } from "@src/interfaces/components";
12+
import { oauthSchema, salesforcePrivateAuthIntegrationSchema } from "@validations";
913

10-
import { Button, ErrorMessage, Input, Spinner } from "@components/atoms";
11-
12-
import { ExternalLinkIcon } from "@assets/image/icons";
14+
import { Select } from "@components/molecules";
1315

1416
export const SalesforceIntegrationAddForm = ({
1517
connectionId,
@@ -20,53 +22,86 @@ export const SalesforceIntegrationAddForm = ({
2022
}) => {
2123
const { t } = useTranslation("integrations");
2224

23-
const { handleCustomOauth, errors, handleSubmit, isLoading, register } = useConnectionForm(
24-
salesforceIntegrationSchema,
25-
"create"
26-
);
25+
const [connectionType, setConnectionType] = useState<SingleValue<SelectOption>>();
26+
27+
const {
28+
control,
29+
copyToClipboard,
30+
errors,
31+
handleCustomOauth,
32+
handleSubmit,
33+
isLoading,
34+
register,
35+
setValidationSchema,
36+
setValue,
37+
clearErrors,
38+
} = useConnectionForm(salesforcePrivateAuthIntegrationSchema, "create");
39+
40+
const configureConnection = async (connectionId: string) => {
41+
switch (connectionType?.value) {
42+
case ConnectionAuthType.OauthDefault:
43+
await handleCustomOauth(connectionId, Integrations.salesforce, ConnectionAuthType.OauthDefault);
44+
break;
45+
case ConnectionAuthType.OauthPrivate:
46+
await handleCustomOauth(connectionId, Integrations.salesforce, ConnectionAuthType.OauthPrivate);
47+
break;
48+
default:
49+
break;
50+
}
51+
};
52+
53+
useEffect(() => {
54+
if (!connectionType?.value) {
55+
return;
56+
}
57+
if (connectionType.value === ConnectionAuthType.OauthDefault) {
58+
setValidationSchema(oauthSchema);
59+
60+
return;
61+
}
62+
if (connectionType.value === ConnectionAuthType.OauthPrivate) {
63+
setValidationSchema(salesforcePrivateAuthIntegrationSchema);
64+
65+
return;
66+
}
67+
clearErrors();
68+
// eslint-disable-next-line react-hooks/exhaustive-deps
69+
}, [connectionType]);
2770

2871
useEffect(() => {
2972
if (connectionId) {
30-
handleCustomOauth(connectionId, Integrations.salesforce, ConnectionAuthType.OauthPrivate);
73+
configureConnection(connectionId);
3174
}
3275
// eslint-disable-next-line react-hooks/exhaustive-deps
3376
}, [connectionId]);
3477

35-
return (
36-
<form className="flex flex-col gap-6" onSubmit={handleSubmit(triggerParentFormSubmit)}>
37-
<div className="relative">
38-
<Input
39-
{...register("client_id")}
40-
aria-label={t("salesforce.placeholders.clientId")}
41-
isError={!!errors.client_id}
42-
isRequired
43-
label={t("salesforce.placeholders.clientId")}
44-
/>
45-
46-
<ErrorMessage>{errors.client_id?.message as string}</ErrorMessage>
47-
</div>
48-
<div className="relative">
49-
<Input
50-
{...register("client_secret")}
51-
aria-label={t("salesforce.placeholders.clientSecret")}
52-
isError={!!errors.client_secret}
53-
isRequired
54-
label={t("salesforce.placeholders.clientSecret")}
55-
/>
78+
const ConnectionTypeComponent =
79+
formsPerIntegrationsMapping[Integrations.height]?.[connectionType?.value as ConnectionAuthType];
5680

57-
<ErrorMessage>{errors.client_secret?.message as string}</ErrorMessage>
58-
</div>
59-
60-
<Button
61-
aria-label={t("buttons.startOAuthFlow")}
62-
className="ml-auto w-fit border-white px-3 font-medium text-white hover:bg-black"
81+
return (
82+
<>
83+
<Select
84+
aria-label={t("placeholders.selectConnectionType")}
6385
disabled={isLoading}
64-
type="submit"
65-
variant="outline"
66-
>
67-
{isLoading ? <Spinner /> : <ExternalLinkIcon className="size-4 fill-white transition" />}
68-
{t("buttons.startOAuthFlow")}
69-
</Button>
70-
</form>
86+
label={t("placeholders.connectionType")}
87+
onChange={(option) => setConnectionType(option)}
88+
options={salesforceIntegrationAuthMethods}
89+
placeholder={t("placeholders.selectConnectionType")}
90+
value={connectionType}
91+
/>
92+
<form className="mt-6 flex flex-col gap-6" onSubmit={handleSubmit(triggerParentFormSubmit)}>
93+
{ConnectionTypeComponent ? (
94+
<ConnectionTypeComponent
95+
control={control}
96+
copyToClipboard={copyToClipboard}
97+
errors={errors}
98+
isLoading={isLoading}
99+
mode="create"
100+
register={register}
101+
setValue={setValue}
102+
/>
103+
) : null}
104+
</form>
105+
</>
71106
);
72107
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { SalesforceOauthForm } from "@components/organisms/connections/integrations/salesforce/authMethods/oauth";
2+
export { SalesforceOauthPrivateForm } from "@components/organisms/connections/integrations/salesforce/authMethods/oauthPrivate";
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from "react";
2+
3+
import { useTranslation } from "react-i18next";
4+
5+
import { Button, Spinner } from "@components/atoms";
6+
7+
import { ExternalLinkIcon } from "@assets/image/icons";
8+
9+
export const SalesforceOauthForm = ({ isLoading }: { isLoading: boolean }) => {
10+
const { t } = useTranslation("integrations");
11+
12+
return (
13+
<Button
14+
aria-label={t("buttons.startOAuthFlow")}
15+
className="ml-auto w-fit border-white px-3 font-medium text-white hover:bg-black"
16+
disabled={isLoading}
17+
type="submit"
18+
variant="outline"
19+
>
20+
{isLoading ? <Spinner /> : <ExternalLinkIcon className="size-4 fill-white transition" />}
21+
{t("buttons.startOAuthFlow")}
22+
</Button>
23+
);
24+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from "react";
2+
3+
import { FieldErrors, UseFormRegister, useWatch } from "react-hook-form";
4+
import { useTranslation } from "react-i18next";
5+
6+
import { Button, ErrorMessage, Input, Spinner } from "@components/atoms";
7+
8+
import { ExternalLinkIcon } from "@assets/image/icons";
9+
10+
export const SalesforceOauthPrivateForm = ({
11+
control,
12+
errors,
13+
isLoading,
14+
register,
15+
}: {
16+
control: any;
17+
errors: FieldErrors<any>;
18+
isLoading: boolean;
19+
mode: "create" | "edit";
20+
register: UseFormRegister<{ [x: string]: any }>;
21+
setValue: any;
22+
}) => {
23+
const { t } = useTranslation("integrations");
24+
25+
const clientId = useWatch({ control, name: "client_id" });
26+
const clientSecret = useWatch({ control, name: "client_secret" });
27+
28+
return (
29+
<>
30+
<div className="relative">
31+
<Input
32+
{...register("client_id")}
33+
aria-label={t("salesforce.placeholders.clientId")}
34+
isError={!!errors.client_id}
35+
isRequired
36+
label={t("salesforce.placeholders.clientId")}
37+
value={clientId}
38+
/>
39+
40+
<ErrorMessage>{errors.client_id?.message as string}</ErrorMessage>
41+
</div>
42+
<div className="relative">
43+
<Input
44+
{...register("client_secret")}
45+
aria-label={t("salesforce.placeholders.clientSecret")}
46+
isError={!!errors.client_secret}
47+
isRequired
48+
label={t("salesforce.placeholders.clientSecret")}
49+
value={clientSecret}
50+
/>
51+
52+
<ErrorMessage>{errors.client_secret?.message as string}</ErrorMessage>
53+
</div>
54+
55+
<Button
56+
aria-label={t("buttons.startOAuthFlow")}
57+
className="ml-auto w-fit border-white px-3 font-medium text-white hover:bg-black"
58+
disabled={isLoading}
59+
type="submit"
60+
variant="outline"
61+
>
62+
{isLoading ? <Spinner /> : <ExternalLinkIcon className="size-4 fill-white transition" />}
63+
{t("buttons.startOAuthFlow")}
64+
</Button>
65+
</>
66+
);
67+
};
Lines changed: 12 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,21 @@
1-
import React, { useEffect, useState } from "react";
1+
import React from "react";
22

3-
import { useWatch } from "react-hook-form";
4-
import { useTranslation } from "react-i18next";
5-
import { useParams } from "react-router-dom";
6-
7-
import { integrationVariablesMapping } from "@src/constants";
3+
import { salesforceIntegrationAuthMethods } from "@src/constants/lists/connections";
84
import { ConnectionAuthType } from "@src/enums";
95
import { Integrations } from "@src/enums/components";
10-
import { useConnectionForm } from "@src/hooks";
11-
import { setFormValues } from "@src/utilities";
12-
import { salesforceIntegrationSchema } from "@validations";
13-
14-
import { Button, ErrorMessage, Input, SecretInput, Spinner } from "@components/atoms";
6+
import { salesforcePrivateAuthIntegrationSchema, oauthSchema } from "@validations";
157

16-
import { ExternalLinkIcon } from "@assets/image/icons";
8+
import { IntegrationEditForm } from "@components/organisms/connections/integrations";
179

1810
export const SalesforceIntegrationEditForm = () => {
19-
const { t } = useTranslation("integrations");
20-
const [lockState, setLockState] = useState(true);
21-
const { connectionId } = useParams();
22-
23-
const { connectionVariables, control, errors, handleSubmit, isLoading, handleCustomOauth, register, setValue } =
24-
useConnectionForm(salesforceIntegrationSchema, "edit");
25-
26-
const clientSecret = useWatch({ control, name: "client_secret" });
27-
28-
useEffect(() => {
29-
setFormValues(connectionVariables, integrationVariablesMapping.chatgpt, setValue);
30-
// eslint-disable-next-line react-hooks/exhaustive-deps
31-
}, [connectionVariables]);
32-
3311
return (
34-
<form
35-
className="flex flex-col gap-4"
36-
onSubmit={handleSubmit(() =>
37-
handleCustomOauth(connectionId!, Integrations.salesforce, ConnectionAuthType.OauthPrivate)
38-
)}
39-
>
40-
<div className="relative">
41-
<Input
42-
{...register("client_id")}
43-
aria-label={t("salesforce.placeholders.clientId")}
44-
isError={!!errors.client_id}
45-
isRequired
46-
label={t("salesforce.placeholders.clientId")}
47-
/>
48-
49-
<ErrorMessage>{errors.client_id?.message as string}</ErrorMessage>
50-
</div>
51-
<div className="relative">
52-
<SecretInput
53-
type="password"
54-
{...register("client_secret")}
55-
aria-label={t("salesforce.placeholders.clientSecret")}
56-
handleInputChange={(newValue) => setValue("client_secret", newValue)}
57-
handleLockAction={setLockState}
58-
isError={!!errors.client_secret}
59-
isLocked={lockState}
60-
isRequired
61-
label={t("salesforce.placeholders.clientSecret")}
62-
value={clientSecret}
63-
/>
64-
<ErrorMessage>{errors.client_secret?.message as string}</ErrorMessage>
65-
</div>
66-
67-
<Button
68-
aria-label={t("buttons.startOAuthFlow")}
69-
className="ml-auto w-fit border-white px-3 font-medium text-white hover:bg-black"
70-
disabled={isLoading}
71-
type="submit"
72-
variant="outline"
73-
>
74-
{isLoading ? <Spinner /> : <ExternalLinkIcon className="size-4 fill-white transition" />}
75-
{t("buttons.startOAuthFlow")}
76-
</Button>
77-
</form>
12+
<IntegrationEditForm
13+
integrationType={Integrations.salesforce}
14+
schemas={{
15+
[ConnectionAuthType.OauthPrivate]: salesforcePrivateAuthIntegrationSchema,
16+
[ConnectionAuthType.OauthDefault]: oauthSchema,
17+
}}
18+
selectOptions={salesforceIntegrationAuthMethods}
19+
/>
7820
);
7921
};

src/constants/connections/formsPerIntegrationsMapping.constants.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ import {
3535
MicrosoftTeamsOauthPrivateForm,
3636
MicrosoftTeamsDaemonForm,
3737
} from "@components/organisms/connections/integrations/microsoft/teams";
38+
import {
39+
SalesforceOauthPrivateForm,
40+
SalesforceOauthForm,
41+
} from "@components/organisms/connections/integrations/salesforce/authMethods";
3842
import {
3943
OauthForm as SlackOauthForm,
4044
SocketForm,
@@ -119,4 +123,8 @@ export const formsPerIntegrationsMapping: Partial<
119123
[ConnectionAuthType.OauthPrivate]: MicrosoftTeamsOauthPrivateForm,
120124
[ConnectionAuthType.DaemonApp]: MicrosoftTeamsDaemonForm,
121125
},
126+
[Integrations.salesforce]: {
127+
[ConnectionAuthType.OauthDefault]: SalesforceOauthForm,
128+
[ConnectionAuthType.OauthPrivate]: SalesforceOauthPrivateForm,
129+
},
122130
};

src/constants/connections/integrationsCustomOAuthPaths.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,4 @@ import { Integrations } from "@src/enums/components";
22

33
export const integrationsCustomOAuthPaths: Partial<Record<keyof typeof Integrations, string>> = {
44
github: "custom-oauth",
5-
height: "save",
6-
linear: "save",
7-
zoom: "save",
85
};

src/constants/featureFlags.constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const featureFlags = {
33
displaySlackSocketIntegration: import.meta.env.DISPLAY_SLACK_SOCKET_INTEGRATION,
44
heightHideDefaultOAuth: import.meta.env.VITE_HEIGHT_HIDE_DEFAULT_OAUTH,
55
linearHideDefaultOAuth: import.meta.env.VITE_LINEAR_HIDE_DEFAULT_OAUTH,
6+
salesforceHideDefaultOAuth: import.meta.env.VITE_SALESFORCE_HIDE_DEFAULT_OAUTH,
67
zoomHideDefaultOAuth: import.meta.env.VITE_ZOOM_HIDE_DEFAULT_OAUTH,
78
microsoftHideIntegration: import.meta.env.VITE_MICROSOFT_HIDE_INTEGRATION,
89
sendDotEmptyTriggerFilter: import.meta.env.VITE_SEND_DOT_EMPTY_TRIGGER_FILTER,

src/constants/lists/connections/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,5 @@ export {
2727
zoomIntegrationAuthMethods,
2828
selectIntegrationLinearActor,
2929
microsoftTeamsIntegrationAuthMethods,
30+
salesforceIntegrationAuthMethods,
3031
} from "@constants/lists/connections/options.constants";

src/constants/lists/connections/options.constants.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ export const linearIntegrationAuthMethods: SelectOption[] = [
3939
{ label: "API Key", value: ConnectionAuthType.ApiKey },
4040
];
4141

42+
const salesforceDisplayOAuth = featureFlags.salesforceHideDefaultOAuth
43+
? []
44+
: [{ label: "OAuth v2 - Default app", value: ConnectionAuthType.OauthDefault }];
45+
46+
export const salesforceIntegrationAuthMethods: SelectOption[] = [
47+
...salesforceDisplayOAuth,
48+
{ label: "OAuth v2 - Private app", value: ConnectionAuthType.OauthPrivate },
49+
];
50+
4251
const zoomDisplayOAuth = featureFlags.zoomHideDefaultOAuth
4352
? []
4453
: [{ label: "OAuth v2 - Default app", value: ConnectionAuthType.OauthDefault }];

0 commit comments

Comments
 (0)