forked from github/plane
212 lines
6.8 KiB
TypeScript
212 lines
6.8 KiB
TypeScript
import { FC, useState } from "react";
|
|
import isEmpty from "lodash/isEmpty";
|
|
import Link from "next/link";
|
|
import { useForm } from "react-hook-form";
|
|
// types
|
|
import { IFormattedInstanceConfiguration, TInstanceGoogleAuthenticationConfigurationKeys } from "@plane/types";
|
|
// ui
|
|
import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui";
|
|
// components
|
|
import {
|
|
ConfirmDiscardModal,
|
|
ControllerInput,
|
|
CopyField,
|
|
TControllerInputFormField,
|
|
TCopyField,
|
|
} from "@/components/common";
|
|
// helpers
|
|
import { API_BASE_URL, cn } from "@/helpers/common.helper";
|
|
// hooks
|
|
import { useInstance } from "@/hooks/store";
|
|
|
|
type Props = {
|
|
config: IFormattedInstanceConfiguration;
|
|
};
|
|
|
|
type GoogleConfigFormValues = Record<TInstanceGoogleAuthenticationConfigurationKeys, string>;
|
|
|
|
export const InstanceGoogleConfigForm: FC<Props> = (props) => {
|
|
const { config } = props;
|
|
// states
|
|
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
|
|
// store hooks
|
|
const { updateInstanceConfigurations } = useInstance();
|
|
// form data
|
|
const {
|
|
handleSubmit,
|
|
control,
|
|
reset,
|
|
formState: { errors, isDirty, isSubmitting },
|
|
} = useForm<GoogleConfigFormValues>({
|
|
defaultValues: {
|
|
GOOGLE_CLIENT_ID: config["GOOGLE_CLIENT_ID"],
|
|
GOOGLE_CLIENT_SECRET: config["GOOGLE_CLIENT_SECRET"],
|
|
},
|
|
});
|
|
|
|
const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";
|
|
|
|
const GOOGLE_FORM_FIELDS: TControllerInputFormField[] = [
|
|
{
|
|
key: "GOOGLE_CLIENT_ID",
|
|
type: "text",
|
|
label: "Client ID",
|
|
description: (
|
|
<>
|
|
Your client ID lives in your Google API Console.{" "}
|
|
<a
|
|
tabIndex={-1}
|
|
href="https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow#creatingcred"
|
|
target="_blank"
|
|
className="text-custom-primary-100 hover:underline"
|
|
rel="noreferrer"
|
|
>
|
|
Learn more
|
|
</a>
|
|
</>
|
|
),
|
|
placeholder: "840195096245-0p2tstej9j5nc4l8o1ah2dqondscqc1g.apps.googleusercontent.com",
|
|
error: Boolean(errors.GOOGLE_CLIENT_ID),
|
|
required: true,
|
|
},
|
|
{
|
|
key: "GOOGLE_CLIENT_SECRET",
|
|
type: "password",
|
|
label: "Client secret",
|
|
description: (
|
|
<>
|
|
Your client secret should also be in your Google API Console.{" "}
|
|
<a
|
|
tabIndex={-1}
|
|
href="https://developers.google.com/identity/oauth2/web/guides/get-google-api-clientid"
|
|
target="_blank"
|
|
className="text-custom-primary-100 hover:underline"
|
|
rel="noreferrer"
|
|
>
|
|
Learn more
|
|
</a>
|
|
</>
|
|
),
|
|
placeholder: "GOCShX-ADp4cI0kPqav1gGCBg5bE02E",
|
|
error: Boolean(errors.GOOGLE_CLIENT_SECRET),
|
|
required: true,
|
|
},
|
|
];
|
|
|
|
const GOOGLE_SERVICE_DETAILS: TCopyField[] = [
|
|
{
|
|
key: "Origin_URL",
|
|
label: "Origin URL",
|
|
url: originURL,
|
|
description: (
|
|
<p>
|
|
We will auto-generate this. Paste this into your Authorized JavaScript origins field. For this OAuth client{" "}
|
|
<a
|
|
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
|
target="_blank"
|
|
className="text-custom-primary-100 hover:underline"
|
|
rel="noreferrer"
|
|
>
|
|
here.
|
|
</a>
|
|
</p>
|
|
),
|
|
},
|
|
{
|
|
key: "Callback_URI",
|
|
label: "Callback URI",
|
|
url: `${originURL}/auth/google/callback/`,
|
|
description: (
|
|
<p>
|
|
We will auto-generate this. Paste this into your Authorized Redirect URI field. For this OAuth client{" "}
|
|
<a
|
|
href="https://console.cloud.google.com/apis/credentials/oauthclient"
|
|
target="_blank"
|
|
className="text-custom-primary-100 hover:underline"
|
|
rel="noreferrer"
|
|
>
|
|
here.
|
|
</a>
|
|
</p>
|
|
),
|
|
},
|
|
];
|
|
|
|
const onSubmit = async (formData: GoogleConfigFormValues) => {
|
|
const payload: Partial<GoogleConfigFormValues> = { ...formData };
|
|
|
|
await updateInstanceConfigurations(payload)
|
|
.then((response = []) => {
|
|
setToast({
|
|
type: TOAST_TYPE.SUCCESS,
|
|
title: "Success",
|
|
message: "Google Configuration Settings updated successfully",
|
|
});
|
|
reset({
|
|
GOOGLE_CLIENT_ID: response.find((item) => item.key === "GOOGLE_CLIENT_ID")?.value,
|
|
GOOGLE_CLIENT_SECRET: response.find((item) => item.key === "GOOGLE_CLIENT_SECRET")?.value,
|
|
});
|
|
})
|
|
.catch((err) => console.error(err));
|
|
};
|
|
|
|
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
|
if (isDirty) {
|
|
e.preventDefault();
|
|
setIsDiscardChangesModalOpen(true);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<ConfirmDiscardModal
|
|
isOpen={isDiscardChangesModalOpen}
|
|
onDiscardHref="/authentication"
|
|
handleClose={() => setIsDiscardChangesModalOpen(false)}
|
|
/>
|
|
<div className="flex flex-col gap-8">
|
|
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
|
|
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1">
|
|
<div className="pt-2 text-xl font-medium">Configuration</div>
|
|
{GOOGLE_FORM_FIELDS.map((field) => (
|
|
<ControllerInput
|
|
key={field.key}
|
|
control={control}
|
|
type={field.type}
|
|
name={field.key}
|
|
label={field.label}
|
|
description={field.description}
|
|
placeholder={field.placeholder}
|
|
error={field.error}
|
|
required={field.required}
|
|
/>
|
|
))}
|
|
<div className="flex flex-col gap-1 pt-4">
|
|
<div className="flex items-center gap-4">
|
|
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting} disabled={!isDirty}>
|
|
{isSubmitting ? "Saving..." : "Save changes"}
|
|
</Button>
|
|
<Link
|
|
href="/authentication"
|
|
className={cn(getButtonStyling("link-neutral", "md"), "font-medium")}
|
|
onClick={handleGoBack}
|
|
>
|
|
Go back
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="col-span-2 md:col-span-1">
|
|
<div className="flex flex-col gap-y-4 px-6 py-4 my-2 bg-custom-background-80/60 rounded-lg">
|
|
<div className="pt-2 text-xl font-medium">Service provider details</div>
|
|
{GOOGLE_SERVICE_DETAILS.map((field) => (
|
|
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|