Skip to content

Commit

Permalink
💄 style: Add login ui for next-auth (#6434)
Browse files Browse the repository at this point in the history
  • Loading branch information
cy948 authored Mar 8, 2025
1 parent 3fb966c commit 541f275
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 8 deletions.
161 changes: 161 additions & 0 deletions src/app/[variants]/(auth)/next-auth/signin/AuthSignInBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
'use client';

import { LobeChat } from '@lobehub/ui/brand';
import { Button, Col, Flex, Row, Skeleton, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { AuthError } from 'next-auth';
import { signIn } from 'next-auth/react';
import { useRouter, useSearchParams } from 'next/navigation';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';

import BrandWatermark from '@/components/BrandWatermark';
import AuthIcons from '@/components/NextAuth/AuthIcons';
import { DOCUMENTS_REFER_URL, PRIVACY_URL, TERMS_URL } from '@/const/url';
import { useUserStore } from '@/store/user';

const { Title, Paragraph } = Typography;

const useStyles = createStyles(({ css, token }) => ({
button: css`
text-transform: capitalize;
`,
container: css`
min-width: 360px;
border: 1px solid ${token.colorBorder};
border-radius: ${token.borderRadiusLG}px;
background: ${token.colorBgContainer};
`,
contentCard: css`
padding-block: 2.5rem;
padding-inline: 2rem;
`,
description: css`
margin: 0;
color: ${token.colorTextSecondary};
`,
footer: css`
padding: 1rem;
border-block-start: 1px solid ${token.colorBorder};
border-radius: 0 0 8px 8px;
color: ${token.colorTextDescription};
background: ${token.colorBgElevated};
`,
text: css`
text-align: center;
`,
title: css`
margin: 0;
color: ${token.colorTextHeading};
`,
}));

const BtnListLoading = memo(() => {
return (
<Flex gap={'small'} vertical>
<Skeleton.Button active style={{ minWidth: 300 }} />
<Skeleton.Button active style={{ minWidth: 300 }} />
<Skeleton.Button active style={{ minWidth: 300 }} />
</Flex>
);
});

/**
* Follow the implementation from AuthJS official documentation,
* but using client components.
* ref: https://authjs.dev/guides/pages/signin
*/
export default memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('clerk');
const router = useRouter();

const oAuthSSOProviders = useUserStore((s) => s.oAuthSSOProviders);

const searchParams = useSearchParams();

// Redirect back to the page url
const callbackUrl = searchParams.get('callbackUrl') ?? '';

const handleSignIn = async (provider: string) => {
try {
await signIn(provider, { redirectTo: callbackUrl });
} catch (error) {
// Signin can fail for a number of reasons, such as the user
// not existing, or the user not having the correct role.
// In some cases, you may want to redirect to a custom error
if (error instanceof AuthError) {
return router.push(`/next-auth/?error=${error.type}`);
}

// Otherwise if a redirects happens Next.js can handle it
// so you can just re-thrown the error and let Next.js handle it.
// Docs: https://nextjs.org/docs/app/api-reference/functions/redirect#server-component
throw error;
}
};

const footerBtns = [
{ href: DOCUMENTS_REFER_URL, id: 0, label: t('footerPageLink__help') },
{ href: PRIVACY_URL, id: 1, label: t('footerPageLink__privacy') },
{ href: TERMS_URL, id: 2, label: t('footerPageLink__terms') },
];

return (
<div className={styles.container}>
<div className={styles.contentCard}>
{/* Card Body */}
<Flex gap="large" vertical>
{/* Header */}
<div className={styles.text}>
<Title className={styles.title} level={4}>
<div>
<LobeChat size={48} />
</div>
{t('signIn.start.title', { applicationName: 'LobeChat' })}
</Title>
<Paragraph className={styles.description}>{t('signIn.start.subtitle')}</Paragraph>
</div>
{/* Content */}
<Flex gap="small" vertical>
{oAuthSSOProviders ? (
oAuthSSOProviders.map((provider) => (
<Button
className={styles.button}
icon={AuthIcons(provider, 16)}
key={provider}
onClick={() => handleSignIn(provider)}
>
{provider}
</Button>
))
) : (
<BtnListLoading />
)}
</Flex>
</Flex>
</div>
<div className={styles.footer}>
{/* Footer */}
<Row>
<Col span={12}>
<Flex justify="left" style={{ height: '100%' }}>
<BrandWatermark />
</Flex>
</Col>
<Col offset={4} span={8}>
<Flex justify="right">
{footerBtns.map((btn) => (
<Button key={btn.id} onClick={() => router.push(btn.href)} size="small" type="text">
{btn.label}
</Button>
))}
</Flex>
</Col>
</Row>
</div>
</div>
);
});
11 changes: 11 additions & 0 deletions src/app/[variants]/(auth)/next-auth/signin/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Suspense } from 'react';

import Loading from '@/components/Loading/BrandTextLoading';

import AuthSignInBox from './AuthSignInBox';

export default () => (
<Suspense fallback={<Loading />}>
<AuthSignInBox />
</Suspense>
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { userService } from '@/services/user';
import { useUserStore } from '@/store/user';
import { userProfileSelectors } from '@/store/user/selectors';

import AuthIcons from './AuthIcons';
import AuthIcons from '@/components/NextAuth/AuthIcons';

const { Item } = List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ import {
} from '@lobehub/ui/icons';
import React from 'react';

const iconProps = {
size: 32,
};

const iconComponents: { [key: string]: React.ElementType } = {
'auth0': Auth0,
'authelia': Authelia.Color,
Expand All @@ -29,9 +25,15 @@ const iconComponents: { [key: string]: React.ElementType } = {
'zitadel': Zitadel.Color,
};

const AuthIcons = (id: string) => {
/**
* Get the auth icons component for the given id
* @param id
* @param size default is 36
* @returns
*/
const AuthIcons = (id: string, size = 36) => {
const IconComponent = iconComponents[id] || iconComponents.default;
return <IconComponent {...iconProps} />;
return <IconComponent size={size}/>;
};

export default AuthIcons;
1 change: 1 addition & 0 deletions src/libs/next-auth/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default {
debug: authEnv.NEXT_AUTH_DEBUG,
pages: {
error: '/next-auth/error',
signIn: '/next-auth/signin',
},
providers: initSSOProviders(),
secret: authEnv.NEXT_AUTH_SECRET,
Expand Down
2 changes: 1 addition & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const config = {

'/login(.*)',
'/signup(.*)',
'/next-auth/error',
'/next-auth/(.*)',
// ↓ cloud ↓
],
};
Expand Down

0 comments on commit 541f275

Please sign in to comment.