-
Notifications
You must be signed in to change notification settings - Fork 8
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
[GGFE-144] 상점 아이템 보관함 페이지 #917
Merged
The head ref may contain hidden characters: "GGFE-144-\uC0C1\uC810-\uC544\uC774\uD15C-\uBCF4\uAD00\uD568-\uD398\uC774\uC9C0"
Merged
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
9e8a1fd
[Feat] [GGFE-144] Inventory 타입 추가
yoouyeon 1facbab
[Feat] [GGFE-144] Inventory Component 추가
yoouyeon 7b7166f
[Test] [GGFE-144] items mock api 추가
yoouyeon 80f6a3c
[FEAT] [GGFE-144] Inventory api를 위한 InfinityScroll 함수 추가
yoouyeon 7376955
[Feat] [GGFE-144] api 호출 로직 적용
yoouyeon e06781a
[Feat] [GGFE-144] InventoryList 컴포넌트 추가
yoouyeon d11e40a
[Feat] [GGFE-144] InvetoryItem 컴포넌트 추가
yoouyeon b0ba5f5
[Style] [GGFE-144] 이미지, 상태 뱃지 스타일 적용
yoouyeon 6e962cd
[Style] [GGFE-144] 상단 tooltip 적용
yoouyeon 3a8d018
[Style] [GGFE-144] 글자 정렬
yoouyeon 2f9bdd8
[Feat] [GGFE-144] dummy button handler 추가
yoouyeon 619662f
[Feat] [GGFE-144] 무한스크롤 적용
yoouyeon 4c4337f
[Feat] [GGFE-144] 무한스크롤 컴포넌트 분리
yoouyeon dfa647b
[Feat] [GGFE-144] 빈 보관함인 경우 안내 메시지 추가
yoouyeon 52ae3e5
Merge branch 'main' into GGFE-144-상점-아이템-보관함-페이지
yoouyeon a6fa795
[Chore] [GGFE-144] 불필요한 클릭 핸들러 삭제
yoouyeon 0e6e238
[Chore] [GGFE-144] 불필요한 div 태그 삭제
yoouyeon a4af302
[Feat] [GGFE-144] coin 정보 props로 넘길 수 있게 수정
yoouyeon c54bb84
[Style] [GGFE-144] 아이템 보관함 이미지 contain으로 수정
yoouyeon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useEffect, useRef } from 'react'; | ||
import { RiPingPongFill } from 'react-icons/ri'; | ||
import styles from 'styles/store/Inventory.module.scss'; | ||
|
||
type InfiniteScrollComponentProps = { | ||
fetchNextPage: () => void; | ||
hasNextPage?: boolean; | ||
}; | ||
|
||
export function InfiniteScrollComponent({ | ||
fetchNextPage, | ||
hasNextPage, | ||
}: InfiniteScrollComponentProps) { | ||
const interactionObserverRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
if (!interactionObserverRef.current || !hasNextPage) return; | ||
const observer = new IntersectionObserver((entries) => { | ||
if (entries[0].isIntersecting) { | ||
fetchNextPage(); | ||
} | ||
}); | ||
observer.observe(interactionObserverRef.current); | ||
return () => observer.disconnect(); | ||
}, [fetchNextPage, hasNextPage]); | ||
|
||
if (!hasNextPage) return null; | ||
return ( | ||
<div className={styles.loadIcon} ref={interactionObserverRef}> | ||
<RiPingPongFill /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { QueryClientProvider, QueryClient } from 'react-query'; | ||
import { InventoryList } from './InventoryList'; | ||
|
||
export function Inventory() { | ||
const queryClient = new QueryClient(); | ||
|
||
return ( | ||
<QueryClientProvider client={queryClient}> | ||
<InventoryList />{' '} | ||
</QueryClientProvider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import Image from 'next/image'; | ||
import { Tooltip } from '@mui/material'; | ||
import { useRecoilValue } from 'recoil'; | ||
import { BsGiftFill, BsCircleFill } from 'react-icons/bs'; | ||
import { InventoryItem } from 'types/storeTypes'; | ||
import { userState } from 'utils/recoil/layout'; | ||
import styles from 'styles/store/Inventory.module.scss'; | ||
|
||
type inventoryItemProps = { | ||
item: InventoryItem; | ||
}; | ||
|
||
export function InvetoryItem({ item }: inventoryItemProps) { | ||
const user = useRecoilValue(userState); | ||
|
||
const { itemId, name, imageUrl, purchaserIntra, itemStatus } = item; | ||
|
||
function handleUseItem(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) { | ||
e.preventDefault(); | ||
console.log(`use item ${itemId}`); | ||
} | ||
|
||
function handleEditItem(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) { | ||
e.preventDefault; | ||
console.log(`edit item ${itemId}`); | ||
} | ||
|
||
return ( | ||
<div key={itemId} className={styles.inventoryItem}> | ||
<div className={styles.topBadgeContainer}> | ||
{user.intraId !== purchaserIntra ? ( | ||
<Tooltip title={`from ${purchaserIntra}`}> | ||
<button> | ||
<BsGiftFill /> | ||
</button> | ||
</Tooltip> | ||
) : ( | ||
<div></div> | ||
)} | ||
{itemStatus === 'USING' && ( | ||
<div className={styles.usingBadge}> | ||
<BsCircleFill /> 사용중 | ||
</div> | ||
)} | ||
</div> | ||
<div className={styles.overlay}> | ||
{itemStatus === 'USING' ? ( | ||
<button onClick={handleEditItem}>편집하기</button> | ||
) : ( | ||
<button onClick={handleUseItem}>사용하기</button> | ||
)} | ||
</div> | ||
<div className={styles.imgContainer}> | ||
<Image className={styles.img} src={imageUrl} alt={name} fill /> | ||
</div> | ||
<div className={styles.itemName}>{name}</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import React from 'react'; | ||
import { InvetoryItem } from './InventoryItem'; | ||
import { InfiniteScrollComponent } from './InfiniteScrollComponent'; | ||
import { InfinityScroll } from 'utils/infinityScroll'; | ||
import { mockInstance } from 'utils/mockAxios'; | ||
import styles from 'styles/store/Inventory.module.scss'; | ||
|
||
function fetchInventoryData(page: number) { | ||
return mockInstance.get(`items?page=${page}&size=${8}`).then((res) => { | ||
return res.data; | ||
}); | ||
} | ||
|
||
export function InventoryList() { | ||
const { data, error, isLoading, hasNextPage, fetchNextPage } = InfinityScroll( | ||
'inventory', | ||
fetchInventoryData, | ||
'JY03' | ||
); | ||
|
||
// TODO : 에러 컴포넌트 구체화 필요함. | ||
if (isLoading) return <div>Loading...</div>; | ||
if (error) return <div>{error.message}</div>; | ||
if (!data) return <div>No data</div>; | ||
|
||
return ( | ||
<div className={styles.inventoryList}> | ||
{data.pages.map((page, pageIndex) => ( | ||
<React.Fragment key={pageIndex}> | ||
{page.storageItemList.length === 0 ? ( | ||
<div className={styles.emptyMessage}> | ||
보유한 아이템이 없습니다. | ||
<br /> 상점 탭에서 아이템을 구입해 보세요! | ||
</div> | ||
) : ( | ||
page.storageItemList.map((item) => ( | ||
<InvetoryItem key={item.itemId} item={item} /> | ||
)) | ||
)} | ||
</React.Fragment> | ||
))} | ||
<InfiniteScrollComponent | ||
fetchNextPage={fetchNextPage} | ||
hasNextPage={hasNextPage} | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import type { NextApiRequest, NextApiResponse } from 'next'; | ||
import { InventoryData, InventoryItem } from 'types/storeTypes'; | ||
|
||
const item1: InventoryItem = { | ||
itemId: 1, | ||
name: '테스트1', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'hyobicho', | ||
itemStatus: 'USING', | ||
}; | ||
|
||
const item2: InventoryItem = { | ||
itemId: 2, | ||
name: '테스트2', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'hyungjpa', | ||
itemStatus: 'BEFORE', | ||
}; | ||
|
||
const item3: InventoryItem = { | ||
itemId: 3, | ||
name: '테스트3', | ||
imageUrl: | ||
'https://dodo.ac/np/images/thumb/1/17/NH_Balloon.jpg/600px-NH_Balloon.jpg', | ||
purchaserIntra: 'jeyoon', | ||
itemStatus: 'USING', | ||
}; | ||
|
||
const item4: InventoryItem = { | ||
itemId: 4, | ||
name: '테스트4', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'sangmipa', | ||
itemStatus: 'USED', | ||
}; | ||
|
||
const item5: InventoryItem = { | ||
itemId: 5, | ||
name: '테스트5', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'hyobicho', | ||
itemStatus: 'BEFORE', | ||
}; | ||
|
||
const item6: InventoryItem = { | ||
itemId: 6, | ||
name: '테스트6', | ||
imageUrl: | ||
'https://dodo.ac/np/images/thumb/1/17/NH_Balloon.jpg/600px-NH_Balloon.jpg', | ||
purchaserIntra: 'hyungjpa', | ||
itemStatus: 'BEFORE', | ||
}; | ||
|
||
const item7: InventoryItem = { | ||
itemId: 7, | ||
name: '테스트7', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'jeyoon', | ||
itemStatus: 'USING', | ||
}; | ||
|
||
const item8: InventoryItem = { | ||
itemId: 8, | ||
name: '테스트8', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'sangmipa', | ||
itemStatus: 'USED', | ||
}; | ||
|
||
const item9: InventoryItem = { | ||
itemId: 9, | ||
name: '테스트9', | ||
imageUrl: | ||
'https://dodo.ac/np/images/thumb/1/17/NH_Balloon.jpg/600px-NH_Balloon.jpg', | ||
purchaserIntra: 'hyobicho', | ||
itemStatus: 'BEFORE', | ||
}; | ||
|
||
const item10: InventoryItem = { | ||
itemId: 10, | ||
name: '테스트10', | ||
imageUrl: 'https://cdn.nookazon.com/nookazon/icons/leaf.png', | ||
purchaserIntra: 'hyungjpa', | ||
itemStatus: 'BEFORE', | ||
}; | ||
|
||
const storageItemList: InventoryItem[] = [ | ||
item1, | ||
item2, | ||
item3, | ||
item4, | ||
item5, | ||
item6, | ||
item7, | ||
item8, | ||
item9, | ||
item10, | ||
]; | ||
|
||
// 빈 인벤토리 | ||
const InventoryDataEmpty: InventoryData = { | ||
storageItemList: [], | ||
totalPage: 0, | ||
}; | ||
|
||
// 페이지 1개 | ||
const InventoryDataSingle: InventoryData = { | ||
storageItemList: storageItemList.slice(0, 4), | ||
totalPage: 1, | ||
}; | ||
|
||
// 페이지 2개 | ||
const InventoryDataDouble: InventoryData = { | ||
storageItemList: storageItemList, | ||
totalPage: 2, | ||
}; | ||
|
||
function pagination( | ||
page: number, | ||
size: number, | ||
storageItemList: InventoryItem[] | ||
): InventoryItem[] { | ||
const ret = []; | ||
for (let index = 0; index < storageItemList.length; index += size) { | ||
ret.push(storageItemList.slice(index, index + size)); | ||
} | ||
return ret[page - 1]; | ||
} | ||
|
||
export default function handler( | ||
req: NextApiRequest, | ||
res: NextApiResponse<InventoryData> | ||
) { | ||
// NOTE: 테스트 케이스에 맞춰서 다른 데이터를 보내주면 됩니다. | ||
// const InventoryData: InventoryData = InventoryDataEmpty; | ||
// const InventoryData: InventoryData = InventoryDataSingle; | ||
const InventoryData: InventoryData = InventoryDataDouble; | ||
|
||
const { page, size } = req.query; | ||
|
||
const ret: InventoryData = { | ||
storageItemList: [], | ||
totalPage: InventoryData.totalPage, | ||
}; | ||
if (page && size) { | ||
const storageItemList = pagination( | ||
Number(page as string), | ||
Number(size as string), | ||
InventoryData.storageItemList | ||
); | ||
if (InventoryData.totalPage !== 0) { | ||
ret.storageItemList = storageItemList; | ||
} | ||
} | ||
|
||
res.status(200).json(ret); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이런식으로 테스트데이터 보내는 건 생각도 못했네요 배워갑니다!