Skip to content

Commit

Permalink
Merge pull request #65 from Team-Partage/feature-64/header
Browse files Browse the repository at this point in the history
해더 작업
  • Loading branch information
sooki88 authored Jul 9, 2024
2 parents 0698372 + eaab5c1 commit d53e92b
Show file tree
Hide file tree
Showing 8 changed files with 723 additions and 18 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@hookform/resolvers": "^3.4.0",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.1.1",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
Expand Down
18 changes: 10 additions & 8 deletions src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useState } from 'react';
import { useEffect, useState } from 'react';

import { z } from 'zod';

Expand All @@ -13,15 +13,23 @@ import { useUserStore } from '@/stores/User';
import { zodResolver } from '@hookform/resolvers/zod';
import { Eye, EyeOff } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { signIn } from 'next-auth/react';
import { signIn, useSession } from 'next-auth/react';
import { useForm } from 'react-hook-form';

const LoginPage = () => {
const { data: session } = useSession();
const router = useRouter();
const [showPassword, setShowPassword] = useState(false);
const { setUserId, setEmail, setUsername, setNickname, setProfileColor, setProfileImage } =
useUserStore();

useEffect(() => {
if (session?.user) {
router.replace('/');
return;
}
}, [session]);

const form = useForm<z.infer<typeof LoginSchema>>({
resolver: zodResolver(LoginSchema),
defaultValues: {
Expand All @@ -36,7 +44,6 @@ const LoginPage = () => {
};

const onSubmit = async (data: z.infer<typeof LoginSchema>) => {
let successLogin = false;
try {
await signIn('credentials', {
username: data.email,
Expand All @@ -51,15 +58,10 @@ const LoginPage = () => {
setUsername(user.username);
setProfileColor(user.profile_color);
setProfileImage(user.profile_image);
successLogin = true;
}
} catch (err) {
throw new Error(`${err}`);
}

if (successLogin) {
router.replace('/');
}
};

return (
Expand Down
11 changes: 10 additions & 1 deletion src/app/auth/register/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useState } from 'react';
import { useEffect, useState } from 'react';

import { z } from 'zod';

Expand All @@ -16,13 +16,22 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { CircleCheck } from 'lucide-react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
import { useForm } from 'react-hook-form';

const RegisterPage = () => {
const { data: session } = useSession();
const { registerUser } = useUserStore();
const [emailCheck, setEmailCheck] = useState(false);
const router = useRouter();

useEffect(() => {
if (session?.user) {
router.replace('/');
return;
}
}, [session]);

const form = useForm<z.infer<typeof RegisterSchema>>({
resolver: zodResolver(RegisterSchema),
defaultValues: {
Expand Down
3 changes: 2 additions & 1 deletion src/app/mypage/_components/WithDraw.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import FormModal from '@/components/FormModal';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardTitle } from '@/components/ui/card';
import { Withdrawal } from '@/services/user';
import { PAGE_ROUTE } from '@/utils/route';
import { useRouter } from 'next/navigation';

const WithDraw = () => {
Expand All @@ -18,7 +19,7 @@ const WithDraw = () => {
};

const handleModal = () => {
router.push('/login');
router.push(PAGE_ROUTE.LOGIN);
};

return (
Expand Down
55 changes: 49 additions & 6 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
'use client';

import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useUserStore } from '@/stores/User';
import { PAGE_ROUTE } from '@/utils/route';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { signOut, useSession } from 'next-auth/react';

import ModalRenderer from './ModalRenderer';
import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar';
import { Button } from './ui/button';

function Header() {
const router = useRouter();
const { data: session } = useSession();
const clearUser = useUserStore((state) => state.clearUser);

const handleLogout = () => {
signOut({ redirect: false }).then(() => router.replace('/'));
clearUser();
};

return (
<header className="flex min-h-[68px] w-full min-w-[375px] items-center justify-between border-b-1 border-neutral-400 px-5 tablet:min-h-[76px] tablet:px-10 desktop:min-h-[84px]">
<Link
Expand All @@ -28,12 +49,34 @@ function Header() {
</span>
</Button>
</ModalRenderer>
<Link className="tablet:small-regular desktop:base-regular" href={PAGE_ROUTE.LOGIN}>
로그인
</Link>
<Link className="tablet:small-regular desktop:base-regular" href={PAGE_ROUTE.REGISTER}>
회원가입
</Link>
{session?.user ? (
<DropdownMenu>
<DropdownMenuTrigger>
<Avatar className="group size-[40px] cursor-pointer tablet:size-[48px] desktop:size-[54px]">
<AvatarImage
className="object-cover"
src={session.user.image || '/default-profile-image.png'}
/>
<AvatarFallback>profile_image</AvatarFallback>
</Avatar>
</DropdownMenuTrigger>
<DropdownMenuContent className="mr-[20px] tablet:mr-[40px]">
<DropdownMenuItem onClick={() => router.push('/mypage')}>
사용자 설정
</DropdownMenuItem>
<DropdownMenuItem onClick={handleLogout}>로그아웃</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
) : (
<>
<Link className="tablet:small-regular desktop:base-regular" href={PAGE_ROUTE.LOGIN}>
로그인
</Link>
<Link className="tablet:small-regular desktop:base-regular" href={PAGE_ROUTE.REGISTER}>
회원가입
</Link>
</>
)}
</div>
</header>
);
Expand Down
187 changes: 187 additions & 0 deletions src/components/ui/dropdown-menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
'use client';

import * as React from 'react';

import { cn } from '@/lib/utils';
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
import { Check, ChevronRight, Circle } from 'lucide-react';

const DropdownMenu = DropdownMenuPrimitive.Root;

const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;

const DropdownMenuGroup = DropdownMenuPrimitive.Group;

const DropdownMenuPortal = DropdownMenuPrimitive.Portal;

const DropdownMenuSub = DropdownMenuPrimitive.Sub;

const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;

const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
<DropdownMenuPrimitive.SubTrigger
ref={ref}
className={cn(
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-slate-100 data-[state=open]:bg-slate-100 dark:focus:bg-slate-800 dark:data-[state=open]:bg-slate-800',
inset && 'pl-8',
className,
)}
{...props}
>
{children}
<ChevronRight className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
));
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;

const DropdownMenuSubContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.SubContent
ref={ref}
className={cn(
'z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50',
className,
)}
{...props}
/>
));
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;

const DropdownMenuContent = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
ref={ref}
sideOffset={sideOffset}
className={cn(
'z-50 min-w-[171px] overflow-hidden rounded-md border border-neutral-400 bg-neutral-600 py-[10px] text-neutral-100 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className,
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
));
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;

const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Item
ref={ref}
className={cn(
'relative flex cursor-pointer justify-center select-none items-center rounded-sm px-2 py-1.5 text-neutral-100 base-regular outline-none transition-colors focus:bg-neutral-500 h-[50px] ',
inset && 'pl-8',
className,
)}
{...props}
/>
));
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;

const DropdownMenuCheckboxItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>
>(({ className, children, checked, ...props }, ref) => (
<DropdownMenuPrimitive.CheckboxItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50',
className,
)}
checked={checked}
{...props}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Check className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
));
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;

const DropdownMenuRadioItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>
>(({ className, children, ...props }, ref) => (
<DropdownMenuPrimitive.RadioItem
ref={ref}
className={cn(
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-slate-100 focus:text-slate-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-slate-800 dark:focus:text-slate-50',
className,
)}
{...props}
>
<span className="absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<Circle className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
));
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;

const DropdownMenuLabel = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
}
>(({ className, inset, ...props }, ref) => (
<DropdownMenuPrimitive.Label
ref={ref}
className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}
{...props}
/>
));
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;

const DropdownMenuSeparator = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>
>(({ className, ...props }, ref) => (
<DropdownMenuPrimitive.Separator
ref={ref}
className={cn('-mx-1 my-1 h-px bg-slate-100 dark:bg-slate-800', className)}
{...props}
/>
));
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;

const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span className={cn('ml-auto text-xs tracking-widest opacity-60', className)} {...props} />
);
};
DropdownMenuShortcut.displayName = 'DropdownMenuShortcut';

export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuGroup,
DropdownMenuPortal,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuRadioGroup,
};
2 changes: 1 addition & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { auth } from './auth';
export async function middleware() {
const session = await auth();
if (!session) {
return NextResponse.redirect('http://localhost:3000/login');
return NextResponse.redirect('http://localhost:3000/auth/login');
}
}

Expand Down
Loading

0 comments on commit d53e92b

Please sign in to comment.