Skip to content

Commit df37dd9

Browse files
authored
Merge pull request #191 from DevKor-github/feat/#156/pick-page
[Feat] pick ํŽ˜์ด์ง€ ๊ตฌํ˜„
2 parents 0443be2 + cd9cfae commit df37dd9

File tree

23 files changed

+737
-30
lines changed

23 files changed

+737
-30
lines changed

โ€Žsrc/common/components/ItemTokenList/index.tsxโ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ interface Props {
1717
color?: Color;
1818
tradeMethods?: TradeMethods[];
1919
quality?: Quality;
20-
size: Size;
20+
size?: Size;
2121
};
2222
showCount?: number;
2323
}

โ€Žsrc/common/components/NoResult/index.tsxโ€Ž

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { cx } from '@styled-system/css';
33
import * as s from './style.css';
44

55
interface Props {
6-
type: 'search' | '404' | 'like' | 'chat';
6+
type: 'search' | '404' | 'like' | 'chat' | 'pick' | 'chat-list';
77
}
88
const NoResult = ({ type }: Props) => {
99
const icon = type === 'search' ? 'mgc_alert_fill' : 'mgc_puzzled_fill';
@@ -12,13 +12,17 @@ const NoResult = ({ type }: Props) => {
1212
if (type === 'search') return '๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์–ด์š”!';
1313
if (type === '404') return '์•—, ์ž˜๋ชป๋œ ์ ‘๊ทผ์ด์—์š”!';
1414
if (type === 'chat') return '์•„์ง ์‹œ์ž‘๋œ ๋Œ€ํ™”๊ฐ€ ์—†์–ด์š”!';
15-
return '๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์–ด์š”!';
15+
if (type === 'chat-list') return '์•„์ง ์‹œ์ž‘๋œ ๋Œ€ํ™”๊ฐ€ ์—†์–ด์š”!';
16+
if (type === 'pick') return '์•„์ง ์ƒ์„ฑ๋œ PICK์ด ์—†์–ด์š”!';
17+
return '์•„์ง ๊ด€์‹ฌ๋ชฉ๋ก์ด ์—†์–ด์š”!';
1618
})();
1719

1820
const description = (() => {
1921
if (type === 'search') return '๋‹ค๋ฅธ ํ‚ค์›Œ๋“œ๋กœ ๋‹ค์‹œ ๊ฒ€์ƒ‰ํ•ด ์ฃผ์„ธ์š”.';
2022
if (type === '404') return '๋‹ค๋ฅธ ๊ฒฝ๋กœ๋กœ ์ ‘์†ํ•ด ์ฃผ์„ธ์š”.';
2123
if (type === 'chat') return '์ƒํ’ˆ์— ๊ด€์‹ฌ ์žˆ๋Š” ์‚ฌ๋žŒ์ด ๋‚˜ํƒ€๋‚˜๋ฉด\n์ฑ„ํŒ…์„ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.';
24+
if (type === 'chat-list') return '์ฑ„ํŒ…์„ ์‹œ์ž‘ํ•ด ๋ณด์„ธ์š”.';
25+
if (type === 'pick') return '์•ฝ์†์„ ์ƒ์„ฑํ•ด ๋ณด์„ธ์š”.';
2226
return '์›ํ•˜๋Š” ์ƒํ’ˆ์„ ๊ด€์‹ฌ๋ชฉ๋ก์— ๋‹ด์•„ ๋ณด์„ธ์š”.';
2327
})();
2428

โ€Žsrc/common/utils/parseDate.tsโ€Ž

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { ko } from 'date-fns/locale/ko';
22
import { format } from 'date-fns';
33

