Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Instance Admin Panel: Configuration Settings #2800

Merged
merged 5 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
204 changes: 204 additions & 0 deletions web/components/instance/email-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { FC } from "react";
import { Controller, useForm } from "react-hook-form";
// ui
import { Button, Input, ToggleSwitch } from "@plane/ui";
// types
import { IFormattedInstanceConfiguration } from "types/instance";
// hooks
import useToast from "hooks/use-toast";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";

export interface IInstanceEmailForm {
config: IFormattedInstanceConfiguration;
}

export interface EmailFormValues {
EMAIL_HOST: string;
EMAIL_PORT: string;
EMAIL_HOST_USER: string;
EMAIL_HOST_PASSWORD: string;
EMAIL_USE_TLS: string;
EMAIL_USE_SSL: string;
}

export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
const { config } = props;
// store
const { instance: instanceStore } = useMobxStore();
// toast
const { setToastAlert } = useToast();
// form data
const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<EmailFormValues>({
defaultValues: {
EMAIL_HOST: config["EMAIL_HOST"],
EMAIL_PORT: config["EMAIL_PORT"],
EMAIL_HOST_USER: config["EMAIL_HOST_USER"],
EMAIL_HOST_PASSWORD: config["EMAIL_HOST_PASSWORD"],
EMAIL_USE_TLS: config["EMAIL_USE_TLS"],
EMAIL_USE_SSL: config["EMAIL_USE_SSL"],
},
});

const onSubmit = async (formData: EmailFormValues) => {
const payload: Partial<EmailFormValues> = { ...formData };

await instanceStore
.updateInstanceConfigurations(payload)
.then(() =>
setToastAlert({
title: "Success",
type: "success",
message: "Email Settings updated successfully",
})
)
.catch((err) => console.error(err));
};

