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

Feat: 마이리스트, 콜라보리스트 페이지 구현 #10

Merged
merged 33 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
19685c9
Feat: 사용자 피드 페이지 mock 데이터 연결해서 프로필 완성
ParkSohyunee Jan 26, 2024
057e58b
Chore: 프리티어 추가
ParkSohyunee Jan 27, 2024
81ba815
Feat: 카테고리, 리스트 mock data 추가
ParkSohyunee Jan 27, 2024
f029bf9
Feat: 리스트 mock data 연결
ParkSohyunee Jan 27, 2024
3b872fc
Feat: 아이템 목록 mock data 연결
ParkSohyunee Jan 27, 2024
9ebb860
Feat: 피드페이지 프로필 컴포넌트 분리, 타입 추가
ParkSohyunee Jan 27, 2024
2197edd
Feat: Content 컴포넌트 분리, 마이/콜라보 타입에 따른 리스트 목록 불러오기 로직 추가
ParkSohyunee Jan 27, 2024
ad1a720
Feat: 피드 페이지 - 카테고리 타입에 따른 리스트 목록 불러오기 로직 추가
ParkSohyunee Jan 29, 2024
087611d
Design: 글로벌css 설정, 피드페이지 - 프로필 컴포넌트 UI 구현
ParkSohyunee Jan 29, 2024
471aaf6
Design: 피드페이지 - 리스트 타입, 카테고리 UI 구현
ParkSohyunee Jan 30, 2024
2afba44
Design: 피드페이지 - 리스트 UI 구현(Masonry 레이아웃 적용)
ParkSohyunee Jan 30, 2024
1b951ba
Design: 피드페이지 전체 스타일 점검, 리스트 배경색 dynamic 스타일 적용
ParkSohyunee Jan 30, 2024
011ff9e
Design: 리스트, 카테고리 selected 상태일때 스타일 적용
ParkSohyunee Jan 31, 2024
7a413d9
Feat: 마이리스트, 콜라보리스트 페이지로 각각 분리
ParkSohyunee Jan 31, 2024
7286239
Style: styles prefix 수정, 주석 정리
ParkSohyunee Jan 31, 2024
375754d
Design: 피드페이지 - 플로팅버튼, 헤더 UI 구현, 배경이미지 불러와서 UI 구현, 스타일 일부 수정
ParkSohyunee Jan 31, 2024
9727f9f
Feat: 리액트쿼리, axios 기본 설정
ParkSohyunee Feb 1, 2024
f70403a
Feat: 카테고리 조회 api 추가
ParkSohyunee Feb 1, 2024
aa420f3
Feat: svg 컴포넌트화 설정 및 기존 img 태그 컴포넌트로 변경
ParkSohyunee Feb 1, 2024
d6a5b3f
Design: 리스트 비공개 디자인 수정
ParkSohyunee Feb 1, 2024
4dfa9f6
Feat: 플로팅버튼 공통 컴포넌트로 분리, 스크롤 top 기능 구현
ParkSohyunee Feb 1, 2024
2705cc6
Chore: next/Image 컴포넌트 사용을 위한 hostname 설정
ParkSohyunee Feb 2, 2024
e262398
Design: 프로필 컴포넌트 이미지 next/Image 컴포넌트로 수정
ParkSohyunee Feb 2, 2024
6c8285c
Chore: 서버 도메인 URL 환경변수로 설정
ParkSohyunee Feb 2, 2024
751a177
Feat: ArrowUp 컴포넌트 스크롤이벤트 쓰로틀링 커스텀 훅 구현
ParkSohyunee Feb 2, 2024
30aea13
Design: ArrowUp 버튼 스타일 효과 수정
ParkSohyunee Feb 2, 2024
e804394
Style: props 이름 수정
ParkSohyunee Feb 2, 2024
6890ebb
Merge branch 'dev' into feature/feed
ParkSohyunee Feb 3, 2024
85c2b6e
Merge branch 'dev' into feature/feed
seoyoung-min Feb 3, 2024
a55bda1
Chore: axios 설정
ParkSohyunee Feb 3, 2024
8ddf9b0
Merge branch 'feature/feed' of https://github.com/ParkSohyunee/ListyW…
ParkSohyunee Feb 3, 2024
f90f2f2
Style: 콜라보리스트 페이지 props 주석 처리
ParkSohyunee Feb 3, 2024
11f8bcb
Style: 마이리스트 페이지 사용하지 않는 props 주석 처리
ParkSohyunee Feb 3, 2024
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: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
}
},
"dependencies": {
"@egjs/react-grid": "^1.16.0",
"@tanstack/react-query": "^5.17.12",
"@tanstack/react-query-devtools": "^5.17.12",
"@vanilla-extract/dynamic": "^2.1.0",
"@vanilla-extract/integration": "^6.2.4",
"@vanilla-extract/next-plugin": "^2.3.2",
"@yaireo/tagify": "^4.19.0",
Expand Down
3 changes: 3 additions & 0 deletions public/icons/arrow_left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/arrow_up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/lock_alt.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/setting.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions src/app/[userNickname]/_components/Action.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { style } from '@vanilla-extract/css';

export const button = style({
padding: '0.8rem 1.2rem',

backgroundColor: 'var(--Blue, #0047FF)',
borderRadius: '5rem',

fontSize: '1rem',
fontWeight: '600',
color: '#fff',
});
27 changes: 27 additions & 0 deletions src/app/[userNickname]/_components/Action.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

/**
TODO
- [ ] 상태(팔로우, 언팔로우)에 따른 팔로우 버튼 UI
- [ ] 조건(비회원, 회원)에 따른 팔로우 버튼 동작(api 연동)
*/

import * as styles from './Action.css';

interface ActionProps {
isFollowed: boolean;
}

export default function Action({ isFollowed }: ActionProps) {
const label = isFollowed ? '팔로우' : '팔로우 취소';

const handleFollowUser = () => {
// 1. follow 하는 api 요청 + update
};

return (
<button className={styles.button} onClick={handleFollowUser}>
{label}
</button>
);
}
51 changes: 51 additions & 0 deletions src/app/[userNickname]/_components/Card.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { style, createVar } from '@vanilla-extract/css';

export const listColor = createVar();

export const container = style({
width: '185px',
padding: '3rem 1.2rem',

borderRadius: '1.5rem',
backgroundColor: listColor,
});

export const title = style({
padding: '1.1rem',

fontSize: '1.7rem',
fontWeight: '600',
color: 'var(--text-text-grey-dark, #202020)',
textAlign: 'right',
letterSpacing: '-0.51px',
wordBreak: 'keep-all',
});

export const list = style({
padding: '1rem 0',

display: 'flex',
flexDirection: 'column',

fontSize: '1.2rem',
fontWeight: '400',
color: 'var(--text-text-grey-dark, #202020)',
lineHeight: '2.5rem',
letterSpacing: '-0.36px',
});

export const lockIcon = style({
padding: '0 1rem',

display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
gap: '2px',
});

export const lockText = style({
fontSize: '1.1rem',
fontWeight: '400',
letterSpacing: '-0.33px',
color: '#AFB1B6',
});
43 changes: 43 additions & 0 deletions src/app/[userNickname]/_components/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
TODO
- [ ] 다른 사람 피드볼때, 비공개 리스트는 보여지지 않음
- [ ] svg 아이콘 컴포넌트화
*/

import { ListType } from '../mockData/mockDataTypes'; // 삭제 예정
import { assignInlineVars } from '@vanilla-extract/dynamic';
import * as styles from './Card.css';

import CardItem from './CardItem';
import LockIcon from '/public/icons/lock_alt.svg';

interface CardProps {
list: ListType;
isOwner: boolean;
}

export default function Card({ list, isOwner }: CardProps) {
const isVisibleLockIcon = isOwner && !list.isPublic;

return (
<ul
className={styles.container}
style={assignInlineVars({
[styles.listColor]: `${list.backgroundColor}`,
})}
>
{isVisibleLockIcon && (
<div className={styles.lockIcon}>
<span className={styles.lockText}>비공개</span>
<LockIcon alt="비공개 리스트 표시" />
</div>
)}
<h2 className={styles.title}>{list.title}</h2>
<ul className={styles.list}>
{list.items.map((item) => (
<CardItem key={item.id} item={item} />
))}
</ul>
</ul>
);
}
6 changes: 6 additions & 0 deletions src/app/[userNickname]/_components/CardItem.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { style } from '@vanilla-extract/css';

export const container = style({
display: 'flex',
gap: '5px',
});
16 changes: 16 additions & 0 deletions src/app/[userNickname]/_components/CardItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ItemType } from '../mockData/mockDataTypes'; // 삭제 예정

