Implement Auth provide user info
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
"projects": "Projects"
|
"projects": "Projects"
|
||||||
},
|
},
|
||||||
"Auth": {
|
"Auth": {
|
||||||
|
"account": "Account",
|
||||||
"signup": "Sign up",
|
"signup": "Sign up",
|
||||||
"signin": "Sign in",
|
"signin": "Sign in",
|
||||||
"or_signup": "You need sign up?",
|
"or_signup": "You need sign up?",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
"projects": "プロジェクト"
|
"projects": "プロジェクト"
|
||||||
},
|
},
|
||||||
"Auth": {
|
"Auth": {
|
||||||
|
"account": "アカウント",
|
||||||
"signup": "新規登録",
|
"signup": "新規登録",
|
||||||
"signin": "サインイン",
|
"signin": "サインイン",
|
||||||
"or_signup": "新規登録ページ",
|
"or_signup": "新規登録ページ",
|
||||||
|
|||||||
64
frontend/src/app/[locale]/DropdownAccount.tsx
Normal file
64
frontend/src/app/[locale]/DropdownAccount.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
"use client";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
DropdownTrigger,
|
||||||
|
Dropdown,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownItem,
|
||||||
|
} from "@nextui-org/react";
|
||||||
|
import { User, ChevronDown } from "lucide-react";
|
||||||
|
import { useContext } from "react";
|
||||||
|
import { TokenContext } from "./TokenProvider";
|
||||||
|
import { Link } from "@/src/navigation";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
locale: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const signinItems = [
|
||||||
|
{ uid: "account", title: "Account", href: "/account" },
|
||||||
|
{ uid: "signout", title: "Sign out", href: "/account/signout" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const signoutItems = [
|
||||||
|
{ uid: "signin", title: "Sign in", href: "/account/signin" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function LangSwitch({ locale }: Props) {
|
||||||
|
const context = useContext(TokenContext);
|
||||||
|
return (
|
||||||
|
<Dropdown>
|
||||||
|
<DropdownTrigger>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="faded"
|
||||||
|
startContent={<User size={16} />}
|
||||||
|
endContent={<ChevronDown size={16} />}
|
||||||
|
>
|
||||||
|
{context.token ? context.token.user.username : "SignIn"}
|
||||||
|
</Button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
{context.token ? (
|
||||||
|
<DropdownMenu aria-label="account actions when sign in">
|
||||||
|
{signinItems.map((entry) => (
|
||||||
|
<DropdownItem key={entry.uid}>
|
||||||
|
<Link href={entry.href} locale={locale}>
|
||||||
|
{entry.title}
|
||||||
|
</Link>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenu>
|
||||||
|
) : (
|
||||||
|
<DropdownMenu aria-label="account actions when sign out">
|
||||||
|
{signoutItems.map((entry) => (
|
||||||
|
<DropdownItem key={entry.uid}>
|
||||||
|
<Link href={entry.href} locale={locale}>
|
||||||
|
{entry.title}
|
||||||
|
</Link>
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenu>
|
||||||
|
)}
|
||||||
|
</Dropdown>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import LangSwitch from "./LangSwitch";
|
|||||||
import { ThemeSwitch } from "@/components/theme-switch";
|
import { ThemeSwitch } from "@/components/theme-switch";
|
||||||
import { GithubIcon } from "@/components/icons";
|
import { GithubIcon } from "@/components/icons";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import DropdownAccount from "./DropdownAccount";
|
||||||
|
|
||||||
export default function Header(params: { locale: string }) {
|
export default function Header(params: { locale: string }) {
|
||||||
const t = useTranslations("Header");
|
const t = useTranslations("Header");
|
||||||
@@ -102,6 +103,7 @@ export default function Header(params: { locale: string }) {
|
|||||||
</NextUiLink>
|
</NextUiLink>
|
||||||
<ThemeSwitch />
|
<ThemeSwitch />
|
||||||
<LangSwitch locale={params.locale} />
|
<LangSwitch locale={params.locale} />
|
||||||
|
<DropdownAccount locale={params.locale} />
|
||||||
<NavbarMenuToggle className="md:hidden" />
|
<NavbarMenuToggle className="md:hidden" />
|
||||||
</NavbarContent>
|
</NavbarContent>
|
||||||
|
|
||||||
|
|||||||
25
frontend/src/app/[locale]/TokenProvider.tsx
Normal file
25
frontend/src/app/[locale]/TokenProvider.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"use client";
|
||||||
|
import { createContext, useState } from "react";
|
||||||
|
import { TokenType, UserType } from "@/types/user";
|
||||||
|
import { TokenProps } from "@/types/user";
|
||||||
|
|
||||||
|
const defaultTokenContext = {
|
||||||
|
token: null,
|
||||||
|
setUser: (token: TokenType) => {},
|
||||||
|
};
|
||||||
|
const TokenContext = createContext(defaultTokenContext);
|
||||||
|
|
||||||
|
const TokenProvider = ({ children }: TokenProps) => {
|
||||||
|
const [token, setToken] = useState<TokenType>(null);
|
||||||
|
const tokenContext = {
|
||||||
|
token,
|
||||||
|
setToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TokenContext.Provider value={tokenContext}>{children}</TokenContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { TokenContext };
|
||||||
|
export default TokenProvider;
|
||||||
@@ -20,6 +20,7 @@ async function signUp(newUser: UserType) {
|
|||||||
}
|
}
|
||||||
const accessToken = await response.json();
|
const accessToken = await response.json();
|
||||||
storeToken(accessToken);
|
storeToken(accessToken);
|
||||||
|
return accessToken;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error sign up:", error);
|
console.error("Error sign up:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -44,6 +45,7 @@ async function signIn(signInUser: UserType) {
|
|||||||
}
|
}
|
||||||
const accessToken = await response.json();
|
const accessToken = await response.json();
|
||||||
storeToken(accessToken);
|
storeToken(accessToken);
|
||||||
|
return accessToken;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error sign in:", error);
|
console.error("Error sign in:", error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useState } from "react";
|
import { useState, useContext } from "react";
|
||||||
import { Input, Button, Card, CardHeader, CardBody } from "@nextui-org/react";
|
import { Input, Button, Card, CardHeader, CardBody } from "@nextui-org/react";
|
||||||
import { Link } from "@/src/navigation";
|
import { Link } from "@/src/navigation";
|
||||||
import { ChevronRight, Eye, EyeOff } from "lucide-react";
|
import { ChevronRight, Eye, EyeOff } from "lucide-react";
|
||||||
@@ -8,6 +8,7 @@ import { UserType, AuthMessages } from "@/types/user";
|
|||||||
import { roles } from "@/config/selection";
|
import { roles } from "@/config/selection";
|
||||||
import { signUp, signIn } from "./authControl";
|
import { signUp, signIn } from "./authControl";
|
||||||
import { isValidEmail, isValidPassword } from "./validate";
|
import { isValidEmail, isValidPassword } from "./validate";
|
||||||
|
import { TokenContext } from "../TokenProvider";
|
||||||
type Props = {
|
type Props = {
|
||||||
isSignup: Boolean;
|
isSignup: Boolean;
|
||||||
messages: AuthMessages;
|
messages: AuthMessages;
|
||||||
@@ -15,6 +16,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function AuthPage({ isSignup, messages, locale }: Props) {
|
export default function AuthPage({ isSignup, messages, locale }: Props) {
|
||||||
|
const context = useContext(TokenContext);
|
||||||
const [user, setUser] = useState<UserType>({
|
const [user, setUser] = useState<UserType>({
|
||||||
id: null,
|
id: null,
|
||||||
email: "",
|
email: "",
|
||||||
@@ -59,14 +61,16 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
|
|||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
if (isSignup) {
|
if (isSignup) {
|
||||||
try {
|
try {
|
||||||
await signUp(user);
|
const token = await signUp(user);
|
||||||
|
context.setToken(token);
|
||||||
// Move to signin page
|
// Move to signin page
|
||||||
} catch {
|
} catch {
|
||||||
setErrorMessage(messages.signupError);
|
setErrorMessage(messages.signupError);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
signIn(user);
|
const token = await signIn(user);
|
||||||
|
context.setToken(token);
|
||||||
// Move to signin page
|
// Move to signin page
|
||||||
} catch {
|
} catch {
|
||||||
setErrorMessage(messages.signinError);
|
setErrorMessage(messages.signinError);
|
||||||
27
frontend/src/app/[locale]/account/page.tsx
Normal file
27
frontend/src/app/[locale]/account/page.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { Input, Button, Card, CardHeader, CardBody } from "@nextui-org/react";
|
||||||
|
import { Link } from "@/src/navigation";
|
||||||
|
|
||||||
|
export default function Page(params: { locale: string }) {
|
||||||
|
const t = useTranslations("Auth");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="border-none bg-background/60 dark:bg-default-100/50 w-[480px]">
|
||||||
|
<CardHeader className="px-4 pt-4 pb-0 flex justify-between">
|
||||||
|
<h4 className="font-bold text-large">{t('account')}</h4>
|
||||||
|
{/* <Link href={isSignup ? "/auth/signin" : "/auth/signup"} locale={locale}>
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
variant="light"
|
||||||
|
endContent={<ChevronRight size={16} />}
|
||||||
|
>
|
||||||
|
{messages.linkTitle}
|
||||||
|
</Button>
|
||||||
|
</Link> */}
|
||||||
|
</CardHeader>
|
||||||
|
<CardBody className="overflow-visible px-4 pt-0 pb-4">
|
||||||
|
afgdsfg
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ export default function RootLayout({
|
|||||||
fontSans.variable
|
fontSans.variable
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
<Providers themeProps={{ attribute: "class", defaultTheme: "light" }}>
|
||||||
<div className="relative flex flex-col min-h-screen light:bg-neutral-50 dark:bg-neutral-800">
|
<div className="relative flex flex-col min-h-screen light:bg-neutral-50 dark:bg-neutral-800">
|
||||||
<Header locale={locale} />
|
<Header locale={locale} />
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
|
|||||||
@@ -6,17 +6,23 @@ import { useRouter } from "next/navigation";
|
|||||||
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
import { ThemeProvider as NextThemesProvider } from "next-themes";
|
||||||
import { ThemeProviderProps } from "next-themes/dist/types";
|
import { ThemeProviderProps } from "next-themes/dist/types";
|
||||||
|
|
||||||
|
import TokenProvider from "./TokenProvider";
|
||||||
|
import { TokenProps } from "@/types/user";
|
||||||
|
|
||||||
export interface ProvidersProps {
|
export interface ProvidersProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
themeProps?: ThemeProviderProps;
|
themeProps?: ThemeProviderProps;
|
||||||
|
userProps?: TokenProps;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Providers({ children, themeProps }: ProvidersProps) {
|
export function Providers({ children, themeProps, userProps }: ProvidersProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NextUIProvider navigate={router.push}>
|
<NextUIProvider navigate={router.push}>
|
||||||
<NextThemesProvider {...themeProps}>{children}</NextThemesProvider>
|
<NextThemesProvider {...themeProps}>
|
||||||
|
<TokenProvider {...userProps}>{children}</TokenProvider>
|
||||||
|
</NextThemesProvider>
|
||||||
</NextUIProvider>
|
</NextUIProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,18 @@ export type UserType = {
|
|||||||
username: string;
|
username: string;
|
||||||
role: number;
|
role: number;
|
||||||
avatarPath: string;
|
avatarPath: string;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
export type TokenProps = {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TokenType = {
|
||||||
|
token: {
|
||||||
|
access_token: string;
|
||||||
|
user: UserType;
|
||||||
|
};
|
||||||
|
setToken: () => {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AuthMessages = {
|
export type AuthMessages = {
|
||||||
@@ -23,4 +35,4 @@ export type AuthMessages = {
|
|||||||
emailNotExist: string;
|
emailNotExist: string;
|
||||||
signupError: string;
|
signupError: string;
|
||||||
signinError: string;
|
signinError: string;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user