Implement signin as guest
This commit is contained in:
@@ -34,6 +34,7 @@
|
|||||||
"signin": "Sign in",
|
"signin": "Sign in",
|
||||||
"or_signup": "You need sign up?",
|
"or_signup": "You need sign up?",
|
||||||
"or_signin": "You need sign in?",
|
"or_signin": "You need sign in?",
|
||||||
|
"signin_as_guest": "Or sign in as Guest",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"username": "User name",
|
"username": "User name",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
"signin": "サインイン",
|
"signin": "サインイン",
|
||||||
"or_signup": "新規登録ページ",
|
"or_signup": "新規登録ページ",
|
||||||
"or_signin": "サインインページ",
|
"or_signin": "サインインページ",
|
||||||
|
"signin_as_guest": "ゲストとしてサインイン",
|
||||||
"email": "メールアドレス",
|
"email": "メールアドレス",
|
||||||
"username": "ユーザー名",
|
"username": "ユーザー名",
|
||||||
"password": "パスワード",
|
"password": "パスワード",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { TokenProps } from '@/types/user';
|
|||||||
import { useRouter, usePathname } from '@/src/navigation';
|
import { useRouter, usePathname } from '@/src/navigation';
|
||||||
|
|
||||||
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
|
const LOCAL_STORAGE_KEY = 'testplat-auth-token';
|
||||||
const privatePaths = ['/account'];
|
const privatePaths = ['/account', '/projects'];
|
||||||
|
|
||||||
function storeTokenToLocalStorage(token: TokenType) {
|
function storeTokenToLocalStorage(token: TokenType) {
|
||||||
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(token));
|
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(token));
|
||||||
@@ -31,6 +31,7 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
const [hasRestoreFinished, setHasRestoreFinished] = useState(false);
|
||||||
const [token, setToken] = useState<TokenType>({
|
const [token, setToken] = useState<TokenType>({
|
||||||
access_token: '',
|
access_token: '',
|
||||||
user: null,
|
user: null,
|
||||||
@@ -53,25 +54,33 @@ const TokenProvider = ({ locale, children }: TokenProps) => {
|
|||||||
if (tokenString) {
|
if (tokenString) {
|
||||||
const restoredToken = JSON.parse(tokenString);
|
const restoredToken = JSON.parse(tokenString);
|
||||||
setToken(restoredToken);
|
setToken(restoredToken);
|
||||||
} else {
|
|
||||||
checkSignInPage();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkSignInPage = () => {
|
|
||||||
if (
|
|
||||||
privatePaths.some((path) => {
|
|
||||||
return path === pathname;
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
router.push(`/account/signin`, { locale: locale });
|
|
||||||
}
|
}
|
||||||
|
setHasRestoreFinished(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
restoreTokenFromLocalStorage();
|
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>;
|
return <TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { UserType } from '@/types/user';
|
import { UserType } from '@/types/user';
|
||||||
import Config from '@/config/config';
|
import Config from '@/config/config';
|
||||||
|
import { roles } from '@/config/selection';
|
||||||
const apiServer = Config.apiServer;
|
const apiServer = Config.apiServer;
|
||||||
|
|
||||||
async function signUp(newUser: UserType) {
|
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() {
|
function getRandomAvatarPath() {
|
||||||
const randomIndex = Math.floor(Math.random() * avatars.length);
|
const randomIndex = Math.floor(Math.random() * avatars.length);
|
||||||
return avatars[randomIndex];
|
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 { ChevronRight, Eye, EyeOff } from 'lucide-react';
|
||||||
import { UserType, AuthMessages } from '@/types/user';
|
import { UserType, AuthMessages } from '@/types/user';
|
||||||
import { roles } from '@/config/selection';
|
import { roles } from '@/config/selection';
|
||||||
import { signUp, signIn } from './authControl';
|
import { signUp, signIn, signInAsGuest } from './authControl';
|
||||||
import { isValidEmail, isValidPassword } from './validate';
|
import { isValidEmail, isValidPassword } from './validate';
|
||||||
import { TokenContext } from '../TokenProvider';
|
import { TokenContext } from '../TokenProvider';
|
||||||
import { useRouter } from '@/src/navigation';
|
import { useRouter } from '@/src/navigation';
|
||||||
@@ -83,6 +83,13 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
|
|||||||
router.push('/account', { locale: locale });
|
router.push('/account', { locale: locale });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleSignInAsGuest = async () => {
|
||||||
|
const token = await signInAsGuest();
|
||||||
|
context.setToken(token);
|
||||||
|
context.storeTokenToLocalStorage(token);
|
||||||
|
router.push('/account', { locale: locale });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="w-[480px] mt-16">
|
<Card className="w-[480px] mt-16">
|
||||||
<CardHeader className="px-4 pt-4 pb-0 flex justify-between">
|
<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">
|
<div className="flex justify-end items-center mt-3">
|
||||||
<Button color="primary" className="mt-3" onPress={validate}>
|
<Button color="primary" onPress={validate}>
|
||||||
{messages.submitTitle}
|
{messages.submitTitle}
|
||||||
</Button>
|
</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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</CardBody>
|
</CardBody>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default function Page(params: { locale: string }) {
|
|||||||
title: t('signin'),
|
title: t('signin'),
|
||||||
linkTitle: t('or_signup'),
|
linkTitle: t('or_signup'),
|
||||||
submitTitle: t('signin'),
|
submitTitle: t('signin'),
|
||||||
|
signInAsGuest: t('signin_as_guest'),
|
||||||
email: t('email'),
|
email: t('email'),
|
||||||
username: t('username'),
|
username: t('username'),
|
||||||
password: t('password'),
|
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 { ProjectType, ProjectsMessages } from '@/types/project';
|
||||||
import ProjectsTable from './ProjectsTable';
|
import ProjectsTable from './ProjectsTable';
|
||||||
import ProjectDialog from './ProjectDialog';
|
import ProjectDialog from './ProjectDialog';
|
||||||
import NeedSignedInDialog from './NeedSignedInDialog';
|
|
||||||
import { fetchProjects, createProject, updateProject, deleteProject } from './projectsControl';
|
import { fetchProjects, createProject, updateProject, deleteProject } from './projectsControl';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
@@ -35,15 +34,9 @@ export default function ProjectsPage({ messages, locale }: Props) {
|
|||||||
}, [context]);
|
}, [context]);
|
||||||
|
|
||||||
// dialog
|
// dialog
|
||||||
const [isNeedSignedInDialogOpen, setIsNeedSignedInDialogOpen] = useState(false);
|
|
||||||
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false);
|
const [isProjectDialogOpen, setIsProjectDialogOpen] = useState(false);
|
||||||
const [editingProject, setEditingProject] = useState<ProjectType | null>(null);
|
const [editingProject, setEditingProject] = useState<ProjectType | null>(null);
|
||||||
const openDialogForCreate = () => {
|
const openDialogForCreate = () => {
|
||||||
if (!context.isSignedIn()) {
|
|
||||||
setIsNeedSignedInDialogOpen(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsProjectDialogOpen(true);
|
setIsProjectDialogOpen(true);
|
||||||
setEditingProject(null);
|
setEditingProject(null);
|
||||||
};
|
};
|
||||||
@@ -105,13 +98,6 @@ export default function ProjectsPage({ messages, locale }: Props) {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NeedSignedInDialog
|
|
||||||
isOpen={isNeedSignedInDialogOpen}
|
|
||||||
onCancel={() => setIsNeedSignedInDialogOpen(false)}
|
|
||||||
messages={messages}
|
|
||||||
locale={locale}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export type AuthMessages = {
|
|||||||
title: string;
|
title: string;
|
||||||
linkTitle: string;
|
linkTitle: string;
|
||||||
submitTitle: string;
|
submitTitle: string;
|
||||||
|
signInAsGuest: string;
|
||||||
email: string;
|
email: string;
|
||||||
username: string;
|
username: string;
|
||||||
password: string;
|
password: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user