import * as styles from './CardItem.css';

interface CardItemProps {
item: ItemType;
}

export default function CardItem({ item }: CardItemProps) {
return (
<li className={styles.container}>
<span>{item.rank}&#46;</span>
<span>{item.title}</span>
</li>
);
}
35 changes: 35 additions & 0 deletions src/app/[userNickname]/_components/Categories.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { style } from '@vanilla-extract/css';

export const container = style({
padding: '2.1rem 0 1.5rem 1.5rem',

display: 'flex',
alignItems: 'flex-start',
gap: '1.2rem',

overflow: 'scroll',
msOverflowStyle: 'none',
'::-webkit-scrollbar': {
display: 'none',
},
});

export const button = style({
padding: '0.8rem 1.2rem',

backgroundColor: '#FFF',
borderRadius: '5rem',
border: '1px solid #DEDEDE',

fontSize: '1.6rem',
fontWeight: '500',
color: '#828282',
letterSpacing: '-0.48px',
whiteSpace: 'nowrap',
});

export const variant = style({
backgroundColor: '#0047FF',
color: '#FFF',
border: 'none',
});
52 changes: 52 additions & 0 deletions src/app/[userNickname]/_components/Categories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use client';

/**
TODO
- [ ] api 연동
- [ ] 클릭했을때 로직 (상위요소에 핸들러 고민) (리팩토링)
*/
import { KINDS } from '../mockData/categories'; // 삭제 예정