4-
export const parseDate = (raw: string) => {
4+
export const parseChatDate = (raw: string) => {
55
const date = new Date(raw);
66
return format(date, 'yyyy๋…„ MM์›” dd์ผ eeee', { locale: ko });
77
};
88

9+
export const parsePickDate = (raw: string) => {
10+
const date = new Date(raw);
11+
return format(date, 'yyyy. MM. dd. eeee', { locale: ko });
12+
};
13+
914
export const parseTime = (raw: string) => {
1015
const date = new Date(raw);
1116
return format(date, 'a h:mm', { locale: ko });

โ€Žsrc/features/chatRoom/components/ChatMessageContents/index.tsxโ€Ž

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { parseDate, parseTime } from '@/common/utils/parseDate';
2+
import { parseChatDate, parseTime } from '@/common/utils/parseDate';
33
import * as s from './style.css';
44

55
import type { ChatInterface } from '@/features/chatRoom/types';
@@ -23,14 +23,14 @@ const ChatMessageContents = ({ chat, index, messages, myUserId, isOpponentOnline
2323
const isRead = isOpponentOnline || isBefore(chat.createdAt, opponentLastEnterAt);
2424

2525
const time = parseTime(chat.createdAt);
26-
const date = parseDate(chat.createdAt);
26+
const date = parseChatDate(chat.createdAt);
2727
const isMine = chat.userId === myUserId;
2828
const isNotification = chat.isNotification;
2929

3030
const prevChat = messages[index - 1];
3131
const nextChat = messages[index + 1];
3232

33-
const prevDate = prevChat ? parseDate(prevChat.createdAt) : null;
33+
const prevDate = prevChat ? parseChatDate(prevChat.createdAt) : null;
3434
const prevIsMine = prevChat ? (prevChat.isNotification ? false : prevChat.userId === myUserId) : false;
3535

3636
const isNewDate = date !== prevDate;

โ€Žsrc/features/chatRoom/components/ChatRoomLayout/ChatRoomContent/index.tsxโ€Ž

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ const ChatRoomContent = ({ data }: Props) => {
2323
restOpponentLastEnterAt: data.opponentLastEnterAt,
2424
});
2525

26-
const { data: chats = [], hasNextPage, isFetching, isFetchingNextPage, fetchNextPage } = useGetLoadChat(chatRoomId);
26+
const {
27+
data: chats = [],
28+
hasNextPage,
29+
isFetching,
30+
isSuccess,
31+
isFetchingNextPage,
32+
fetchNextPage,
33+
} = useGetLoadChat(chatRoomId);
2734

2835
// ๋ฉ”์‹œ์ง€ ํ•ฉ์น˜๊ธฐ
2936
const messages = [[...chats].reverse(), ...newMessages].flat();
@@ -39,11 +46,15 @@ const ChatRoomContent = ({ data }: Props) => {
3946

4047
useEffect(() => {
4148
// ์ฒซ ๋ฐฉ๋ฌธ์‹œ์— ์Šคํฌ๋กค ์œ„์น˜ ์ดˆ๊ธฐํ™”
42-
if (scrollRef.current && !isFetching && !isMountedRef.current) {
49+
if (scrollRef.current && isSuccess && !isMountedRef.current) {
50+
console.log(scrollRef.current.scrollTop);
51+
console.log(scrollRef.current.scrollHeight);
4352
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
53+
console.log(scrollRef.current.scrollTop);
54+
console.log(scrollRef.current.scrollHeight);
4455
isMountedRef.current = true;
4556
}
46-
}, [isFetching]);
57+
}, [isSuccess]);
4758

4859
useEffect(() => {
4960
// ํŽ˜์ด์ง€๋„ค์ด์…˜์‹œ์— ์Šคํฌ๋กค ๋ณด์ •

โ€Žsrc/features/pick/components/DetailBottom/index.tsxโ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ const DetailBottom = ({ id, itemId, isCreator, pickState }: Props) => {
3232
const confirmPick = () => {
3333
confirm(id, {
3434
onSuccess: () => {
35-
navigate(`/pick-detail/${id}`);
35+
// TODO: ์ฑ„ํŒ…๋ฐฉ ์ด๋™
36+
navigate(`/chat`);
3637
},
3738
});
3839
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import client from '@/common/utils/client';
2+
import { QUERY_KEYS } from '@/libs/queryKeys';
3+
import { useInfiniteQuery } from '@tanstack/react-query';
4+
import type { AppointmentInterface } from '../types';
5+
6+
export interface GetAppointmentReuest {
7+
pageSize: number;
8+
subject: 'REQUESTER' | 'OWNER';
9+
period: 'ALL' | 'YEAR' | 'SIX_MONTH' | 'WEEK';
10+
cursorState?: string | null;
11+
cursorId?: string | null;
12+
cursorDate?: string | null;
13+
}
14+
15+
export interface GetAppointmentResponst {
16+
message: string;
17+
data: {
18+
appointmentInfoList: AppointmentInterface[];
19+
pageInfo: {
20+
cursorState: string | null;
21+
cursorDate: string | null;
22+
cursorId: string | null;
23+
hasNext: boolean;
24+
};
25+
};
26+
}
27+
28+
interface PageParam {
29+
cursorState?: string | null;
30+
cursorId?: string | null;
31+
cursorDate?: string | null;
32+
}
33+
34+
const getAppointment = async (params: GetAppointmentReuest) => {
35+
const res = await client.get<GetAppointmentResponst>('/api/v1/appointment', {
36+
params,
37+
});
38+
39+
return res.data;
40+
};
41+
42+
const useGetAppointment = (params: GetAppointmentReuest) => {
43+
const initialPageParam: PageParam = {};
44+
45+
return useInfiniteQuery({
46+
queryKey: [QUERY_KEYS.APPOINTMENT_LIST, params],
47+
queryFn: ({ pageParam }) => getAppointment({ ...params, ...pageParam }),
48+
getNextPageParam: lastPage =>
49+
lastPage.data.pageInfo.hasNext
50+
? {
51+
cursorId: lastPage.data.pageInfo.cursorId,
52+
cursorLastChatAt: lastPage.data.pageInfo.cursorDate,
53+
}
54+
: undefined,
55+
initialPageParam,
56+
select: data => data.pages.flatMap(page => page.data.appointmentInfoList),
57+
staleTime: 0,
58+
});
59+
};
60+
61+
export default useGetAppointment;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { cx } from '@styled-system/css';
2+
import * as s from './style.css';
3+
import { useNavigate } from 'react-router';
4+
5+
interface BtnProps {
6+
type: 'review' | 'chat';
7+
nickname?: string;
8+
chatRoomId?: number;
9+
appointmentId: number;
10+
isComplete?: boolean;
11+
}
12+
13+
const Btn = ({ type, isComplete, chatRoomId, appointmentId, nickname }: BtnProps) => {
14+
const title = type === 'chat' ? '์ฑ„ํŒ…์ฐฝ' : isComplete ? '์ž‘์„ฑ์™„๋ฃŒ' : '๋ฆฌ๋ทฐ์ž‘์„ฑ';
15+
const iconColor = type === 'chat' ? '54' : isComplete ? 'systemGray2' : '100';
16+
const icon = type === 'chat' ? 'mgc_chat_1_fill' : isComplete ? 'mgc_choice_fill' : 'mgc_message_4_fill';
17+
18+
const bg = type === 'chat' ? 'systemGray2' : isComplete ? 'systemGray5' : 'main';
19+
const color = type === 'review' && isComplete ? '54' : '100';
20+
21+
const navigate = useNavigate();
22+
23+
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
24+
e.stopPropagation();
25+
e.preventDefault();
26+
27+
if (type === 'chat') {
28+
alert('์ฑ„ํŒ…์ฐฝ์œผ๋กœ ์—ฐ๊ฒฐ');
29+
navigate(`/chatroom/${chatRoomId}`);
30+
31+
return;
32+
}
33+
if (type === 'review') {
34+
// ๋ฆฌ๋ทฐ ์ž‘์„ฑ ์ „์ผ ๋•Œ,
35+
if (!isComplete) {
36+
alert('๋ฆฌ๋ทฐ ์ž‘์„ฑ ํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐ');
37+
navigate(`/review/${appointmentId}`, {
38+
state: {
39+
nickname: nickname,
40+
},
41+
});
42+
return;
43+
}
44+
// ๋ฆฌ๋ทฐ ์ž‘์„ฑ ์™„๋ฃŒ ๋™์ž‘
45+
alert('๋ฆฌ๋ทฐ ์ž‘์„ฑ ์™„๋ฃŒ');
46+
}
47+
};
48+
49+
return (
50+
<button className={s.Container({ bg, color })} onClick={onClick}>
51+
<div className={cx(icon, s.Icon({ iconColor }))} />
52+
{title}
53+
</button>
54+
);
55+
};
56+
57+
export default Btn;
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { cva } from '@styled-system/css';
2+
3+
export const Container = cva({
4+
base: {
5+
display: 'flex',
6+
padding: '0.25rem 0.375rem',
7+
justifyContent: 'center',
8+
alignItems: 'center',
9+
gap: '0.375rem',
10+
11+
borderRadius: '0.375rem',
12+
bg: 'systemGray2',
13+
14+
fontFamily: 'Pretendard',
15+
fontSize: '0.75rem',
16+
fontStyle: 'normal',
17+
fontWeight: 500,
18+
lineHeight: 'normal',
19+
letterSpacing: '-0.03rem',
20+
},
21+
variants: {
22+
bg: {
23+
main: {
24+
bg: 'main',
25+
},
26+
systemGray2: {
27+
bg: 'systemGray2',
28+
},
29+
systemGray5: {
30+
bg: 'systemGray5',
31+
},
32+
},
33+
color: {
34+
'100': {
35+
color: '100',
36+
},
37+
'54': {
38+
color: '54',
39+
},
40+
},
41+
},
42+
});
43+
44+
export const Icon = cva({
45+
base: {
46+
fontSize: '1rem',
47+
},
48+
variants: {
49+
iconColor: {
50+
systemGray2: {
51+
color: 'systemGray2',
52+
},
53+
'100': {
54+
color: '100',
55+
},
56+
'54': {
57+
color: '54',
58+
},
59+
},
60+
},
61+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import ItemTokenList from '@/common/components/ItemTokenList';
2+
import getImageUrl from '@/common/utils/getImageUrl';
3+
import PriceToken from '@/features/home/components/ItemCard/PriceToken';
4+
import * as s from './style.css';
5+
import TradeInfo from '../TradeInfo';
6+
import Btn from '../Btn';
7+
import type { AppointmentInterface } from '../../types';
8+
import { Link } from 'react-router';
9+
import { parsePickDate } from '@/common/utils/parseDate';
10+
11+
interface PickItemListProps {
12+
data: AppointmentInterface;
13+
}
14+
15+
const PickItemList = ({ data }: PickItemListProps) => {
16+
const isSuccess = data.state === 'SUCCESS';
17+
const date = parsePickDate(data.rentalDate);
18+
19+
return (
20+
<div className={s.TradeInfo}>
21+
<TradeInfo isSuccess={isSuccess} date={date} />
22+
<Link className={s.Container} to={`/pick-detail/${data.appointmentId}`}>
23+
<img className={s.Image} src={getImageUrl(data.imageUrl)} aria-hidden />
24+
25+
<div className={s.Wrapper}>
26+
<div className={s.Heart}>
27+
<div className={s.Info}>
28+
<div className={s.Header}>
29+
<h2 className={s.Title}>{data.title}</h2>
30+
<div className={s.Price}>
31+
{data.type === 'RENTAL' && <PriceToken price={data.price} deposit={data.deposit} />}
32+
{data.type === 'SALE' && <PriceToken price={data.price} />}
33+
</div>
34+
35+
<div className={s.Tokens}>
36+
<ItemTokenList
37+
showCount={3}
38+
itemInfo={{
39+
productTypes: data.productTypes,
40+
// transactionTypes: data.type,
41+
// quality: 'BEST',
42+
// size: 'L',
43+
// color: 'BLACK',
44+
// tradeMethods: ['DIRECT'],
45+
}}
46+
/>
47+
</div>
48+
</div>
49+
</div>
50+
</div>
51+
52+
<div className={s.Footer}>
53+
<Btn
54+
type="chat"
55+
appointmentId={data.appointmentId}
56+
// nickname={data.nickname}
57+
// chatRoomId={data.chatRoomId}
58+
/>
59+
{isSuccess && (
60+
<Btn
61+
type="review"
62+
appointmentId={data.appointmentId}
63+
// nickname={data.nickname}
64+
// isComplete={data.isComplete}
65+
// chatRoomId={data.chatRoomId}
66+
/>
67+
)}
68+
</div>
69+
</div>
70+
</Link>
71+
<div className={s.Border} />
72+
</div>
73+
);
74+
};
75+
76+
export default PickItemList;

0 commit comments

Comments
ย (0)