mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-02 08:46:38 +00:00
clean up login page
This commit is contained in:
@@ -1135,7 +1135,7 @@
|
|||||||
"create": "Create",
|
"create": "Create",
|
||||||
"orgs": "Organizations",
|
"orgs": "Organizations",
|
||||||
"loginError": "An error occurred while logging in",
|
"loginError": "An error occurred while logging in",
|
||||||
"loginRequiredForDevice": "Login is required to authenticate your device.",
|
"loginRequiredForDevice": "Login is required for your device.",
|
||||||
"passwordForgot": "Forgot your password?",
|
"passwordForgot": "Forgot your password?",
|
||||||
"otpAuth": "Two-Factor Authentication",
|
"otpAuth": "Two-Factor Authentication",
|
||||||
"otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.",
|
"otpAuthDescription": "Enter the code from your authenticator app or one of your single-use backup codes.",
|
||||||
@@ -1876,7 +1876,7 @@
|
|||||||
"orgAuthChooseIdpDescription": "Choose your identity provider to continue",
|
"orgAuthChooseIdpDescription": "Choose your identity provider to continue",
|
||||||
"orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.",
|
"orgAuthNoIdpConfigured": "This organization doesn't have any identity providers configured. You can log in with your Pangolin identity instead.",
|
||||||
"orgAuthSignInWithPangolin": "Sign in with Pangolin",
|
"orgAuthSignInWithPangolin": "Sign in with Pangolin",
|
||||||
"orgAuthSignInToOrg": "Sign in to an organization",
|
"orgAuthSignInToOrg": "Use organization's identity provider",
|
||||||
"orgAuthSelectOrgTitle": "Organization Sign In",
|
"orgAuthSelectOrgTitle": "Organization Sign In",
|
||||||
"orgAuthSelectOrgDescription": "Enter your organization ID to continue",
|
"orgAuthSelectOrgDescription": "Enter your organization ID to continue",
|
||||||
"orgAuthOrgIdPlaceholder": "your-organization",
|
"orgAuthOrgIdPlaceholder": "your-organization",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default async function AuthLayout({ children }: AuthLayoutProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col">
|
<div className="h-full flex flex-col">
|
||||||
<div className="flex justify-end items-center p-3 space-x-2">
|
<div className="hidden md:flex justify-end items-center p-3 space-x-2">
|
||||||
<ThemeSwitcher />
|
<ThemeSwitcher />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -127,26 +127,6 @@ export default async function AuthLayout({ children }: AuthLayoutProps) {
|
|||||||
</a>
|
</a>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Separator orientation="vertical" />
|
|
||||||
<a
|
|
||||||
href="https://docs.pangolin.net"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label="Built by Fossorial"
|
|
||||||
className="flex items-center space-x-2 whitespace-nowrap"
|
|
||||||
>
|
|
||||||
<span>{t("docs")}</span>
|
|
||||||
</a>
|
|
||||||
<Separator orientation="vertical" />
|
|
||||||
<a
|
|
||||||
href="https://github.com/fosrl/pangolin"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
aria-label="GitHub"
|
|
||||||
className="flex items-center space-x-2 whitespace-nowrap"
|
|
||||||
>
|
|
||||||
<span>{t("github")}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ export default async function Page(props: {
|
|||||||
redirect={redirectUrl}
|
redirect={redirectUrl}
|
||||||
idps={loginIdps}
|
idps={loginIdps}
|
||||||
forceLogin={forceLogin}
|
forceLogin={forceLogin}
|
||||||
|
showOrgLogin={
|
||||||
|
!isInvite && (build === "saas" || env.flags.useOrgOnlyIdp)
|
||||||
|
}
|
||||||
|
searchParams={searchParams}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(!signUpDisabled || isInvite) && (
|
{(!signUpDisabled || isInvite) && (
|
||||||
@@ -120,35 +124,6 @@ export default async function Page(props: {
|
|||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isInvite && (build === "saas" || env.flags.useOrgOnlyIdp) ? (
|
|
||||||
<div className="text-center text-muted-foreground mt-12 flex flex-col items-center">
|
|
||||||
<span>{t("needToSignInToOrg")}</span>
|
|
||||||
<Link
|
|
||||||
href={`/auth/org${buildQueryString(searchParams)}`}
|
|
||||||
className="underline"
|
|
||||||
>
|
|
||||||
{t("orgAuthSignInToOrg")}
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildQueryString(searchParams: {
|
|
||||||
[key: string]: string | string[] | undefined;
|
|
||||||
}): string {
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
const redirect = searchParams.redirect;
|
|
||||||
const forceLogin = searchParams.forceLogin;
|
|
||||||
|
|
||||||
if (redirect && typeof redirect === "string") {
|
|
||||||
params.set("redirect", redirect);
|
|
||||||
}
|
|
||||||
if (forceLogin && typeof forceLogin === "string") {
|
|
||||||
params.set("forceLogin", forceLogin);
|
|
||||||
}
|
|
||||||
const queryString = params.toString();
|
|
||||||
return queryString ? `?${queryString}` : "";
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,17 +17,26 @@ import { cleanRedirect } from "@app/lib/cleanRedirect";
|
|||||||
import BrandingLogo from "@app/components/BrandingLogo";
|
import BrandingLogo from "@app/components/BrandingLogo";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
|
import { useLicenseStatusContext } from "@app/hooks/useLicenseStatusContext";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { Button } from "./ui/button";
|
||||||
|
import { ArrowRight } from "lucide-react";
|
||||||
|
|
||||||
type DashboardLoginFormProps = {
|
type DashboardLoginFormProps = {
|
||||||
redirect?: string;
|
redirect?: string;
|
||||||
idps?: LoginFormIDP[];
|
idps?: LoginFormIDP[];
|
||||||
forceLogin?: boolean;
|
forceLogin?: boolean;
|
||||||
|
showOrgLogin?: boolean;
|
||||||
|
searchParams?: {
|
||||||
|
[key: string]: string | string[] | undefined;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DashboardLoginForm({
|
export default function DashboardLoginForm({
|
||||||
redirect,
|
redirect,
|
||||||
idps,
|
idps,
|
||||||
forceLogin
|
forceLogin,
|
||||||
|
showOrgLogin,
|
||||||
|
searchParams
|
||||||
}: DashboardLoginFormProps) {
|
}: DashboardLoginFormProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { env } = useEnvContext();
|
const { env } = useEnvContext();
|
||||||
@@ -35,6 +44,9 @@ export default function DashboardLoginForm({
|
|||||||
const { isUnlocked } = useLicenseStatusContext();
|
const { isUnlocked } = useLicenseStatusContext();
|
||||||
|
|
||||||
function getSubtitle() {
|
function getSubtitle() {
|
||||||
|
if (forceLogin) {
|
||||||
|
return t("loginRequiredForDevice");
|
||||||
|
}
|
||||||
if (isUnlocked() && env.branding?.loginPage?.subtitleText) {
|
if (isUnlocked() && env.branding?.loginPage?.subtitleText) {
|
||||||
return env.branding.loginPage.subtitleText;
|
return env.branding.loginPage.subtitleText;
|
||||||
}
|
}
|
||||||
@@ -57,6 +69,22 @@ export default function DashboardLoginForm({
|
|||||||
<div className="text-center space-y-1 pt-3">
|
<div className="text-center space-y-1 pt-3">
|
||||||
<p className="text-muted-foreground">{getSubtitle()}</p>
|
<p className="text-muted-foreground">{getSubtitle()}</p>
|
||||||
</div>
|
</div>
|
||||||
|
{showOrgLogin && (
|
||||||
|
<div className="space-y-2 mt-4">
|
||||||
|
<Link
|
||||||
|
href={`/auth/org${buildQueryString(searchParams || {})}`}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
className="w-full gap-2"
|
||||||
|
>
|
||||||
|
{t("orgAuthSignInToOrg")}
|
||||||
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-6">
|
<CardContent className="pt-6">
|
||||||
<LoginForm
|
<LoginForm
|
||||||
@@ -76,3 +104,20 @@ export default function DashboardLoginForm({
|
|||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildQueryString(searchParams: {
|
||||||
|
[key: string]: string | string[] | undefined;
|
||||||
|
}): string {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
const redirect = searchParams.redirect;
|
||||||
|
const forceLogin = searchParams.forceLogin;
|
||||||
|
|
||||||
|
if (redirect && typeof redirect === "string") {
|
||||||
|
params.set("redirect", redirect);
|
||||||
|
}
|
||||||
|
if (forceLogin && typeof forceLogin === "string") {
|
||||||
|
params.set("forceLogin", forceLogin);
|
||||||
|
}
|
||||||
|
const queryString = params.toString();
|
||||||
|
return queryString ? `?${queryString}` : "";
|
||||||
|
}
|
||||||
|
|||||||
@@ -409,15 +409,6 @@ export default function LoginForm({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{forceLogin && (
|
|
||||||
<Alert variant="neutral">
|
|
||||||
<AlertDescription className="flex items-center gap-2">
|
|
||||||
<LockIcon className="w-4 h-4" />
|
|
||||||
{t("loginRequiredForDevice")}
|
|
||||||
</AlertDescription>
|
|
||||||
</Alert>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{showSecurityKeyPrompt && (
|
{showSecurityKeyPrompt && (
|
||||||
<Alert>
|
<Alert>
|
||||||
<FingerprintIcon className="w-5 h-5 mr-2" />
|
<FingerprintIcon className="w-5 h-5 mr-2" />
|
||||||
|
|||||||
Reference in New Issue
Block a user