return (
<div className="flex flex-col gap-8 m-8 w-4/5">
<div className="pb-2 mb-2 border-b border-custom-border-100">
<div className="text-custom-text-100 font-medium text-lg">Email</div>
<div className="text-custom-text-300 font-normal text-sm">Email related settings.</div>
</div>
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 items-center justify-between gap-x-16 gap-y-8 w-full">
<div className="flex flex-col gap-1">
<h4 className="text-sm">Host</h4>
<Controller
control={control}
name="EMAIL_HOST"
render={({ field: { value, onChange, ref } }) => (
<Input
id="EMAIL_HOST"
name="EMAIL_HOST"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.EMAIL_HOST)}
placeholder="Email Host"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>

<div className="flex flex-col gap-1">
<h4 className="text-sm">Port</h4>
<Controller
control={control}
name="EMAIL_PORT"
render={({ field: { value, onChange, ref } }) => (
<Input
id="EMAIL_PORT"
name="EMAIL_PORT"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.EMAIL_PORT)}
placeholder="Email Port"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>
</div>
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 items-center justify-between gap-x-16 gap-y-8 w-full">
<div className="flex flex-col gap-1">
<h4 className="text-sm">Username</h4>
<Controller
control={control}
name="EMAIL_HOST_USER"
render={({ field: { value, onChange, ref } }) => (
<Input
id="EMAIL_HOST_USER"
name="EMAIL_HOST_USER"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.EMAIL_HOST_USER)}
placeholder="Username"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>

<div className="flex flex-col gap-1">
<h4 className="text-sm">Password</h4>
<Controller
control={control}
name="EMAIL_HOST_PASSWORD"
render={({ field: { value, onChange, ref } }) => (
<Input
id="EMAIL_HOST_PASSWORD"
name="EMAIL_HOST_PASSWORD"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.EMAIL_HOST_PASSWORD)}
placeholder="Password"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>
</div>

<div className="flex items-center gap-8 pt-4">
<div>
<div className="text-custom-text-100 font-medium text-sm">Enable TLS</div>
</div>
<div>
<Controller
control={control}
name="EMAIL_USE_TLS"
render={({ field: { value, onChange } }) => (
<ToggleSwitch
value={Boolean(parseInt(value))}
onChange={() => {
Boolean(parseInt(value)) === true ? onChange("0") : onChange("1");
}}
size="sm"
/>
)}
/>
</div>
</div>

<div className="flex items-center gap-8 pt-4">
<div>
<div className="text-custom-text-100 font-medium text-sm">Enable SSL</div>
</div>
<div>
<Controller
control={control}
name="EMAIL_USE_SSL"
render={({ field: { value, onChange } }) => (
<ToggleSwitch
value={Boolean(parseInt(value))}
onChange={() => {
Boolean(parseInt(value)) === true ? onChange("0") : onChange("1");
}}
size="sm"
/>
)}
/>
</div>
</div>

<div className="flex items-center py-1">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save Changes"}
</Button>
</div>
</div>
);
};
6 changes: 6 additions & 0 deletions web/components/instance/general-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export const InstanceGeneralForm: FC<IInstanceGeneralForm> = (props) => {

return (
<div className="flex flex-col gap-8 m-8">
<div className="pb-2 mb-2 border-b border-custom-border-100">
<div className="text-custom-text-100 font-medium text-lg">General</div>
<div className="text-custom-text-300 font-normal text-sm">
The usual things like your mail, name of instance and other stuff.
</div>
</div>
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 items-center justify-between gap-8 w-full">
<div className="flex flex-col gap-1">
<h4 className="text-sm">Name of instance</h4>
Expand Down
132 changes: 132 additions & 0 deletions web/components/instance/github-config-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { FC } from "react";
import { Controller, useForm } from "react-hook-form";
// ui
import { Button, Input } from "@plane/ui";
// types
import { IFormattedInstanceConfiguration } from "types/instance";
// hooks
import useToast from "hooks/use-toast";
// mobx store
import { useMobxStore } from "lib/mobx/store-provider";
// icons
import { Copy } from "lucide-react";

export interface IInstanceGithubConfigForm {
config: IFormattedInstanceConfiguration;
}

export interface GithubConfigFormValues {
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET: string;
}

export const InstanceGithubConfigForm: FC<IInstanceGithubConfigForm> = (props) => {
const { config } = props;
// store
const { instance: instanceStore } = useMobxStore();
// toast
const { setToastAlert } = useToast();
// form data
const {
handleSubmit,
control,
formState: { errors, isSubmitting },
} = useForm<GithubConfigFormValues>({
defaultValues: {
GITHUB_CLIENT_ID: config["GITHUB_CLIENT_ID"],
GITHUB_CLIENT_SECRET: config["GITHUB_CLIENT_SECRET"],
},
});

const onSubmit = async (formData: GithubConfigFormValues) => {
const payload: Partial<GithubConfigFormValues> = { ...formData };

await instanceStore
.updateInstanceConfigurations(payload)
.then(() =>
setToastAlert({
title: "Success",
type: "success",
message: "Github Configuration Settings updated successfully",
})
)
.catch((err) => console.error(err));
};

const originURL = typeof window !== "undefined" ? window.location.origin : "";

return (
<>
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 items-center justify-between gap-x-16 gap-y-8 w-full">
<div className="flex flex-col gap-1">
<h4 className="text-sm">Client ID</h4>
<Controller
control={control}
name="GITHUB_CLIENT_ID"
render={({ field: { value, onChange, ref } }) => (
<Input
id="GITHUB_CLIENT_ID"
name="GITHUB_CLIENT_ID"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.GITHUB_CLIENT_ID)}
placeholder="Github Client ID"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>
<div className="flex flex-col gap-1">
<h4 className="text-sm">Client Secret</h4>
<Controller
control={control}
name="GITHUB_CLIENT_SECRET"
render={({ field: { value, onChange, ref } }) => (
<Input
id="GITHUB_CLIENT_SECRET"
name="GITHUB_CLIENT_SECRET"
type="text"
value={value}
onChange={onChange}
ref={ref}
hasError={Boolean(errors.GITHUB_CLIENT_SECRET)}
placeholder="Github Client Secret"
className="rounded-md font-medium w-full"
/>
)}
/>
</div>
</div>
<div className="grid grid-col grid-cols-1 lg:grid-cols-2 items-center justify-between gap-x-16 gap-y-8 w-full">
<div className="flex flex-col gap-1">
<h4 className="text-sm">Origin URL</h4>
<Button
variant="neutral-primary"
className="py-2 flex justify-between items-center"
onClick={() => {
navigator.clipboard.writeText(originURL);
setToastAlert({
message: "The Origin URL has been successfully copied to your clipboard",
type: "success",
title: "Copied to clipboard",
});
}}
>
<p className="font-medium text-sm">{originURL}</p>
<Copy size={18} color="#B9B9B9" />
</Button>
<p className="text-xs text-custom-text-400/60">*paste this URL in your Github console.</p>
</div>
<div className="flex flex-col gap-1">
<div className="flex items-center p-2">
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting}>
{isSubmitting ? "Saving..." : "Save Changes"}
</Button>
</div>
</div>
</div>
</>
);
};