Implement Auth provide user info

This commit is contained in:
Takeshi Kimata
2024-05-19 11:14:04 +09:00
parent 0a783bddfd
commit 3d141cf05b
16 changed files with 151 additions and 7 deletions

View File

@@ -25,6 +25,7 @@
"projects": "Projects"
},
"Auth": {
"account": "Account",
"signup": "Sign up",
"signin": "Sign in",
"or_signup": "You need sign up?",

View File

@@ -25,6 +25,7 @@
"projects": "プロジェクト"
},
"Auth": {
"account": "アカウント",
"signup": "新規登録",
"signin": "サインイン",
"or_signup": "新規登録ページ",

View 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>
);
}

View File

@@ -16,6 +16,7 @@ import LangSwitch from "./LangSwitch";
import { ThemeSwitch } from "@/components/theme-switch";
import { GithubIcon } from "@/components/icons";
import Image from "next/image";
import DropdownAccount from "./DropdownAccount";
export default function Header(params: { locale: string }) {
const t = useTranslations("Header");
@@ -102,6 +103,7 @@ export default function Header(params: { locale: string }) {
</NextUiLink>
<ThemeSwitch />
<LangSwitch locale={params.locale} />
<DropdownAccount locale={params.locale} />
<NavbarMenuToggle className="md:hidden" />
</NavbarContent>

View 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;

View File

@@ -20,6 +20,7 @@ async function signUp(newUser: UserType) {
}
const accessToken = await response.json();
storeToken(accessToken);
return accessToken;
} catch (error: any) {
console.error("Error sign up:", error);
throw error;
@@ -44,6 +45,7 @@ async function signIn(signInUser: UserType) {
}
const accessToken = await response.json();
storeToken(accessToken);
return accessToken;
} catch (error: any) {
console.error("Error sign in:", error);
throw error;

View File

@@ -1,6 +1,6 @@
"use client";
import React from "react";
import { useState } from "react";
import { useState, useContext } from "react";
import { Input, Button, Card, CardHeader, CardBody } from "@nextui-org/react";
import { Link } from "@/src/navigation";
import { ChevronRight, Eye, EyeOff } from "lucide-react";
@@ -8,6 +8,7 @@ import { UserType, AuthMessages } from "@/types/user";
import { roles } from "@/config/selection";
import { signUp, signIn } from "./authControl";
import { isValidEmail, isValidPassword } from "./validate";
import { TokenContext } from "../TokenProvider";
type Props = {
isSignup: Boolean;
messages: AuthMessages;
@@ -15,6 +16,7 @@ type Props = {
};
export default function AuthPage({ isSignup, messages, locale }: Props) {
const context = useContext(TokenContext);
const [user, setUser] = useState<UserType>({
id: null,
email: "",
@@ -59,14 +61,16 @@ export default function AuthPage({ isSignup, messages, locale }: Props) {
const submit = async () => {
if (isSignup) {
try {
await signUp(user);
const token = await signUp(user);
context.setToken(token);
// Move to signin page
} catch {
setErrorMessage(messages.signupError);
}
} else {
try {
signIn(user);
const token = await signIn(user);
context.setToken(token);
// Move to signin page
} catch {
setErrorMessage(messages.signinError);

View 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>
);
}

View File

@@ -34,7 +34,7 @@ export default function RootLayout({
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">
<Header locale={locale} />
<main>{children}</main>

View File

@@ -6,17 +6,23 @@ import { useRouter } from "next/navigation";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";
import TokenProvider from "./TokenProvider";
import { TokenProps } from "@/types/user";
export interface ProvidersProps {
children: React.ReactNode;
themeProps?: ThemeProviderProps;
userProps?: TokenProps;
}
export function Providers({ children, themeProps }: ProvidersProps) {
export function Providers({ children, themeProps, userProps }: ProvidersProps) {
const router = useRouter();
return (
<NextUIProvider navigate={router.push}>
<NextThemesProvider {...themeProps}>{children}</NextThemesProvider>
<NextThemesProvider {...themeProps}>
<TokenProvider {...userProps}>{children}</TokenProvider>
</NextThemesProvider>
</NextUIProvider>
);
}

View File

@@ -5,6 +5,18 @@ export type UserType = {
username: string;
role: number;
avatarPath: string;
} | null;
export type TokenProps = {
children?: React.ReactNode;
};
export type TokenType = {
token: {
access_token: string;
user: UserType;
};
setToken: () => {};
};
export type AuthMessages = {
@@ -23,4 +35,4 @@ export type AuthMessages = {
emailNotExist: string;
signupError: string;
signinError: string;
};
};