[WEB-4542] feat: god mode auth revamp and code refactor (#7563)
* chore: auth color updated and remove unused tokens * chore: god-mode brand revamp * fix: space app spinner logo
This commit is contained in:
parent
047080a66f
commit
d317755ab9
17 changed files with 358 additions and 418 deletions
12
apps/admin/app/(all)/(home)/auth-header.tsx
Normal file
12
apps/admin/app/(all)/(home)/auth-header.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { PlaneLockup } from "@plane/ui";
|
||||
|
||||
export const AuthHeader = () => (
|
||||
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
|
||||
<Link href="/">
|
||||
<PlaneLockup height={20} width={95} className="text-custom-text-100" />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
|
@ -1,35 +1,9 @@
|
|||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useTheme } from "next-themes";
|
||||
// logo assets
|
||||
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
|
||||
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
|
||||
import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.png";
|
||||
import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.png";
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
const patternBackground = resolvedTheme === "light" ? PlaneBackgroundPattern : PlaneBackgroundPatternDark;
|
||||
const logo = resolvedTheme === "light" ? BlackHorizontalLogo : WhiteHorizontalLogo;
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="h-screen w-full overflow-hidden overflow-y-auto flex flex-col">
|
||||
<div className="container h-[110px] flex-shrink-0 mx-auto px-5 lg:px-0 flex items-center justify-between gap-5 z-50">
|
||||
<div className="flex items-center gap-x-2 py-10">
|
||||
<Link href={`/`} className="h-[30px] w-[133px]">
|
||||
<Image src={logo} alt="Plane logo" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image src={patternBackground} className="w-screen h-full object-cover" alt="Plane background pattern" />
|
||||
</div>
|
||||
<div className="relative z-10 flex-grow">{children}</div>
|
||||
</div>
|
||||
<div className="relative z-10 flex flex-col items-center w-screen h-screen overflow-hidden overflow-y-auto pt-6 pb-10 px-8">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
import { observer } from "mobx-react";
|
||||
// components
|
||||
import { LogoSpinner } from "@/components/common/logo-spinner";
|
||||
import { InstanceFailureView } from "@/components/instance/failure";
|
||||
import { InstanceLoading } from "@/components/instance/loading";
|
||||
import { InstanceSetupForm } from "@/components/instance/setup-form";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store";
|
||||
|
|
@ -17,46 +17,24 @@ const HomePage = () => {
|
|||
// if instance is not fetched, show loading
|
||||
if (!instance && !error) {
|
||||
return (
|
||||
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
|
||||
<InstanceLoading />
|
||||
<div className="flex items-center justify-center h-screen w-full">
|
||||
<LogoSpinner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// if instance fetch fails, show failure view
|
||||
if (error) {
|
||||
return (
|
||||
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
|
||||
<InstanceFailureView />
|
||||
</div>
|
||||
);
|
||||
return <InstanceFailureView />;
|
||||
}
|
||||
|
||||
// if instance is fetched and setup is not done, show setup form
|
||||
if (instance && !instance?.is_setup_done) {
|
||||
return (
|
||||
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
|
||||
<InstanceSetupForm />
|
||||
</div>
|
||||
);
|
||||
return <InstanceSetupForm />;
|
||||
}
|
||||
|
||||
// if instance is fetched and setup is done, show sign in form
|
||||
return (
|
||||
<div className="flex-grow container mx-auto max-w-lg px-10 lg:max-w-md lg:px-5 py-10 lg:pt-28 transition-all">
|
||||
<div className="relative flex flex-col space-y-6">
|
||||
<div className="text-center space-y-1">
|
||||
<h3 className="flex gap-4 justify-center text-3xl font-bold text-onboarding-text-100">
|
||||
Manage your Plane instance
|
||||
</h3>
|
||||
<p className="font-medium text-onboarding-text-400">
|
||||
Configure instance-wide settings to secure your instance
|
||||
</p>
|
||||
</div>
|
||||
<InstanceSignInForm />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return <InstanceSignInForm />;
|
||||
};
|
||||
|
||||
export default observer(HomePage);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import { Button, Input, Spinner } from "@plane/ui";
|
|||
// components
|
||||
import { Banner } from "@/components/common/banner";
|
||||
// local components
|
||||
import { FormHeader } from "../../../core/components/instance/form-header";
|
||||
import { AuthBanner } from "./auth-banner";
|
||||
import { AuthHeader } from "./auth-header";
|
||||
import { authErrorHandler } from "./auth-helpers";
|
||||
|
||||
// service initialization
|
||||
|
|
@ -101,78 +103,91 @@ export const InstanceSignInForm: FC = () => {
|
|||
}, [errorCode]);
|
||||
|
||||
return (
|
||||
<form
|
||||
className="space-y-4"
|
||||
method="POST"
|
||||
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
|
||||
onSubmit={() => setIsSubmitting(true)}
|
||||
onError={() => setIsSubmitting(false)}
|
||||
>
|
||||
{errorData.type && errorData?.message ? (
|
||||
<Banner type="error" message={errorData?.message} />
|
||||
) : (
|
||||
<>{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}</>
|
||||
)}
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||
|
||||
<div className="w-full space-y-1">
|
||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="email">
|
||||
Email <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
inputSize="md"
|
||||
placeholder="name@company.com"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
autoComplete="on"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full space-y-1">
|
||||
<label className="text-sm text-onboarding-text-300 font-medium" htmlFor="password">
|
||||
Password <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
className="w-full border border-onboarding-border-100 !bg-onboarding-background-200 placeholder:text-onboarding-text-400"
|
||||
id="password"
|
||||
name="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
inputSize="md"
|
||||
placeholder="Enter your password"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleFormChange("password", e.target.value)}
|
||||
autoComplete="on"
|
||||
<>
|
||||
<AuthHeader />
|
||||
<div className="flex flex-col justify-center items-center flex-grow w-full py-6 mt-10">
|
||||
<div className="relative flex flex-col gap-6 max-w-[22.5rem] w-full">
|
||||
<FormHeader
|
||||
heading="Manage your Plane instance"
|
||||
subHeading="Configure instance-wide settings to secure your instance"
|
||||
/>
|
||||
{showPassword ? (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(false)}
|
||||
>
|
||||
<EyeOff className="h-4 w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(true)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
<form
|
||||
className="space-y-4"
|
||||
method="POST"
|
||||
action={`${API_BASE_URL}/api/instances/admins/sign-in/`}
|
||||
onSubmit={() => setIsSubmitting(true)}
|
||||
onError={() => setIsSubmitting(false)}
|
||||
>
|
||||
{errorData.type && errorData?.message ? (
|
||||
<Banner type="error" message={errorData?.message} />
|
||||
) : (
|
||||
<>
|
||||
{errorInfo && <AuthBanner bannerData={errorInfo} handleBannerData={(value) => setErrorInfo(value)} />}
|
||||
</>
|
||||
)}
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
|
||||
|
||||
<div className="w-full space-y-1">
|
||||
<label className="text-sm text-custom-text-300 font-medium" htmlFor="email">
|
||||
Email <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<Input
|
||||
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
inputSize="md"
|
||||
placeholder="name@company.com"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
autoComplete="on"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="w-full space-y-1">
|
||||
<label className="text-sm text-custom-text-300 font-medium" htmlFor="password">
|
||||
Password <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<Input
|
||||
className="w-full border border-custom-border-100 !bg-custom-background-100 placeholder:text-custom-text-400"
|
||||
id="password"
|
||||
name="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
inputSize="md"
|
||||
placeholder="Enter your password"
|
||||
value={formData.password}
|
||||
onChange={(e) => handleFormChange("password", e.target.value)}
|
||||
autoComplete="on"
|
||||
/>
|
||||
{showPassword ? (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(false)}
|
||||
>
|
||||
<EyeOff className="h-4 w-4" />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute right-3 top-3.5 flex items-center justify-center text-custom-text-400"
|
||||
onClick={() => setShowPassword(true)}
|
||||
>
|
||||
<Eye className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
|
||||
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className="py-2">
|
||||
<Button type="submit" size="lg" className="w-full" disabled={isButtonDisabled}>
|
||||
{isSubmitting ? <Spinner height="20px" width="20px" /> : "Sign in"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue