Implement signin as guest
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
"signin": "Sign in",
|
||||
"or_signup": "You need sign up?",
|
||||
"or_signin": "You need sign in?",
|
||||
"signin_as_guest": "Or sign in as Guest",
|
||||
"email": "Email",
|
||||
"username": "User name",
|
||||
"password": "Password",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"signin": "サインイン",
|
||||
"or_signup": "新規登録ページ",
|
||||
"or_signin": "サインインページ",
|
||||
"signin_as_guest": "ゲストとしてサインイン",
|
||||
"email": "メールアドレス",
|
||||
"username": "ユーザー名",
|
||||
"password": "パスワード",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TokenProps } from '@/types/user';
|
||||
import { useRouter, usePathname } from '@/src/navigation';
|
||||
|
||||
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
|
||||
const privatePaths = ['/account'];
|
||||
const privatePaths = ['/account', '/projects'];
|
||||
|
||||
function storeTokenToLocalStorage(token: TokenType) {
|
||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(token));
|
||||
@@ -31,6 +31,7 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const [hasRestoreFinished, setHasRestoreFinished] = useState(false);
|
||||
const [token, setToken] = useState<TokenType>({
|
||||
access_token: '',
|
||||
user: null,
|
||||
@@ -53,25 +54,33 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
|
||||
if (tokenString) {
|
||||
const restoredToken = JSON.parse(tokenString);
|
||||
setToken(restoredToken);
|
||||
} else {
|
||||
checkSignInPage();
|
||||
}
|
||||
};
|
||||
|
||||
const checkSignInPage = () => {
|
||||
if (
|
||||
privatePaths.some((path) => {
|
||||
return path === pathname;
|
||||
})
|
||||
) {
|
||||
router.push(`/account/signin`, { locale: locale });
|
||||
}
|
||||
setHasRestoreFinished(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
restoreTokenFromLocalStorage();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const isPrivatePath = () => {
|
||||
return privatePaths.some((path) => {
|
||||
return path === pathname;
|
||||
});
|
||||
};
|
||||
|
||||
const checkSignInPage = () => {
|
||||
if (!hasRestoreFinished) {
|
||||
return;
|
||||
}
|
||||
if (isPrivatePath() && !isSignedIn()) {
|
||||
router.push(`/account/signin`, { locale: locale });
|
||||
}
|
||||
};
|
||||
|
||||
checkSignInPage();
|
||||
}, [pathname, hasRestoreFinished]);
|
||||
|
||||
return <TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { UserType } from '@/types/user';
|
||||
import Config from '@/config/config';
|
||||
import { roles } from '@/config/selection';
|
||||
const apiServer = Config.apiServer;
|
||||
|
||||
async function signUp(newUser: UserType) {
|
||||
@@ -50,9 +51,35 @@ async function signIn(signInUser: UserType) {
|
||||
}
|
||||
}
|
||||
|
||||
async function signInAsGuest() {
|
||||
const guestUser = {
|
||||
id: null,
|
||||
email: generateRandomEmail(),
|
||||
password: 'guestpassword',
|
||||
username: 'Guest',
|
||||
role: roles.findIndex((entry) => entry.uid === 'user'),
|
||||
avatarPath: '',
|
||||
};
|
||||
const token = await signUp(guestUser);
|
||||
return token;
|
||||
}
|
||||
|
||||
function generateRandomEmail() {
|
||||
const randomStringLength = 10;
|
||||
const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
let randomString = '';
|
||||
const charactersLength = characters.length;
|
||||
|
||||
for (let i = 0; i < randomStringLength; i++) {
|
||||
randomString += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
}
|
||||
|
||||
return `${randomString}@example.com`;
|
||||
}
|
||||
|
||||
function getRandomAvatarPath() {
|
||||
const randomIndex = Math.floor(Math.random() * avatars.length);
|
||||
return avatars[randomIndex];
|
||||
}
|
||||
|
||||
export { signUp, signIn, getRandomAvatarPath };
|
||||
export { signUp, signIn, signInAsGuest, getRandomAvatarPath };
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Link } from '@/src/navigation';
|
||||
import { ChevronRight, Eye, EyeOff } from 'lucide-react';
|
||||
import { UserType, AuthMessages } from '@/types/user';
|
||||
import { roles } from '@/config/selection';
|
||||
import { signUp, signIn } from './authControl';
|
||||
import { signUp, signIn, signInAsGuest } from './authControl';
|
||||
import { isValidEmail, isValidPassword } from './validate';
|
||||
import { TokenContext } from '../TokenProvider';
|
||||
import { useRouter } from '@/src/navigation';
|
||||
@@ -83,6 +83,13 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
|
||||
router.push('/account', { locale: locale });
|
||||
};
|
||||
|
||||
const handleSignInAsGuest = async () => {
|
||||
const token = await signInAsGuest();
|
||||
context.setToken(token);
|
||||
context.storeTokenToLocalStorage(token);
|
||||
router.push('/account', { locale: locale });
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-[480px] mt-16">
|
||||
<CardHeader className="px-4 pt-4 pb-0 flex justify-between">
|
||||
@@ -159,10 +166,18 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div className="flex justify-end">
|
||||
<Button color="primary" className="mt-3" onPress={validate}>
|
||||
<div className="flex justify-end items-center mt-3">
|
||||
<Button color="primary" onPress={validate}>
|
||||
{messages.submitTitle}
|
||||
</Button>
|
||||
{!isSignup && (
|
||||
<Button
|
||||
className="ms-3 bg-gradient-to-tr from-pink-500 to-yellow-500 text-white shadow-lg"
|
||||
onPress={handleSignInAsGuest}
|
||||
>
|
||||
{messages.signInAsGuest}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</CardBody>
|
||||
|
||||
@@ -7,6 +7,7 @@ export default function Page(params: { locale: string }) {
|
||||
title: t('signin'),
|
||||
linkTitle: t('or_signup'),
|
||||
submitTitle: t('signin'),
|
||||
signInAsGuest: t('signin_as_guest'),
|
||||
email: t('email'),
|
||||
username: t('username'),
|
||||
password: t('password'),
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
'use client';
|
||||
import { ProjectsMessages } from '@/types/project';
|
||||
import { Link } from '@/src/navigation';
|
||||
import { Button, Modal, ModalContent, ModalHeader, ModalFooter } from '@nextui-org/react';
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onCancel: () => void;
|
||||
messages: ProjectsMessages;
|
||||
locale: string;
|
||||
};
|
||||
|
||||
export default function NeedSignedInDialog({ isOpen, onCancel, messages, locale }: Props) {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={() => {
|
||||
onCancel();
|
||||
}}
|
||||
>
|
||||
<ModalContent>
|
||||
<ModalHeader className="flex flex-col gap-1">{messages.needSignedIn}</ModalHeader>
|
||||
<ModalFooter>
|
||||
<Link href={'/account/signin'} locale={locale}>
|
||||
<Button color="primary">{messages.signIn}</Button>
|
||||
</Link>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import { TokenContext } from '@/src/app/[locale]/TokenProvider';
|
||||
import { ProjectType, ProjectsMessages } from '@/types/project';
|
||||
import ProjectsTable from './ProjectsTable';
|
||||
import ProjectDialog from './ProjectDialog';
|
||||
import NeedSignedInDialog from './NeedSignedInDialog';
|
||||
import { fetchProjects, createProject, updateProject, deleteProject } from './projectsControl';
|
||||
|
||||
export type Props = {
|
||||
@@ -35,15 +34,9 @@ export default function ProjectsPage({ messages, locale }: Props) {
|
||||
}, [context]);
|
||||
|
||||
// dialog
|
||||
const [isNeedSignedInDialogOpen, setIsNeedSignedInDialogOpen] = useState(false);
|
||||
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false);
|
||||
const [editingProject, setEditingProject] = useState<ProjectType | null>(null);
|
||||
const openDialogForCreate = () => {
|
||||
if (!context.isSignedIn()) {
|
||||
setIsNeedSignedInDialogOpen(true);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsProjectDialogOpen(true);
|
||||
setEditingProject(null);
|
||||
};
|
||||
@@ -105,13 +98,6 @@ export default function ProjectsPage({ messages, locale }: Props) {
|
||||
onSubmit={onSubmit}
|
||||
messages={messages}
|
||||
/>
|
||||
|
||||
<NeedSignedInDialog
|
||||
isOpen={isNeedSignedInDialogOpen}
|
||||
onCancel={() => setIsNeedSignedInDialogOpen(false)}
|
||||
messages={messages}
|
||||
locale={locale}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ export type AuthMessages = {
|
||||
title: string;
|
||||
linkTitle: string;
|
||||
submitTitle: string;
|
||||
signInAsGuest: string;
|
||||
email: string;
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
Reference in New Issue
Block a user