Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#456] [프로필] 프로필 페이지 #461

Merged
merged 30 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
40773c8
refactor: TopHeader 컴포넌트가 text prop을 받을 수 있게끔 수정
minjongbaek Dec 9, 2023
148a354
feat: 사용자 프로필 페이지 새로운 디자인으로 교체
minjongbaek Dec 9, 2023
c66aeb9
feat: 내 프로필 페이지 새로운 디자인으로 교체
minjongbaek Dec 9, 2023
3f71405
refactor: 프로필 페이지에서 Chakra UI 제거
minjongbaek Dec 9, 2023
6595d58
feat: 로그인 여부에 따른 내 프로필 페이지 조건부 렌더링 처리
minjongbaek Dec 9, 2023
06d5558
refactor: 프로필 페이지에서 사용하는 쿼리키 정리
minjongbaek Dec 9, 2023
bfb1a75
chore: 잘못된 스토리 prop 수정
minjongbaek Dec 9, 2023
61c9c6c
feat: BackButton 컴포넌트 작성
minjongbaek Dec 9, 2023
5b6457d
feat: ShareButton 컴포넌트 작성
minjongbaek Dec 9, 2023
f175c15
refactor: TopNavigation 에 직접 작성한 버튼을 컴포넌트로 교체
minjongbaek Dec 9, 2023
c8fb4b8
fix: 잘못 삭제한 useGroupInfoQuery 되돌리기
minjongbaek Dec 9, 2023
e7d4b72
feat: 사용자 프로필 페이지에 TopNavigation 컴포넌트 적용
minjongbaek Dec 9, 2023
5348c30
feat: 카카오 로그인 URL 변경
minjongbaek Dec 10, 2023
c37ea5a
fix: 프로필 페이지를 새로고침 했을 때 발생하는 hydration 에러 수정
minjongbaek Dec 16, 2023
ab5d4ae
refactor: 불필요한 요소 제거
minjongbaek Dec 16, 2023
0279838
feat: 프로필 페이지 누락된 스타일 추가
minjongbaek Dec 16, 2023
8570418
feat: 프로필 페이지에서 로그아웃 했을 때 페이지 이동대신 리프레시 하도록 수정
minjongbaek Dec 16, 2023
24bd510
Update src/v1/profile/bookShelf/ProfileBookshelfPresenter.tsx
minjongbaek Dec 18, 2023
c5cd88c
Update src/v1/profile/info/ProfileInfoPresenter.tsx
minjongbaek Dec 18, 2023
d3cbd35
refactor: ProfileGroup 컴포넌트 이름 수정
minjongbaek Dec 18, 2023
94837c9
Update src/v1/profile/bookShelf/ProfileBookshelfPresenter.tsx
minjongbaek Dec 18, 2023
d4b8487
feat: 프로필 페이지에 프로필 수정 버튼 추가
minjongbaek Dec 18, 2023
8171413
feat: ProfileInfo 스켈레톤 코드 작성
minjongbaek Dec 18, 2023
e18de11
feat: ProfileGroup 스켈레톤 코드 작성
minjongbaek Dec 18, 2023
01408a3
feat: ProfileBookShelf 스켈레톤 코드 작성
minjongbaek Dec 18, 2023
d82cc23
refactor: 코드리뷰 피드백 반영
minjongbaek Dec 18, 2023
9bc7ed5
style: 프리티어 룰 적용
minjongbaek Dec 18, 2023
8e538d0
Merge branch 'main' into feat/#456
minjongbaek Dec 18, 2023
87e723f
fix: 컴포넌트명 오타 수정
minjongbaek Dec 20, 2023
743b2ee
style: 프로필 페이지에서 사용되는 좋아요 뱃지 스타일링을 tailwind 로 변경
minjongbaek Dec 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/bookarchive/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import TopHeader from '@/v1/base/TopHeader';
export default function BookArchivePage() {
return (
<div className="flex w-full flex-col gap-[1rem]">
<TopHeader pathname="/bookarchive" />
<TopHeader text="BookArchvie" />
{/* TODO: 스켈레톤 컴포넌트로 교체 */}
<Suspense fallback={null}>
<Contents />
Expand Down
34 changes: 6 additions & 28 deletions src/app/bookshelf/[bookshelfId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,20 @@

import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

import type { APIBookshelf, APIBookshelfInfo } from '@/types/bookshelf';

import useBookShelfBooksQuery from '@/queries/bookshelf/useBookShelfBookListQuery';
import useBookShelfInfoQuery from '@/queries/bookshelf/useBookShelfInfoQuery';
import useMutateBookshelfLikeQuery from '@/queries/bookshelf/useMutateBookshelfLikeQuery';

import useToast from '@/v1/base/Toast/useToast';
import { isAuthed } from '@/utils/helpers';

import { IconArrowLeft, IconShare, IconKakao } from '@public/icons';

import { IconKakao } from '@public/icons';
import TopNavigation from '@/v1/base/TopNavigation';
import BookShelfRow from '@/v1/bookShelf/BookShelfRow';
import Button from '@/v1/base/Button';
import LikeButton from '@/v1/base/LikeButton';
import BackButton from '@/v1/base/BackButton';
import ShareButton from '@/v1/base/ShareButton';
import type { APIBookshelf, APIBookshelfInfo } from '@/types/bookshelf';

const KAKAO_OAUTH_LOGIN_URL = `${process.env.NEXT_PUBLIC_API_URL}/oauth2/authorize/kakao?redirect_uri=${process.env.NEXT_PUBLIC_CLIENT_REDIRECT_URI}`;

Expand All @@ -34,23 +30,9 @@ export default function UserBookShelfPage({
const { mutate: mutateBookshelfLike } =
useMutateBookshelfLikeQuery(bookshelfId);
const { show: showToast } = useToast();
const router = useRouter();

if (!isSuccess) return null;

const handleClickShareButton = () => {
const url = window.location.href;

navigator.clipboard
.writeText(url)
.then(() => {
showToast({ message: '링크를 복사했어요.', type: 'success' });
})
.catch(() => {
showToast({ message: '잠시 후 다시 시도해주세요', type: 'error' });
});
};

const handleClickLikeButton = () => {
if (!isAuthed()) {
showToast({ message: '로그인 후 이용해주세요.', type: 'normal' });
Expand All @@ -64,14 +46,10 @@ export default function UserBookShelfPage({
<div className="flex w-full flex-col">
<TopNavigation>
<TopNavigation.LeftItem>
<button onClick={() => router.back()}>
<IconArrowLeft />
</button>
<BackButton />
</TopNavigation.LeftItem>
<TopNavigation.RightItem>
<button onClick={handleClickShareButton}>
<IconShare />
</button>
<ShareButton />
</TopNavigation.RightItem>
</TopNavigation>
<div className="mt-[0.8rem] flex flex-col gap-[0.8rem] pb-[2rem] pt-[1rem] font-bold">
Expand Down
4 changes: 2 additions & 2 deletions src/app/group/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import SimpleBookGroupCard from '@/v1/bookGroup/SimpleBookGroupCard';
import DetailBookGroupCard from '@/v1/bookGroup/DetailBookGroupCard';

import useEntireGroupsQuery from '@/queries/group/useEntireGroupsQuery';
import useMyGroupsQuery from '@/queries/group/useMyGroupsQuery';
import useMyGroupsQuery from '@/queries/group/useMyGroupQuery';
import { Skeleton, VStack } from '@chakra-ui/react';
import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
Expand Down Expand Up @@ -44,7 +44,7 @@ const GroupPage = () => {

return (
<>
<TopHeader pathname={'/group'} />
<TopHeader text="Group" />
<div className="mt-[2rem] flex w-full flex-col gap-[1.5rem]">
<SearchGroup
onClick={() => {
Expand Down
26 changes: 17 additions & 9 deletions src/app/profile/[userId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
'use client';

import { APIUser } from '@/types/user';
import TopNavigation from '@/ui/common/TopNavigation';
import ProfileBookShelf from '@/ui/Profile/ProfileBookshelf';
import ProfileInfo from '@/ui/Profile/ProfileInfo';
import { VStack } from '@chakra-ui/react';
import BackButton from '@/v1/base/BackButton';
import ShareButton from '@/v1/base/ShareButton';
import TopNavigation from '@/v1/base/TopNavigation';
import ProfileBookShelf from '@/v1/profile/bookShelf/ProfileBookShelf';
import ProfileInfo from '@/v1/profile/info/ProfileInfo';

const UserProfilePage = ({
params: { userId },
}: {
params: { userId: APIUser['userId'] };
}) => {
return (
<VStack justify="center" align="flex-start" w="100%">
<TopNavigation pageTitle="" />
<VStack justify="flex-start" gap="2rem" w="100%">
<>
<TopNavigation>
<TopNavigation.LeftItem>
<BackButton />
</TopNavigation.LeftItem>
<TopNavigation.RightItem>
<ShareButton />
</TopNavigation.RightItem>
</TopNavigation>
<div className="mt-[1.5rem] flex flex-col gap-[2rem]">
<ProfileInfo userId={userId} />
<ProfileBookShelf userId={userId} />
</VStack>
</VStack>
</div>
</>
);
};

Expand Down
5 changes: 5 additions & 0 deletions src/app/profile/me/group/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const UserGroupPage = () => {
return '준비중입니다.';
};

export default UserGroupPage;
121 changes: 83 additions & 38 deletions src/app/profile/me/page.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,99 @@
'use client';

import { Box, VStack } from '@chakra-ui/react';
import Link from 'next/link';
import { usePathname, useRouter } from 'next/navigation';

import AuthRequired from '@/ui/AuthRequired';
import ProfileInfo from '@/ui/Profile/ProfileInfo';
import ProfileBookShelf from '@/ui/Profile/ProfileBookshelf';
import ProfileGroup from '@/ui/Profile/ProfileGroup';
import Button from '@/ui/common/Button';
import { Menu, MenuItem } from '@/ui/common/Menu';
import { removeAuth } from '@/utils/helpers';
import { useRouter } from 'next/navigation';
import { isAuthed, removeAuth } from '@/utils/helpers';
import userAPI from '@/apis/user';
import TopHeader from '@/v1/base/TopHeader';
import ProfileInfo from '@/v1/profile/info/ProfileInfo';
import ProfileBookShelf from '@/v1/profile/bookShelf/ProfileBookShelf';
import ProfileGroup from '@/v1/profile/group/ProfileGroup';
import Avatar from '@/v1/base/Avatar';
import Link from 'next/link';
import { IconArrowRight } from '@public/icons';
import BookShelf from '@/v1/bookShelf/BookShelf';
import SSRSafeSuspense from '@/components/SSRSafeSuspense';
import Loading from '@/v1/base/Loading';
import Button from '@/v1/base/Button';

const USER_ID = 'me';
const KAKAO_LOGIN_URL = `${process.env.NEXT_PUBLIC_API_URL}/oauth2/authorize/kakao?redirect_uri=${process.env.NEXT_PUBLIC_CLIENT_REDIRECT_URI}`;

const MyProfilePage = () => {
const { push } = useRouter();
const pathname = usePathname();
return (
<SSRSafeSuspense fallback={<Loading fullpage />}>
{isAuthed() ? <MyProfileForAuth /> : <MyProfileForUnAuth />}
</SSRSafeSuspense>
);
};

const MyProfileForUnAuth = () => {
return (
<>
<TopHeader text="Profile" />
<div className="flex flex-col gap-[2rem]">
<div className="mb-[2rem] flex items-center gap-[1rem]">
<Avatar size="large" />
<div className="flex-grow">
<h2 className="text-lg font-bold">로그인 / 회원가입</h2>
<p className="text-sm text-placeholder">
카카오로 3초만에 가입할 수 있어요.
</p>
</div>
<Link href={KAKAO_LOGIN_URL}>
<IconArrowRight width="20px" />
</Link>
</div>
<div className="flex flex-col gap-[0.6rem]">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold">책장</h3>
</div>
<BookShelf>
<div className="w-app pb-[2.5rem] pt-[2rem] shadow-[0px_20px_20px_-16px_#D1D1D1]">
<BookShelf.Background />
<div className="pb-[5.5rem] pt-[3rem] text-center">
<p className="text-sm text-placeholder">책장이 비었어요.</p>
</div>
</div>
</BookShelf>
</div>
<div className="flex flex-col gap-[0.6rem]">
<h3 className="text-lg font-bold">참여한 모임</h3>
<div className="pb-[5.5rem] pt-[5.5rem] text-center">
<p className="text-sm text-placeholder">참여 중인 모임이 없어요.</p>
</div>
</div>
</div>
</>
);
};

const MyProfileForAuth = () => {
const router = useRouter();

const handleLogoutButtonClick = async () => {
await userAPI.logout();
removeAuth();
push('/');
router.refresh();
};

return (
<AuthRequired>
<VStack justify="center" align="flex-start">
<Box alignSelf="flex-end">
<Menu>
<MenuItem text="로그아웃" onClick={handleLogoutButtonClick} />
</Menu>
</Box>
<VStack w="100%" align="flex-start" gap="2rem">
<ProfileInfo userId="me">
<Button
as={Link}
href={`${pathname}/edit`}
scheme="orange"
fullWidth
bgColor="main"
color="white.900"
>
<>
<TopHeader text="Profile">
<button onClick={handleLogoutButtonClick}>로그아웃</button>
</TopHeader>
<div className="flex flex-col gap-[2rem]">
<ProfileInfo userId={USER_ID} />
<Link href="/profile/me/edit" className="w-full">
<Button colorScheme="main-light" size="full">
<span className="mr-[0.5rem] text-sm font-bold text-black-700">
프로필 수정
</Button>
</ProfileInfo>
<ProfileBookShelf userId="me" />
<ProfileGroup />
</VStack>
</VStack>
</AuthRequired>
</span>
</Button>
</Link>
<ProfileBookShelf userId={USER_ID} />
<ProfileGroup userId={USER_ID} />
</div>
</>
);
};

Expand Down
1 change: 1 addition & 0 deletions src/queries/bookshelf/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const bookShelfKeys = {
[...bookShelfKeys.all, bookshelfId] as const,
books: (bookshelfId: APIBookshelf['bookshelfId']) =>
[...bookShelfKeys.all, bookshelfId, 'books'] as const,
summary: (userId: string) => [...bookShelfKeys.all, 'summary', userId],
};

export default bookShelfKeys;
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import bookshelfAPI from '@/apis/bookshelf';
import { useQuery } from '@tanstack/react-query';
import type { QueryOptions } from '@/types/query';
import type { APIBookshelf } from '@/types/bookshelf';
import bookShelfKeys from './key';

const useMySummaryBookshlefQuery = (options?: QueryOptions<APIBookshelf>) =>
useQuery(
['summaryBookshlef', 'me'],
bookShelfKeys.summary('me'),
() => bookshelfAPI.getMySummaryBookshelf().then(({ data }) => data),
options
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import bookshelfAPI from '@/apis/bookshelf';
import { useQuery } from '@tanstack/react-query';
import type { QueryOptions } from '@/types/query';
import type { APIUser } from '@/types/user';
import type { APIBookshelf } from '@/types/bookshelf';
import bookShelfKeys from './key';
import useQueryWithSuspense from '@/hooks/useQueryWithSuspense';

const useUserSummaryBookshlefQuery = (
userId: APIUser['userId'],
options?: QueryOptions<APIBookshelf>
) =>
useQuery(
['summaryBookshlef', String(userId)],
useQueryWithSuspense(
bookShelfKeys.summary(String(userId)),
() =>
bookshelfAPI.getUserSummaryBookshelf({ userId }).then(({ data }) => data),
options
Expand Down
1 change: 1 addition & 0 deletions src/queries/group/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const bookGroupKeys = {
[...bookGroupKeys.details(), id] as const,
comments: (id: APIGroupDetail['bookGroupId']) =>
[...bookGroupKeys.details(), id, 'comments'] as const,
me: () => [...bookGroupKeys.all, 'me'],
};

export default bookGroupKeys;
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import GroupAPI from '@/apis/group';
import { useQuery } from '@tanstack/react-query';
import type { QueryOptions } from '@/types/query';
import type { APIGroupPagination } from '@/types/group';
import bookGroupKeys from './key';

const useMyGroupsQuery = (options?: QueryOptions<APIGroupPagination>) =>
useQuery(
['groups', 'me'],
bookGroupKeys.me(),
() => GroupAPI.getMyGroups().then(({ data }) => data),
options
);
Expand Down
4 changes: 2 additions & 2 deletions src/stories/base/TopHeader.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ export default meta;
type Story = StoryObj<typeof TopHeader>;

export const Default: Story = {
args: { pathname: '/bookarchive' },
args: { text: 'BookArchive' },
render: args => <TopHeader {...args} />,
};

export const WithMenu: Story = {
args: { pathname: '/profile/me' },
args: { text: 'Profile' },
render: args => (
<TopHeader {...args}>
<button
Expand Down
Loading