Skip to content

Commit

Permalink
feat: 헤더 리뉴얼 (#189)
Browse files Browse the repository at this point in the history
* feat: 헤더 UI 완성

* chore: 스키마 변경 사항 반영

* feat: 로그아웃 구현
  • Loading branch information
Yejin0O0 authored Nov 5, 2024
1 parent 2d8f9a1 commit 9023e6d
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 241 deletions.
220 changes: 93 additions & 127 deletions components/ui/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,142 +1,108 @@
"use client";
import { useGetLoginState, usePostLogout } from "@/lib/api/hooks/SessionHook";
// import { useGetMyInfo } from "@/lib/api/hooks/memberHook";

import SImage from "@/components/ui/Image";
import { Input } from "@/components/ui/Input";
import { usePostLogout } from "@/lib/api/hooks/SessionHook";
import { useGetMembersSession } from "@/lib/api/hooks/memberHook";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import React, { useState } from "react";
import SImage from "./Image";
import { Input } from "./Input";
import { LinkText } from "./Text";

// const PersonalSection = () => {
// // const { data: isLogin } = useGetLoginState();
// const [showMenu, setShowMenu] = useState(false);
// const { mutate: logout } = usePostLogout();
// // TODO(Yejin0O0): 백엔드에 세션 확인하는 api 만들어달라고 한 후 로직 바꾸기 ( api 직접 만드는 것은 좋은 방법이 아님 )
// // const { data } = useGetMyInfo(!!isLogin?.loggedIn);
// // const clubId = data?.club_member_my_page_response?.club_id || null;
// const router = useRouter();

// const handleProfileClick = () => {
// setShowMenu(!showMenu);
// };
import type { ReactNode } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "./dropdown-menu";

// const handleLogout = () => {
// logout(undefined, {
// onSuccess: () => {
// router.push("/");
// },
// });
// };
function Header() {
const path = usePathname();
const router = useRouter();
const { data, isLoading } = useGetMembersSession();

// if (!isLogin?.loggedIn) {
// return (
// <div className="flex w-44 justify-evenly">
// <LinkText
// color="gray"
// size="sm"
// align="center"
// className="cursor-pointer leading-6 items-center"
// link="/login"
// >
// 동호회 만들기
// </LinkText>
// <LinkText
// color="primary"
// size="sm"
// className="cursor-pointer leading-6"
// link="/login"
// >
// Login
// </LinkText>
// </div>
// );
// }
const { mutate: logout } = usePostLogout();

// return (
// <div className="relative flex items-center">
// {!clubId ? (
// <LinkText
// color="gray"
// size="sm"
// align="center"
// className="cursor-pointer leading-6 mr-4"
// link="/club/create"
// >
// 동호회 만들기
// </LinkText>
// ) : null}
const handleLogout = () => {
if (confirm("정말로 로그아웃 하시겠습니까?")) {
logout(undefined, {
onSuccess: () => {
router.push("/");
},
});
}
};

// <button
// onClick={() => handleProfileClick()}
// className="cursor-pointer"
// type="button"
// >
// <SImage
// src={data?.profile_image || "/images/dummy-image.jpg"}
// radius="circular"
// width={45}
// height={45}
// alt="profile"
// />
// </button>
const getActiveStyle = (linkPath: string) => {
let baseClass =
"cursor-pointer hover:text-primary transition-colors duration-300 ";
if (
path === linkPath ||
(linkPath === "/club" && path.startsWith("/club"))
) {
baseClass += "text-primary";
return baseClass;
}
baseClass += "text-gray-400";
return baseClass;
};

// {showMenu && (
// <div className="absolute top-full right-0 mt-2 bg-white border border-gray-200 shadow-lg p-3 rounded w-40 space-y-2">
// {clubId && (
// <div>
// <LinkText
// color="primary"
// size="sm"
// className="block cursor-pointer hover:underline"
// link={`/club/${clubId}`}
// >
// 내 동호회
// </LinkText>
// </div>
// )}
// <div>
// <LinkText
// color="primary"
// size="sm"
// className="block cursor-pointer hover:underline"
// link="/my"
// >
// 마이페이지
// </LinkText>
// </div>
// <div>
// <button
// onClick={() => handleLogout()}
// type="button"
// className="block w-full text-left text-primary text-sm cursor-pointer hover:underline"
// >
// Logout
// </button>
// </div>
// </div>
// )}
// </div>
// );
// };
let userMenu: ReactNode;
if (isLoading) {
userMenu = null;
} else if (data) {
userMenu = (
<DropdownMenu>
<DropdownMenuTrigger className="focus-visible:ring-2 rounded-full">
<SImage
src={data.profile_image || "/images/dummy-image.jpg"}
radius="circular"
width={36}
height={36}
alt="profile"
/>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem className="w-full flex justify-center items-center cursor-pointer">
<Link href="/my-page">마이페이지</Link>
</DropdownMenuItem>
<DropdownMenuItem
className="w-full flex justify-center items-center cursor-pointer"
onClick={handleLogout}
>
로그아웃
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
} else {
userMenu = (
<Link href="/login">
<p className="rounded-full h-fit text-gray-500 text-sm">로그인</p>
</Link>
);
}

function Header() {
const path = usePathname();
return (
<div className="flex items-center justify-between space-x-4 w-full max-w-5xl h-16 sticky top-0 z-50 border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<Link href="/">
<div className="text-xl font-semibold cursor-pointer">LOGO</div>
</Link>
<div className="flex items-center justify-end space-x-2 w-1/2">
{/* {path === "/" && (
<div className="w-1/2">
<Input search radius="round" placeholder="" size="sm" />
</div>
)} */}
{/* <PersonalSection /> */}
<div className="flex items-center justify-between w-full max-w-6xl h-16 px-4 sticky top-0 z-50 backdrop-blur-md">
<div className="flex items-center space-x-8">
<Link href="/">
<div className="text-2xl cursor-pointer">LOGO</div>
</Link>
<nav className="flex space-x-4">
<Link href="/">
<p className={getActiveStyle("/")}>오늘의경기</p>
</Link>
<Link href="/club">
<p className={getActiveStyle("/club")}>동호회</p>
</Link>
</nav>
</div>
<div className="flex items-center justify-end space-x-4 w-1/2 gap-2">
<div className="w-1/2">
<Input search radius="round" placeholder="동호회 검색" size="sm" />
</div>
{userMenu}
</div>
</div>
);
}

export default Header;
47 changes: 11 additions & 36 deletions lib/api/functions/memberFn.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,21 @@
import type { components } from "@/schemas/schema";
import type { GetMemberSessionResponse } from "@/types/memberTypes";
import restClient from "../restClient";

// type MemberIsClubMemberResponse =
// components["schemas"]["MemberIsClubMemberResponse"];
// type MemberMyPageResponse = components["schemas"]["MemberMyPageResponse"];
// type MemberUpdateRequest = components["schemas"]["MemberUpdateRequest"];
// type MemberResponse = components["schemas"]["MemberResponse"];
type MemberMyPageResponse =
components["schemas"]["CommonResponseMemberMyPageResponse"];
type MemberMatchRecord = components["schemas"]["MatchResultResponse"];

const BASE_URL = `${process.env.NEXT_PUBLIC_BASE_URL}`;

// export const getIsClubMember =
// async (): Promise<MemberIsClubMemberResponse> => {
// const response = await fetch(`${BASE_URL}/members/is-club-member`, {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// },
// credentials: "include",
// });
export const getMembersSession =
async (): Promise<GetMemberSessionResponse> => {
return restClient.get<GetMemberSessionResponse>("/members/session");
};

// if (!response.ok) {
// throw new Error("멤버가 클럽에 속해있는지 조회할 수 없습니다.");
// }

// return response.json();
// };

// export const getMembersMyPage = async (): Promise<MemberMyPageResponse> => {
// const response = await fetch(`${BASE_URL}/members/myPage`, {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// },
// credentials: "include",
// });

// if (!response.ok) {
// throw new Error("내 정보 조회에 실패하였습니다.");
// }

// return response.json();
// };
export const getMembersMyPage = async (): Promise<MemberMyPageResponse> => {
return restClient.get<MemberMyPageResponse>("/members/myPage");
};

export const postMembersProfileImage = async (
profileImage: FormData,
Expand Down
2 changes: 1 addition & 1 deletion lib/api/hooks/SessionHook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const usePostLogout = () => {
mutationFn: postLogout,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["loginState"] });
queryClient.invalidateQueries({ queryKey: ["myInfo"] });
queryClient.invalidateQueries({ queryKey: ["mySession"] });
},
onError: (error: Error) => alert(error),
});
Expand Down
32 changes: 20 additions & 12 deletions lib/api/hooks/memberHook.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
import type { components } from "@/schemas/schema";
import type {
GetMemberSessionData,
MemberMyPageData,
} from "@/types/memberTypes";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
// getIsClubMember,
getMembersMatchRecord,
// getMembersMyPage,
getMembersMyPage,
getMembersSession,
postMembersProfileImage,
// putMembersProfileImage,
} from "../functions/memberFn";
import useQueryWithToast from "./useQueryWithToast";

type MemberImageUpdate = components["schemas"]["MemberUpdateRequest"];
export const useGetMembersSession = () => {
return useQuery({
queryKey: ["mySession"],
queryFn: async () => {
const result = await getMembersSession();

// export const useGetIsClubMember = () => {
// return useQuery({
// queryKey: ["isClubMemberData"],
// queryFn: getIsClubMember,
// });
// };
if (result.result === "FAIL") {
return null;
}
return result.data;
},
});
};

// export const useGetMembersMyPage = () => {
// return useQuery({
// queryKey: ["myPageData"],
// queryFn: getMembersMyPage,
// });
// return useQueryWithToast<MemberMyPageData>(["myPage"], getMembersMyPage);
// };

// export const useGetMyInfo = (isEnabled: boolean) => {
Expand Down
Loading

0 comments on commit 9023e6d

Please sign in to comment.