Skip to content

Commit 06f235a

Browse files
authored
Merge pull request #85 from DevKor-github/fix/#54/item-detail
[#54] μ•„μ΄ν…œ 상세 νŽ˜μ΄μ§€ λ””μžμΈ μˆ˜μ • !
2 parents 5c35b95 + 70a91c7 commit 06f235a

16 files changed

Lines changed: 213 additions & 61 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { ItemInfoInterface } from '@/features/detail/types';
2+
import Token from '@/common/components/Token';
3+
import { TAG_TYPES_MAP, type TagType } from '@/libs/types/item';
4+
5+
interface Props {
6+
itemInfo: ItemInfoInterface;
7+
}
8+
const ItemTokenList = ({ itemInfo }: Props) => {
9+
const tags: TagType[] = [
10+
...itemInfo.transactionTypes,
11+
...itemInfo.productTypes,
12+
itemInfo.color,
13+
...itemInfo.tradeMethods,
14+
itemInfo.quality,
15+
itemInfo.size,
16+
];
17+
18+
return (
19+
<>
20+
{tags.map(type => (
21+
<Token key={type}>{TAG_TYPES_MAP[type]}</Token>
22+
))}
23+
</>
24+
);
25+
};
26+
export default ItemTokenList;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import VerifyIcon from '@/libs/assets/VerifyIcon';
2+
3+
import * as s from './style.css';
4+
5+
// TODO: λ””μžμΈ μ—…λ°μ΄νŠΈ ... μ§€κΈˆ 인증된 μœ μ €κ°€ μ—†μ–΄μ„œ 확인이 λΆˆκ°€λŠ₯ γ…‹γ…‹
6+
const SchoolVerifiedTag = () => {
7+
return (
8+
<span className={s.Container}>
9+
<p>학ꡐ인증</p>
10+
<VerifyIcon />
11+
</span>
12+
);
13+
};
14+
export default SchoolVerifiedTag;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { css } from '@styled-system/css';
2+
3+
export const Container = css({
4+
padding: '0.1rem 0.275rem',
5+
fontSize: '0.625rem',
6+
fontWeight: 400,
7+
lineHeight: 1.4,
8+
letterSpacing: '-0.025rem',
9+
color: 'main',
10+
display: 'flex',
11+
alignItems: 'center',
12+
gap: '0.22rem',
13+
borderRadius: '0.25rem',
14+
backgroundColor: 'main-26',
15+
'& > p': {
16+
color: '100',
17+
},
18+
});
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* κ²Œμ‹œκΈ€ μž‘μ„± μ‹œκ°„ ν‘œμ‹œ ν•¨μˆ˜
3+
* Date 객체λ₯Ό λ°›μ•„ μ•Œμž˜λ”±ν•˜κ²Œ ν•œκΈ€μ„ λ±‰μ–΄μ€Œ
4+
*/
5+
export const getKoreanRelativeTime = (a: Date) => {
6+
const milliSeconds = new Date().getTime() - a.getTime();
7+
const seconds = milliSeconds / 1000;
8+
const minutes = seconds / 60;
9+
const hours = minutes / 60;
10+
const days = hours / 24;
11+
const weeks = days / 7;
12+
const months = days / 30;
13+
const years = days / 365;
14+
15+
if (seconds < 60) return `방금 μ „`;
16+
if (minutes < 60) return `${Math.floor(minutes)}λΆ„ μ „`;
17+
if (hours < 24) return `${Math.floor(hours)}μ‹œκ°„ μ „`;
18+
if (days < 7) return `${Math.floor(days)}일 μ „`;
19+
if (weeks < 5) return `${Math.floor(weeks)}μ£Ό μ „`;
20+
if (months < 12) return `${Math.floor(months)}κ°œμ›” μ „`;
21+
return `${Math.floor(years)}λ…„ μ „`;
22+
};

β€Žsrc/common/utils/toKST.tsβ€Ž

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* UTC Dateλ₯Ό KST Date둜 λ³€ν™˜
3+
*/
4+
export const toKST = (date: Date) => {
5+
// KST : UTC + 9
6+
return new Date(date.getTime() + 9 * 60 * 60 * 1000);
7+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import client from '@/common/utils/client';
2+
import { useMutation, useQueryClient } from '@tanstack/react-query';
3+
4+
const deleteItem = async (itemId: number) => {
5+
const response = await client.delete(`/api/v1/item/${itemId}`);
6+
return response.data;
7+
};
8+
9+
export const useDeleteItem = () => {
10+
const queryClient = useQueryClient();
11+
12+
return useMutation({
13+
mutationFn: deleteItem,
14+
onSuccess: () => {
15+
queryClient.invalidateQueries({ queryKey: ['item-list'] });
16+
},
17+
});
18+
};
File renamed without changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import client from '@/common/utils/client';
2+
import { useMutation, useQueryClient } from '@tanstack/react-query';
3+
4+
const postLike = async (itemId: number) => {
5+
const response = await client.post(`/api/v1/like/${itemId}`);
6+
return response.data;
7+
};
8+
9+
export const usePostLike = () => {
10+
const queryClient = useQueryClient();
11+
12+
return useMutation({
13+
mutationFn: postLike,
14+
onSuccess: () => {
15+
queryClient.invalidateQueries({ queryKey: ['item-detail'] });
16+
queryClient.invalidateQueries({ queryKey: ['item-list'] });
17+
},
18+
});
19+
};

β€Žsrc/features/detail/components/DetailHeader/index.tsxβ€Ž

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
import { useNavigate } from 'react-router';
22
import * as s from './style.css';
3+
import { useDeleteItem } from '@/features/detail/apis/useDeleteItem';
34

4-
const DetailHeader = () => {
5+
interface Props {
6+
itemId: number;
7+
isMine: boolean;
8+
}
9+
const DetailHeader = ({ itemId, isMine }: Props) => {
510
const navigate = useNavigate();
11+
const { mutate: deleteItem } = useDeleteItem();
612

713
const goBack = () => navigate(-1);
814

15+
const handleMoreButtonClick = () => {
16+
// TODO: λͺ¨λ‹¬ λ„μš°λ“ κ°€ μ•‘μ…˜ μΆ”κ°€
17+
deleteItem(itemId, {
18+
onSuccess: () => {
19+
navigate(-1);
20+
},
21+
});
22+
};
23+
924
// TODO: Action μΆ”κ°€
1025
return (
1126
<>
@@ -14,8 +29,8 @@ const DetailHeader = () => {
1429
<header className={s.Container}>
1530
<button className={`mgc_left_line ${s.BackButton}`} onClick={goBack} aria-label="Go back" />
1631
<div className={s.RightSide}>
17-
<button className={`${s.RightButton} mgc_share_3_fill`} />
18-
<button className={`${s.RightButton} mgc_more_2_fill`} />
32+
<button className={`${s.RightButton} mgc_share_3_fill`} aria-label="Share item" />
33+
{isMine && <button className={`${s.RightButton} mgc_more_2_fill`} onClick={handleMoreButtonClick} />}
1934
</div>
2035
</header>
2136
</>

β€Žsrc/features/detail/components/PostContent/index.tsxβ€Ž

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,63 @@
1-
import Token from '@/common/components/Token';
21
import * as s from './style.css';
3-
import { PRODUCT_TYPES_MAP } from '@/libs/types/item';
2+
43
import type { ItemInfoInterface } from '@/features/detail/types';
4+
import { getKoreanRelativeTime } from '@/common/utils/getKoreanRelativeTime';
5+
import { toKST } from '@/common/utils/toKST';
6+
import ItemTokenList from '@/common/components/ItemTokenList';
57

68
interface Props {
79
itemInfo: ItemInfoInterface;
810
}
911
const PostContent = ({ itemInfo }: Props) => {
12+
const isRental = itemInfo.transactionTypes.includes('RENTAL');
13+
const isSale = itemInfo.transactionTypes.includes('SALE');
14+
1015
return (
1116
<div className={s.Container}>
1217
<div className={s.TextContainer}>
1318
<div className={s.Header}>
1419
<h1 className={s.Title}>{itemInfo.title}</h1>
1520
<div className={s.PriceContainer}>
16-
{/* TODO: νŒλ§€κΈ€μΈ 경우 어캐 보일까 */}
17-
<div className={s.PriceItem}>
18-
<label>λŒ€μ—¬λ£Œ</label>
19-
<p>{itemInfo.rentalFee.toLocaleString()}원</p>
20-
</div>
21-
{itemInfo.deposit !== 0 && (
21+
{isRental && (
22+
<div>
23+
<div className={s.PriceItem}>
24+
<label>λŒ€μ—¬λ£Œ</label>
25+
<p>{itemInfo.rentalFee.toLocaleString()}원</p>
26+
</div>
27+
{itemInfo.deposit !== 0 && (
28+
<div className={s.PriceItem}>
29+
<label>보증금</label>
30+
<p>{itemInfo.deposit.toLocaleString()}원</p>
31+
</div>
32+
)}
33+
</div>
34+
)}
35+
{isSale && (
2236
<div className={s.PriceItem}>
23-
<label>보증금</label>
24-
<p>{itemInfo.deposit.toLocaleString()}원</p>
37+
<label>νŒλ§€κ°€</label>
38+
<p>{itemInfo.salePrice.toLocaleString()}원</p>
2539
</div>
2640
)}
2741
</div>
2842
</div>
29-
{/* TODO: API μΈν„°νŽ˜μ΄μŠ€ μˆ˜μ • ν•„μš” */}
3043
<div className={s.PostInfoContainer}>
31-
<p>3일 μ „</p>
44+
<p>{getKoreanRelativeTime(toKST(new Date(itemInfo.repostDate)))}</p>
3245
<span />
3346
<div className={s.InteractionContainer}>
3447
<span className={s.InteractionItem}>
3548
<span className="mgc_heart_fill" />
36-
<p>12</p>
49+
<p>{itemInfo.likeCount}</p>
3750
</span>
3851
<span className={s.InteractionItem}>
3952
<span className="mgc_chat_2_fill" />
40-
<p>4</p>
53+
<p>{itemInfo.chatRoomCount}</p>
4154
</span>
4255
</div>
4356
</div>
44-
<div className={s.TextContent}>
45-
{itemInfo.description.split('\n').map((line, index) => (
46-
<p key={index}>{line}</p>
47-
))}
48-
</div>
57+
<div className={s.TextContent}>{itemInfo.description}</div>
4958
</div>
5059
<div className={s.TokenContainer}>
51-
{itemInfo.productTypes.map(type => (
52-
<Token key={type}>{PRODUCT_TYPES_MAP[type]}</Token>
53-
))}
60+
<ItemTokenList itemInfo={itemInfo} />
5461
</div>
5562
</div>
5663
);

0 commit comments

Comments
Β (0)