Skip to content

Commit 96abc70

Browse files
fix: preview sync merge conflict
2 parents 8715972 + 350107d commit 96abc70

File tree

260 files changed

+8255
-2289
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

260 files changed

+8255
-2289
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"use client";
2+
3+
import type { FC } from "react";
4+
import { useState } from "react";
5+
import { isEmpty } from "lodash-es";
6+
import Link from "next/link";
7+
import { useForm } from "react-hook-form";
8+
// plane internal packages
9+
import { API_BASE_URL } from "@plane/constants";
10+
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
11+
import type { IFormattedInstanceConfiguration, TInstanceGiteaAuthenticationConfigurationKeys } from "@plane/types";
12+
import { Button, getButtonStyling } from "@plane/ui";
13+
import { cn } from "@plane/utils";
14+
// components
15+
import { CodeBlock } from "@/components/common/code-block";
16+
import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal";
17+
import type { TControllerInputFormField } from "@/components/common/controller-input";
18+
import { ControllerInput } from "@/components/common/controller-input";
19+
import type { TCopyField } from "@/components/common/copy-field";
20+
import { CopyField } from "@/components/common/copy-field";
21+
// hooks
22+
import { useInstance } from "@/hooks/store";
23+
24+
type Props = {
25+
config: IFormattedInstanceConfiguration;
26+
};
27+
28+
type GiteaConfigFormValues = Record<TInstanceGiteaAuthenticationConfigurationKeys, string>;
29+
30+
export const InstanceGiteaConfigForm: FC<Props> = (props) => {
31+
const { config } = props;
32+
// states
33+
const [isDiscardChangesModalOpen, setIsDiscardChangesModalOpen] = useState(false);
34+
// store hooks
35+
const { updateInstanceConfigurations } = useInstance();
36+
// form data
37+
const {
38+
handleSubmit,
39+
control,
40+
reset,
41+
formState: { errors, isDirty, isSubmitting },
42+
} = useForm<GiteaConfigFormValues>({
43+
defaultValues: {
44+
GITEA_HOST: config["GITEA_HOST"] || "https://gitea.com",
45+
GITEA_CLIENT_ID: config["GITEA_CLIENT_ID"],
46+
GITEA_CLIENT_SECRET: config["GITEA_CLIENT_SECRET"],
47+
},
48+
});
49+
50+
const originURL = !isEmpty(API_BASE_URL) ? API_BASE_URL : typeof window !== "undefined" ? window.location.origin : "";
51+
52+
const GITEA_FORM_FIELDS: TControllerInputFormField[] = [
53+
{
54+
key: "GITEA_HOST",
55+
type: "text",
56+
label: "Gitea Host",
57+
description: (
58+
<>Use the URL of your Gitea instance. For the official Gitea instance, use &quot;https://gitea.com&quot;.</>
59+
),
60+
placeholder: "https://gitea.com",
61+
error: Boolean(errors.GITEA_HOST),
62+
required: true,
63+
},
64+
{
65+
key: "GITEA_CLIENT_ID",
66+
type: "text",
67+
label: "Client ID",
68+
description: (
69+
<>
70+
You will get this from your{" "}
71+
<a
72+
tabIndex={-1}
73+
href="https://gitea.com/user/settings/applications"
74+
target="_blank"
75+
className="text-custom-primary-100 hover:underline"
76+
rel="noreferrer"
77+
>
78+
Gitea OAuth application settings.
79+
</a>
80+
</>
81+
),
82+
placeholder: "70a44354520df8bd9bcd",
83+
error: Boolean(errors.GITEA_CLIENT_ID),
84+
required: true,
85+
},
86+
{
87+
key: "GITEA_CLIENT_SECRET",
88+
type: "password",
89+
label: "Client secret",
90+
description: (
91+
<>
92+
Your client secret is also found in your{" "}
93+
<a
94+
tabIndex={-1}
95+
href="https://gitea.com/user/settings/applications"
96+
target="_blank"
97+
className="text-custom-primary-100 hover:underline"
98+
rel="noreferrer"
99+
>
100+
Gitea OAuth application settings.
101+
</a>
102+
</>
103+
),
104+
placeholder: "9b0050f94ec1b744e32ce79ea4ffacd40d4119cb",
105+
error: Boolean(errors.GITEA_CLIENT_SECRET),
106+
required: true,
107+
},
108+
];
109+
110+
const GITEA_SERVICE_FIELD: TCopyField[] = [
111+
{
112+
key: "Callback_URI",
113+
label: "Callback URI",
114+
url: `${originURL}/auth/gitea/callback/`,
115+
description: (
116+
<>
117+
We will auto-generate this. Paste this into your <CodeBlock darkerShade>Authorized Callback URI</CodeBlock>{" "}
118+
field{" "}
119+
<a
120+
tabIndex={-1}
121+
href={`${control._formValues.GITEA_HOST || "https://gitea.com"}/user/settings/applications`}
122+
target="_blank"
123+
className="text-custom-primary-100 hover:underline"
124+
rel="noreferrer"
125+
>
126+
here.
127+
</a>
128+
</>
129+
),
130+
},
131+
];
132+
133+
const onSubmit = async (formData: GiteaConfigFormValues) => {
134+
const payload: Partial<GiteaConfigFormValues> = { ...formData };
135+
136+
await updateInstanceConfigurations(payload)
137+
.then((response = []) => {
138+
setToast({
139+
type: TOAST_TYPE.SUCCESS,
140+
title: "Done!",
141+
message: "Your Gitea authentication is configured. You should test it now.",
142+
});
143+
reset({
144+
GITEA_HOST: response.find((item) => item.key === "GITEA_HOST")?.value,
145+
GITEA_CLIENT_ID: response.find((item) => item.key === "GITEA_CLIENT_ID")?.value,
146+
GITEA_CLIENT_SECRET: response.find((item) => item.key === "GITEA_CLIENT_SECRET")?.value,
147+
});
148+
})
149+
.catch((err) => console.error(err));
150+
};
151+
152+
const handleGoBack = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
153+
if (isDirty) {
154+
e.preventDefault();
155+
setIsDiscardChangesModalOpen(true);
156+
}
157+
};
158+
159+
return (
160+
<>
161+
<ConfirmDiscardModal
162+
isOpen={isDiscardChangesModalOpen}
163+
onDiscardHref="/authentication"
164+
handleClose={() => setIsDiscardChangesModalOpen(false)}
165+
/>
166+
<div className="flex flex-col gap-8">
167+
<div className="grid grid-cols-2 gap-x-12 gap-y-8 w-full">
168+
<div className="flex flex-col gap-y-4 col-span-2 md:col-span-1 pt-1">
169+
<div className="pt-2.5 text-xl font-medium">Gitea-provided details for Plane</div>
170+
{GITEA_FORM_FIELDS.map((field) => (
171+
<ControllerInput
172+
key={field.key}
173+
control={control}
174+
type={field.type}
175+
name={field.key}
176+
label={field.label}
177+
description={field.description}
178+
placeholder={field.placeholder}
179+
error={field.error}
180+
required={field.required}
181+
/>
182+
))}
183+
<div className="flex flex-col gap-1 pt-4">
184+
<div className="flex items-center gap-4">
185+
<Button variant="primary" onClick={handleSubmit(onSubmit)} loading={isSubmitting} disabled={!isDirty}>
186+
{isSubmitting ? "Saving..." : "Save changes"}
187+
</Button>
188+
<Link
189+
href="/authentication"
190+
className={cn(getButtonStyling("neutral-primary", "md"), "font-medium")}
191+
onClick={handleGoBack}
192+
>
193+
Go back
194+
</Link>
195+
</div>
196+
</div>
197+
</div>
198+
<div className="col-span-2 md:col-span-1">
199+
<div className="flex flex-col gap-y-4 px-6 pt-1.5 pb-4 bg-custom-background-80/60 rounded-lg">
200+
<div className="pt-2 text-xl font-medium">Plane-provided details for Gitea</div>
201+
{GITEA_SERVICE_FIELD.map((field) => (
202+
<CopyField key={field.key} label={field.label} url={field.url} description={field.description} />
203+
))}
204+
</div>
205+
</div>
206+
</div>
207+
</div>
208+
</>
209+
);
210+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { ReactNode } from "react";
2+
import type { Metadata } from "next";
3+
4+
export const metadata: Metadata = {
5+
title: "Gitea Authentication - God Mode",
6+
};
7+
8+
export default function GiteaAuthenticationLayout({ children }: { children: ReactNode }) {
9+
return <>{children}</>;
10+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { observer } from "mobx-react";
5+
import Image from "next/image";
6+
import { useTheme } from "next-themes";
7+
import useSWR from "swr";
8+
// plane internal packages
9+
import { setPromiseToast } from "@plane/propel/toast";
10+
import { Loader, ToggleSwitch } from "@plane/ui";
11+
// components
12+
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
13+
// hooks
14+
import { useInstance } from "@/hooks/store";
15+
// icons
16+
import giteaLogo from "@/public/logos/gitea-logo.svg";
17+
//local components
18+
import { InstanceGiteaConfigForm } from "./form";
19+
20+
const InstanceGiteaAuthenticationPage = observer(() => {
21+
// store
22+
const { fetchInstanceConfigurations, formattedConfig, updateInstanceConfigurations } = useInstance();
23+
// state
24+
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
25+
// theme
26+
const { resolvedTheme } = useTheme();
27+
// config
28+
const enableGiteaConfig = formattedConfig?.IS_GITEA_ENABLED ?? "";
29+
useSWR("INSTANCE_CONFIGURATIONS", () => fetchInstanceConfigurations());
30+
31+
const updateConfig = async (key: "IS_GITEA_ENABLED", value: string) => {
32+
setIsSubmitting(true);
33+
34+
const payload = {
35+
[key]: value,
36+
};
37+
38+
const updateConfigPromise = updateInstanceConfigurations(payload);
39+
40+
setPromiseToast(updateConfigPromise, {
41+
loading: "Saving Configuration...",
42+
success: {
43+
title: "Configuration saved",
44+
message: () => `Gitea authentication is now ${value === "1" ? "active" : "disabled"}.`,
45+
},
46+
error: {
47+
title: "Error",
48+
message: () => "Failed to save configuration",
49+
},
50+
});
51+
52+
await updateConfigPromise
53+
.then(() => {
54+
setIsSubmitting(false);
55+
})
56+
.catch((err) => {
57+
console.error(err);
58+
setIsSubmitting(false);
59+
});
60+
};
61+
62+
const isGiteaEnabled = enableGiteaConfig === "1";
63+
64+
return (
65+
<>
66+
<div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
67+
<div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
68+
<AuthenticationMethodCard
69+
name="Gitea"
70+
description="Allow members to login or sign up to plane with their Gitea accounts."
71+
icon={<Image src={giteaLogo} height={24} width={24} alt="Gitea Logo" />}
72+
config={
73+
<ToggleSwitch
74+
value={isGiteaEnabled}
75+
onChange={() => {
76+
updateConfig("IS_GITEA_ENABLED", isGiteaEnabled ? "0" : "1");
77+
}}
78+
size="sm"
79+
disabled={isSubmitting || !formattedConfig}
80+
/>
81+
}
82+
disabled={isSubmitting || !formattedConfig}
83+
withBorder={false}
84+
/>
85+
</div>
86+
<div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
87+
{formattedConfig ? (
88+
<InstanceGiteaConfigForm config={formattedConfig} />
89+
) : (
90+
<Loader className="space-y-8">
91+
<Loader.Item height="50px" width="25%" />
92+
<Loader.Item height="50px" />
93+
<Loader.Item height="50px" />
94+
<Loader.Item height="50px" />
95+
<Loader.Item height="50px" width="50%" />
96+
</Loader>
97+
)}
98+
</div>
99+
</div>
100+
</>
101+
);
102+
});
103+
104+
export default InstanceGiteaAuthenticationPage;

apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const InstanceGithubAuthenticationPage = observer(() => {
4444
loading: "Saving Configuration...",
4545
success: {
4646
title: "Configuration saved",
47-
message: () => `GitHub authentication is now ${value ? "active" : "disabled"}.`,
47+
message: () => `GitHub authentication is now ${value === "1" ? "active" : "disabled"}.`,
4848
},
4949
error: {
5050
title: "Error",

apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const InstanceGitlabAuthenticationPage = observer(() => {
3838
loading: "Saving Configuration...",
3939
success: {
4040
title: "Configuration saved",
41-
message: () => `GitLab authentication is now ${value ? "active" : "disabled"}.`,
41+
message: () => `GitLab authentication is now ${value === "1" ? "active" : "disabled"}.`,
4242
},
4343
error: {
4444
title: "Error",

apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const InstanceGoogleAuthenticationPage = observer(() => {
3838
loading: "Saving Configuration...",
3939
success: {
4040
title: "Configuration saved",
41-
message: () => `Google authentication is now ${value ? "active" : "disabled"}.`,
41+
message: () => `Google authentication is now ${value === "1" ? "active" : "disabled"}.`,
4242
},
4343
error: {
4444
title: "Error",

apps/admin/ce/components/authentication/authentication-modes.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import { resolveGeneralTheme } from "@plane/utils";
1212
// components
1313
import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card";
1414
import { EmailCodesConfiguration } from "@/components/authentication/email-config-switch";
15+
import { GiteaConfiguration } from "@/components/authentication/gitea-config";
1516
import { GithubConfiguration } from "@/components/authentication/github-config";
1617
import { GitlabConfiguration } from "@/components/authentication/gitlab-config";
1718
import { GoogleConfiguration } from "@/components/authentication/google-config";
1819
import { PasswordLoginConfiguration } from "@/components/authentication/password-config-switch";
1920
// plane admin components
2021
import { UpgradeButton } from "@/plane-admin/components/common";
2122
// assets
23+
import giteaLogo from "@/public/logos/gitea-logo.svg";
2224
import githubLightModeImage from "@/public/logos/github-black.png";
2325
import githubDarkModeImage from "@/public/logos/github-white.png";
2426
import GitlabLogo from "@/public/logos/gitlab-logo.svg";
@@ -80,6 +82,13 @@ export const getAuthenticationModes: (props: TGetBaseAuthenticationModeProps) =>
8082
icon: <Image src={GitlabLogo} height={20} width={20} alt="GitLab Logo" />,
8183
config: <GitlabConfiguration disabled={disabled} updateConfig={updateConfig} />,
8284
},
85+
{
86+
key: "gitea",
87+
name: "Gitea",
88+
description: "Allow members to log in or sign up to plane with their Gitea accounts.",
89+
icon: <Image src={giteaLogo} height={20} width={20} alt="Gitea Logo" />,
90+
config: <GiteaConfiguration disabled={disabled} updateConfig={updateConfig} />,
91+
},
8392
{
8493
key: "oidc",
8594
name: "OIDC",

0 commit comments

Comments
 (0)