import { useState } from 'react';
// import { useQuery } from '@tanstack/react-query'; // 주석 import 나중에 사용 예정

import * as styles from './Categories.css';

// import { getCategories } from '@/app/_api/getCategories';
// import { CategoriesType } from '@/lib/types/categoriesType';
// import { queryKeys } from '@/lib/constants/queryKeys';

interface CategoriesProps {
onClick: (kind: string) => void;
}

const DEFAULT_CATEGORY = '전체'; // 나중에 constants 파일로 분리

export default function Categories({ onClick }: CategoriesProps) {
const [selected, setSelected] = useState(DEFAULT_CATEGORY);

// 1. 카테고리 api 요청
// const { data } = useQuery<CategoriesType>({
// queryKey: [queryKeys.getCategories],
// queryFn: getCategories,
// });

const handleChangeCategory = (kind: string) => () => {
onClick(kind);
setSelected(kind);
};

return (
<div className={styles.container}>
{KINDS.map((kind) => (
<button
key={kind.codeValue}
onClick={handleChangeCategory(kind.korNameValue)}
className={`${styles.button} ${kind.korNameValue === selected ? styles.variant : ''}`}
>
{kind.korNameValue}
</button>
))}
</div>
);
}
59 changes: 59 additions & 0 deletions src/app/[userNickname]/_components/Content.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { style } from '@vanilla-extract/css';

export const container = style({
width: '100%',
marginTop: '40rem',

position: 'absolute',
top: 0,

backgroundColor: '#FFF',
borderTopLeftRadius: '2.5rem',
borderTopRightRadius: '2.5rem',
});

export const options = style({
height: '6.4rem',
display: 'flex',
borderBottom: '1px solid rgba(0, 0, 0, 0.10)',
});

export const link = style({
flexGrow: '1',
});

export const button = style({
width: '100%',
height: '100%',

backgroundColor: 'white',
borderTop: '1px solid rgba(0, 0, 0, 0.25)',
borderBottom: '1px solid rgba(0, 0, 0, 0.10)',

fontSize: '1.6rem',
fontWeight: '500',
});

export const leftButton = style([
button,
{
paddingLeft: '5.75rem',
borderTopLeftRadius: '2.5rem',
},
]);

export const rightButton = style([
button,
{
paddingRight: '5.75rem',
borderTopRightRadius: '2.5rem',
},
]);

export const variant = style({
borderBottom: '1px solid #0047FF',
});

export const cards = style({
padding: '2.1rem',
});
54 changes: 54 additions & 0 deletions src/app/[userNickname]/_components/Content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use client';

/**
TODO
- [ ] api 연동
- [ ] 무한스크롤 적용
- [ ] 피드페이지 스켈레톤 ui 적용
*/

import Link from 'next/link';
import { MasonryGrid } from '@egjs/react-grid';

import * as styles from './Content.css';

import { ListType, UserType } from '../mockData/mockDataTypes'; // 삭제 예정
import { LISTS_ME } from '../mockData/lists'; // 삭제 예정

import Card from './Card';
import Categories from './Categories';

interface ContentProps {
user: UserType;
type: string;
}

export default function Content({ user, type }: ContentProps) {
// 1. props로 받아온 userId, type으로 피드 정보 가져오는 api 요청

const handleFetchListsOnCategory = (kind: string) => {
// console.log(type, kind); // 삭제 예정
// 2. userId, type, category로 피드 정보 가져오는 api 요청
};

return (
<div className={styles.container}>
<div className={styles.options}>
<Link href={`/${user.nickname}/mylist`} className={styles.link}>
<button className={`${styles.leftButton} ${type === 'my' ? styles.variant : ''}`}>마이 리스트</button>
</Link>
<Link href={`/${user.nickname}/collabolist`} className={styles.link}>
<button className={`${styles.rightButton} ${type === 'collabo' ? styles.variant : ''}`}>콜라보 리스트</button>
</Link>
Comment on lines +37 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

옵셔널한 경우에는 이렇게 클래스명 2개 사용해서 스타일링을 할 수 있겠군요...
배워갑니다!! (제 코드 수정하러 갑니다🏃‍♀️)

</div>
<Categories onClick={handleFetchListsOnCategory} />
<div className={styles.cards}>
<MasonryGrid gap={16} defaultDirection={'end'} align={'start'}>
{LISTS_ME.map((list: ListType) => (
<Card key={list.listId} list={list} isOwner={user.isOwner} />
))}
</MasonryGrid>
</div>
</div>
);
}
Loading