Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38,193 changes: 19,097 additions & 19,096 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions src/app/profile/[userId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';

function Profile() {
return (
<div>
<div className="bg-black">profile page</div>
</div>
);
}

export default Profile;
7 changes: 6 additions & 1 deletion src/app/profile/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import ProfilePage from '@/features/profile/components/ProfilePage';
import React from 'react';

const Profile = () => {
return <div>Profile</div>;
return (
<>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ํ•„์š”์—†์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!

<ProfilePage />
</>
);
};

export default Profile;
11 changes: 8 additions & 3 deletions src/components/sorting-button/SortingButton.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
'use client';
import { useState } from 'react';
import React, { useState } from 'react';
import { IcSorting } from '../../../public/icons/index';

interface SortingButtonProps {
interface SortingButtonProps extends React.ComponentPropsWithoutRef<'button'> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

export ๋˜์ง€ ์•Š๋Š” Props ํƒ€์ž…์€ ์š”์ฆ˜ Props์“ฐ๋Š” ๊ฒƒ ๊ฐ™์•„์š”.
ISortButtonProps
ISortButton

variant: 'byDeadline' | 'byDate';
onClickSorting: (sortBy: string) => void;
}

function SortingButton({ variant, onClickSorting }: SortingButtonProps) {
function SortingButton({
variant,
onClickSorting,
...buttonProps

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...props๊ฐ€ ์ •๋ง ๋ฆฌํŒฉํ† ๋ง ๋‚ด์„ฑ๋„ ๊ธธ๋Ÿฌ์ฃผ๊ณ , ํƒ€์ž…์•ˆ์ •์ ์ด๋ชปํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ ๊ฐ™์•„์š”. ๊ทธ๋ž˜์„œ ์กฐ๊ธˆ ๋” ํ”„๋กญ์Šค๋Š” ๋ช…์‹œ์ ์œผ๋กœ ์ž‘์„ฑํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

}: SortingButtonProps) {
const [isActive, setIsActive] = useState(false);

const onClick = (): void => {
Expand Down Expand Up @@ -37,6 +41,7 @@ function SortingButton({ variant, onClickSorting }: SortingButtonProps) {

return (
<button
{...buttonProps}
className={`flex h-[40px] items-center rounded-xl border px-[12px] py-[8px] text-sm font-medium ${
variant === 'byDeadline' && isActive
? 'border-green-normal-01 text-green-normal-01'
Expand Down
13 changes: 9 additions & 4 deletions src/components/tab/Tab.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { Meta, StoryObj } from '@storybook/react';
import Tab from './Tab';
import { BOOK_TABS, CONTENT_TABS, MY_PAGE_TABS } from '@/constants/tabs';
import {
BOOK_TABS,
CONTENT_TABS,
CLUB_TABS,
EXCHANGE_TABS,
} from '@/constants/tabs';

const meta = {
title: 'Components/Tab',
Expand All @@ -17,17 +22,17 @@ const meta = {
},
items: {
control: 'select',
options: [BOOK_TABS, CONTENT_TABS, MY_PAGE_TABS],
options: [BOOK_TABS, CONTENT_TABS, CLUB_TABS, EXCHANGE_TABS],
mapping: {
BOOK_TABS: BOOK_TABS,
CONTENT_TABS: CONTENT_TABS,
MY_PAGE_TABS: MY_PAGE_TABS,
CLUB_TABS: CLUB_TABS,
},
description: 'ํƒญ ์•„์ดํ…œ ๋ชฉ๋ก',
},
activeTab: {
control: 'select',
options: [...BOOK_TABS, ...CONTENT_TABS, ...MY_PAGE_TABS],
options: [...BOOK_TABS, ...CONTENT_TABS, ...CLUB_TABS, ...EXCHANGE_TABS],
description: 'ํ˜„์žฌ ์„ ํƒ๋œ ํƒญ',
},
},
Expand Down
15 changes: 10 additions & 5 deletions src/constants/tabs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
export const BOOK_TABS = ['์ „์ฒด', '์ž์œ ์ฑ…', '์ง€์ •์ฑ…'] as const;

export const CONTENT_TABS = ['๋ชจ์ž„', '๊ตํ™˜'] as const;
export const MY_PAGE_TABS = [
'๋‚˜์˜ ๋ชจ์ž„',
'๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„',
'๋‚˜์˜ ๋ฆฌ๋ทฐ',

export const CLUB_TABS = ['๋‚˜์˜ ๋ชจ์ž„', '๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„', '๋‚˜์˜ ๋ฆฌ๋ทฐ'] as const;

export const EXCHANGE_TABS = [
'๋‚˜์˜ ๊ตํ™˜',
'๋‚ด๊ฐ€ ๋“ฑ๋กํ•œ ์ฑ…',
'๊ตํ™˜ ๋ฆฌ๋ทฐ',
] as const;

export type TabType = 'MAIN_TAB' | 'SUB_TAB';
export type BookTab = (typeof BOOK_TABS)[number];
export type ContentTab = (typeof CONTENT_TABS)[number];
export type MyPageTab = (typeof MY_PAGE_TABS)[number];
export type ClubTab = (typeof CLUB_TABS)[number];
export type ExchangeTab = (typeof EXCHANGE_TABS)[number];
17 changes: 17 additions & 0 deletions src/features/profile/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Profile from './profile/Profile';

function Header() {
return (
<div
className="flex w-full min-w-[336px] flex-col items-center pb-[20px] pt-[24px] sm:pl-[20px] sm:pr-[19px] md:px-[24px] lg:px-[102px]"
role="upper"
>
<h1 className="w-full min-w-[336px] text-xl font-bold text-gray-black">
๋งˆ์ด ํŽ˜์ด์ง€
</h1>
<Profile />
</div>
);
}

export default Header;
30 changes: 30 additions & 0 deletions src/features/profile/components/MainContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';
import Tab from '@/components/tab/Tab';
import { CONTENT_TABS, ContentTab } from '@/constants';
import { useState } from 'react';
import ClubContents from './clubs/ClubContents';
import ExchangeContents from './exchange/ExchangeContents';

function MainContent() {
const [selectedTab, setSelectedTab] = useState<ContentTab>(CONTENT_TABS[0]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์ œ ์ƒ๊ฐ์—๋Š” useSelectTab์œผ๋กœ ํ›…์„ ํ•˜๋‚˜๊ตฌ์„ฑํ•˜๊ณ ,
์ฟผ๋ฆฌํŒŒ๋ผ๋ฏธํ„ฐ์— ์—ฐ๋™์‹œํ‚ค๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”.

์ฟผ๋ฆฌํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ํ•˜๋‚˜์˜ ์ „์—ญ์ ์ธ ์ƒํƒœ๋กœ ์“ฐ์‹œ๋ฉด ์ข‹์•„์š”.


return (
<div>
<div className="w-full border-b-2 sm:px-5 md:px-6 lg:px-[102px]">
<Tab
items={CONTENT_TABS}
activeTab={selectedTab}
onTabChange={(item) => setSelectedTab(item)}
tabType="MAIN_TAB"
/>
</div>
{selectedTab === CONTENT_TABS[0] ? (
<ClubContents />
) : (
<ExchangeContents />
)}
</div>
);
}

export default MainContent;
13 changes: 13 additions & 0 deletions src/features/profile/components/ProfilePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Header from './Header';
import MainContent from './MainContent';

function ProfilePage() {
return (
<div className="flex w-full min-w-[375px] flex-col">
<Header />
<MainContent />
</div>
);
}

export default ProfilePage;
48 changes: 48 additions & 0 deletions src/features/profile/components/clubs/ClubContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import SortingButton from '@/components/sorting-button/SortingButton';
import { CLUB_TABS, ClubTab } from '@/constants';
import { useState } from 'react';
import JoinedClubList from './JoinedClubList';
import MyReviewList from './MyReviewList';
import HostedClubList from './HostedClubsList';
import Tab from '@/components/tab/Tab';

export default function ClubContents() {
const [sortBy, setSortBy] = useState<string | undefined>('NEWEST');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ํ•˜๋“œ์ฝ”๋”ฉ์ด ์•„๋‹ˆ๋ผ, enum as const ์„ ์–ธ๋œ ๊ฐ’์˜ ๋ฉค๋ฒ„๊ฐ’์ด ๋“ค์–ด์˜ค๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”.

ValueOf <- ์ฐพ์•„๋ณด์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

type ValueOf = T[keyof T];

KeyOf TypeOf

const [selectedList, setSelectedList] = useState<ClubTab>(CLUB_TABS[0]);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์กฐ๊ธˆ ๋” ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ข€ ๋” ๋ณด๊ธฐ ์‰ฝ๊ฒŒ ๊ฐœ์„ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์š”
CLUB_TABS_STATUS.MYSCHEDULE


const renderList = (selectedList: ClubTab) => {
switch (selectedList) {
case CLUB_TABS[0]:
return <JoinedClubList />;
case CLUB_TABS[1]:
return <HostedClubList />;
case CLUB_TABS[2]:
return <MyReviewList />;
}
};

return (
<div
className="min-h-screen w-full flex-grow bg-gray-light-02 pt-3 sm:px-[20px] sm:pb-[44px] md:px-[24px] md:pb-[45px] lg:px-[102px] lg:pb-[41px]"
role="main"
>
<div className="flex items-center justify-between gap-x-2 sm:overflow-auto sm:whitespace-nowrap sm:[&::-webkit-scrollbar]:hidden">
<Tab
items={CLUB_TABS}
activeTab={selectedList}
onTabChange={(item) => setSelectedList(item)}
tabType="SUB_TAB"
/>

<SortingButton
variant="byDate"
onClickSorting={(order) => {
setSortBy(order);
console.log(sortBy);
}}
/>
</div>
<div className="pt-[18px]">{renderList(selectedList)}</div>
</div>
);
}
3 changes: 3 additions & 0 deletions src/features/profile/components/clubs/HostedClubsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function HostedClubList() {
return <div>๋‚ด๊ฐ€ ๋งŒ๋“  ๋ชจ์ž„ ๋ทฐ</div>;
}
3 changes: 3 additions & 0 deletions src/features/profile/components/clubs/JoinedClubList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function JoinedClubList() {
return <div>๋‚˜์˜ ๋ชจ์ž„ ๋ทฐ</div>;
}
3 changes: 3 additions & 0 deletions src/features/profile/components/clubs/MyReviewList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function MyReviewList() {
return <div>๋‚˜์˜ ๋ฆฌ๋ทฐ ๋ทฐ</div>;
}
50 changes: 50 additions & 0 deletions src/features/profile/components/exchange/ExchangeContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import SortingButton from '@/components/sorting-button/SortingButton';
import { EXCHANGE_TABS, ExchangeTab } from '@/constants';
import { useState } from 'react';
import TradeRecords from './TradeRecords';
import MyRegisteredBooks from './MyRegisteredBooks';
import TradeReviews from './TradeReviews';
import Tab from '@/components/tab/Tab';

export default function ClubContents() {
const [sortBy, setSortBy] = useState<string | undefined>('NEWEST');
const [selectedList, setSelectedList] = useState<ExchangeTab>(
EXCHANGE_TABS[0],
);

const renderList = (selectedList: ExchangeTab) => {
switch (selectedList) {
case EXCHANGE_TABS[0]:
return <TradeRecords />;
case EXCHANGE_TABS[1]:
return <MyRegisteredBooks />;
case EXCHANGE_TABS[2]:
return <TradeReviews />;
}
};

return (
<div
className="min-h-screen w-full flex-grow bg-gray-light-02 pt-3 sm:px-[20px] sm:pb-[44px] md:px-[24px] md:pb-[45px] lg:px-[102px] lg:pb-[41px]"
role="main"
>
<div className="flex items-center justify-between gap-x-2 sm:overflow-auto sm:whitespace-nowrap sm:[&::-webkit-scrollbar]:hidden">
<Tab
items={EXCHANGE_TABS}
activeTab={selectedList}
onTabChange={(item) => setSelectedList(item)}
tabType="SUB_TAB"
/>

<SortingButton
variant="byDate"
onClickSorting={(order) => {
setSortBy(order);
console.log(sortBy);
}}
/>
</div>
<div className="pt-[18px]">{renderList(selectedList)}</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function MyRegisteredBooks() {
return <div>๋‚ด๊ฐ€ ๋“ฑ๋กํ•œ ์ฑ…</div>;
}
3 changes: 3 additions & 0 deletions src/features/profile/components/exchange/TradeRecords.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function TradeRecords() {
return <div>๋‚˜์˜ ๊ตํ™˜ ๊ธฐ๋ก</div>;
}
3 changes: 3 additions & 0 deletions src/features/profile/components/exchange/TradeReviews.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function TradeReviews() {
return <div>๊ตํ™˜ ๋ฆฌ๋ทฐ</div>;
}
4 changes: 4 additions & 0 deletions src/features/profile/components/profile/Profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function Profile() {
return <div className="mt-5 flex h-[200px] w-full min-w-[336px]">ํ”„๋กœํ•„</div>;
}
export default Profile;
Loading