diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 9c5ca3b..34849dc 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -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", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index adf0474..9ebf022 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -34,6 +34,7 @@ "signin": "サインイン", "or_signup": "新規登録ページ", "or_signin": "サインインページ", + "signin_as_guest": "ゲストとしてサインイン", "email": "メールアドレス", "username": "ユーザー名", "password": "パスワード", diff --git a/frontend/src/app/[locale]/TokenProvider.tsx b/frontend/src/app/[locale]/TokenProvider.tsx index 25ce3c9..fce7ca4 100644 --- a/frontend/src/app/[locale]/TokenProvider.tsx +++ b/frontend/src/app/[locale]/TokenProvider.tsx @@ -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({ 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 {children}; }; diff --git a/frontend/src/app/[locale]/account/authControl.ts b/frontend/src/app/[locale]/account/authControl.ts index 01392c9..2742cd8 100644 --- a/frontend/src/app/[locale]/account/authControl.ts +++ b/frontend/src/app/[locale]/account/authControl.ts @@ -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 }; diff --git a/frontend/src/app/[locale]/account/authPage.tsx b/frontend/src/app/[locale]/account/authPage.tsx index bad45b7..a6db86e 100644 --- a/frontend/src/app/[locale]/account/authPage.tsx +++ b/frontend/src/app/[locale]/account/authPage.tsx @@ -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 ( @@ -159,10 +166,18 @@ export default function AuthPage({ isSignup, messages, locale }: Props) { }} /> )} -
- + {!isSignup && ( + + )}
diff --git a/frontend/src/app/[locale]/account/signin/page.tsx b/frontend/src/app/[locale]/account/signin/page.tsx index a516d2e..863df8f 100644 --- a/frontend/src/app/[locale]/account/signin/page.tsx +++ b/frontend/src/app/[locale]/account/signin/page.tsx @@ -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'), diff --git a/frontend/src/app/[locale]/projects/NeedSignedInDialog.tsx b/frontend/src/app/[locale]/projects/NeedSignedInDialog.tsx deleted file mode 100644 index 91ebb07..0000000 --- a/frontend/src/app/[locale]/projects/NeedSignedInDialog.tsx +++ /dev/null @@ -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 ( - { - onCancel(); - }} - > - - {messages.needSignedIn} - - - - - - - - ); -} diff --git a/frontend/src/app/[locale]/projects/ProjectsPage.tsx b/frontend/src/app/[locale]/projects/ProjectsPage.tsx index 2a24871..3323136 100644 --- a/frontend/src/app/[locale]/projects/ProjectsPage.tsx +++ b/frontend/src/app/[locale]/projects/ProjectsPage.tsx @@ -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(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} /> - - setIsNeedSignedInDialogOpen(false)} - messages={messages} - locale={locale} - /> ); } diff --git a/frontend/types/user.ts b/frontend/types/user.ts index 067fd16..10bcc98 100644 --- a/frontend/types/user.ts +++ b/frontend/types/user.ts @@ -32,6 +32,7 @@ export type AuthMessages = { title: string; linkTitle: string; submitTitle: string; + signInAsGuest: string; email: string; username: string; password: string;