Skip to content

Commit 5b71c8f

Browse files
committed
merge: refactor/chat 브랜치 병합 후 마이그레이션 반영
1 parent def852a commit 5b71c8f

8 files changed

Lines changed: 181 additions & 88 deletions

File tree

‎app/App.tsx‎

Lines changed: 0 additions & 20 deletions
This file was deleted.

‎app/data/chat-room.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const rooms: ChatRoom[] = [
1010
status: "matching",
1111
logoUrl: "",
1212
type: "sent",
13+
isCollaborating: true,
1314
},
1415
{
1516
id: "2",
@@ -20,6 +21,7 @@ export const rooms: ChatRoom[] = [
2021
status: "reviewing",
2122
logoUrl: "",
2223
type: "received",
24+
isCollaborating: false,
2325
},
2426
];
2527

‎app/routes/chat/ChatList.tsx‎

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { formatKoreanDateTime } from "../../utils/dateTime";
2+
import { useNavigate } from "react-router";
3+
4+
export type ChatRoom = {
5+
id: string;
6+
brandName: string;
7+
lastMessage: string;
8+
updatedAt: string;
9+
unreadCount: number;
10+
logoUrl: string;
11+
type: "sent" | "received";
12+
isCollaborating: boolean;
13+
};
14+
15+
export function ChatList({ rooms }: { rooms: ChatRoom[] }) {
16+
return (
17+
<div className="flex flex-col gap-[10px]">
18+
{rooms.map((room) => (
19+
<ChatListItem key={room.id} room={room} />
20+
))}
21+
</div>
22+
);
23+
}
24+
25+
export function ChatListItem({ room }: { room: ChatRoom }) {
26+
27+
const { dateText, timeText } = formatKoreanDateTime(room.updatedAt);
28+
const navigate = useNavigate();
29+
30+
return (
31+
<button
32+
type="button"
33+
className=" w-full max-w-[420px] rounded-[10px] bg-white px-4 py-[14px] flex items-start gap-[14px] text-left active:bg-[#F2F2F5]"
34+
onClick={() => navigate(`/rooms/${room.id}`)}
35+
>
36+
{/* 왼쪽 로고 */}
37+
<div className="w-[43px] h-[43px] rounded-[10px] bg-white border border-[#E6E6F3] flex items-center justify-center overflow-hidden shrink-0">
38+
{room.logoUrl ? (
39+
<img
40+
src={room.logoUrl}
41+
alt={`${room.brandName} 로고`}
42+
className="w-full h-full object-contain"
43+
/>
44+
) : (
45+
<span className="text-callout3 text-text-gray3">
46+
{room.brandName.slice(0, 2)}
47+
</span>
48+
)}
49+
</div>
50+
51+
{/* 중간 텍스트 영역 */}
52+
<div className="w-[227px] min-w-0">
53+
{/* 1줄: 브랜드명 + 상태 뱃지 */}
54+
<div className="flex items-center gap-2">
55+
<div className="text-title1 text-text-black font-Pretendard truncate">
56+
{room.brandName}
57+
</div>
58+
59+
{room.isCollaborating && (
60+
<span
61+
className={`
62+
bg-[#B7B7F380] text-[#6666E5]
63+
text-[10px]
64+
text-Medium
65+
px-[6px] py-[2px]
66+
rounded-[5px]
67+
shrink-0
68+
`}
69+
>
70+
협업 중
71+
</span>
72+
)}
73+
</div>
74+
75+
{/* 2줄 미리보기 */}
76+
<div className="text-[12px] mt-2.5 text-Medium text-text-gray3 line-clamp-2">
77+
{room.lastMessage}
78+
</div>
79+
</div>
80+
81+
{/* 오른쪽 영역: unread + 날짜/시간 */}
82+
<div className="flex flex-col items-end justify-between w-[68px] h-[65px] shrink-0">
83+
{/* 안 읽음 배지 */}
84+
{room.unreadCount > 0 ? (
85+
<div className="w-[20px] h-[20px] rounded-full bg-[#6666E5] text-[14px] text-white text-Semibold flex items-center justify-center">
86+
{room.unreadCount}
87+
</div>
88+
) : (
89+
<div className="w-5 h-5" />
90+
)}
91+
92+
{/* 날짜/시간 */}
93+
<div className="text-Medium text-[10px] text-[#A0A0A0] text-right leading-tight">
94+
<div>{dateText}</div>
95+
<div>{timeText}</div>
96+
</div>
97+
</div>
98+
</button>
99+
);
100+
}
101+
102+
export default ChatList;

‎app/routes/chat/chat-content.tsx‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { useState, useMemo } from "react";
2-
import { type SortOption, SORT_LABEL } from "./types/SortOption";
2+
import { SORT_LABEL, type SortOption } from "./components/SortingSheetConstant";
33
import { rooms } from "../../data/chat-room";
44
import ChatListHeader from "./components/ChatListHeader";
55
import SortFilterSheet from "./components/SortingSheet";
6-
import ChatList from "./components/ChatList";
6+
import ChatList from "./ChatList";
77
import { EmptyChatState } from "./components/EmptyState";
88
import { useHideBottomTab } from "../../hooks/useHideBottomTab";
99

@@ -21,21 +21,19 @@ function ChatPage() {
2121
return rooms.filter((room) => room.type === activeTab);
2222
}, [activeTab]);
2323

24-
// 정렬 적용
24+
// 정렬 + (필요시) 협업중 필터
2525
const sortedRooms = useMemo(() => {
26-
27-
// 받은/보낸 필터링
2826
let filtered = filteredRooms;
2927

30-
// latest가 아니면 status로 한 번 더 필터
31-
if (sort !== "latest") {
32-
filtered = filteredRooms.filter((room) => room.status === sort);
28+
// 협업중만 보기
29+
if (sort === "collaborating") {
30+
filtered = filteredRooms.filter((room) => room.isCollaborating);
3331
}
3432

3533
const copy = [...filtered];
36-
copy.sort(
37-
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
38-
);
34+
copy.sort((a, b) => {
35+
return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
36+
});
3937

4038
return copy;
4139
}, [filteredRooms, sort]);

‎app/routes/chat/components/ChatListHeader.tsx‎

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import searchIcon from "../../../assets/search2.svg";
2+
import closeIcon from "../../../assets/cancel.svg";
3+
import { useState } from "react";
14

25
export function ChatListHeader({
3-
activeTab,
4-
setActiveTab,
56
sortLabel,
67
onClickSort,
78
}: {
@@ -10,48 +11,60 @@ export function ChatListHeader({
1011
sortLabel: string;
1112
onClickSort: () => void;
1213
sortOpen: boolean;
14+
1315
}) {
16+
const [query, setQuery] = useState("");
17+
1418
return (
15-
<div className="flex items-center justify-between mb-4">
16-
{/* 탭 */}
17-
<div className="flex gap-4">
18-
<button className={`
19-
text-Medium
20-
${activeTab === "sent" ? "!text-[#6666E5]" : "text-gray3"}
21-
`}
22-
onClick={() => setActiveTab("sent")}
23-
>
24-
보낸 제안
25-
</button>
26-
<button className={`
27-
text-Medium
28-
${activeTab === "received" ? "!text-[#6666E5]" : "text-gray3"}
29-
`}
30-
onClick={() => setActiveTab("received")}
19+
20+
<div className="flex flex-col gap-4 border-b border-text-gray5">
21+
22+
{/* 검색 입력창 */}
23+
<div className="flex items-center w-full relative bg-bg-w border border-core-2 rounded-[8px] px-3 py-2">
24+
<img src={searchIcon} alt="search" className="w-4 h-4 opacity-40" />
25+
26+
<input
27+
className="flex-1 bg-transparent mx-2 outline-none text-body1 text-center placeholder:text-text-gray3"
28+
placeholder="검색어 입력"
29+
value={query}
30+
onChange={(e) => setQuery(e.target.value)}
31+
/>
32+
33+
<button
34+
onClick={() => setQuery("")}
35+
className="flex-shrink-0"
3136
>
32-
받은 제안
37+
<img src={closeIcon} alt="close" />
3338
</button>
39+
3440
</div>
3541

36-
{/* 정렬 버튼 - 텍스트는 sortLabel */}
37-
<button
38-
onClick={onClickSort}
39-
className="flex items-center gap-1 w-fit h-7 px-3 rounded-full border border-text-[#E6E6F3] text-[14px] font-Pretendard text-text-gray2 bg-white"
40-
>
41-
{sortLabel}
42-
<svg
43-
xmlns="http://www.w3.org/2000/svg"
44-
viewBox="0 0 20 20"
45-
fill="currentColor"
46-
className="w-4 h-4 text-text-gray3"
42+
<div className="flex items-center pt-2 pb-4 justify-between">
43+
44+
<div className="px-1 text-[16px] leading-[20px] font-Semibold text-black">
45+
채팅 목록
46+
</div>
47+
48+
{/* 정렬 버튼 - 텍스트는 sortLabel */}
49+
<button
50+
onClick={onClickSort}
51+
className="flex items-center gap-1 w-fit h-7 px-3 rounded-full border border-[#E6E6F3] text-[#5B5D6B] text-[14px] font-Pretendard text-text-gray2 bg-white"
4752
>
48-
<path
49-
fillRule="evenodd"
50-
d="M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.25a.75.75 0 01-1.06 0L5.21 8.29a.75.75 0 01.02-1.08z"
51-
clipRule="evenodd"
52-
/>
53-
</svg>
54-
</button>
53+
{sortLabel}
54+
<svg
55+
xmlns="http://www.w3.org/2000/svg"
56+
viewBox="0 0 20 20"
57+
fill="currentColor"
58+
className="w-4 h-4 text-text-gray3"
59+
>
60+
<path
61+
fillRule="evenodd"
62+
d="M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.71a.75.75 0 111.06 1.06l-4.24 4.25a.75.75 0 01-1.06 0L5.21 8.29a.75.75 0 01.02-1.08z"
63+
clipRule="evenodd"
64+
/>
65+
</svg>
66+
</button>
67+
</div>
5568
</div>
5669
);
5770
}

‎app/routes/chat/components/SortingSheet.tsx‎

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { type SortOption } from "../types/SortOption";
1+
import { type SortOption } from "./SortingSheetConstant";
2+
3+
// 정렬, 필터 옵션 (최신순/협업 중)
24

35
export function SortFilterSheet({
46
open,
@@ -26,8 +28,8 @@ export function SortFilterSheet({
2628
/>
2729

2830
{/* 시트 */}
29-
<div className="fixed left-1/2 -translate-x-1/2 top-[235px] w-full max-w-[430px] h-[530px] bg-white rounded-t-[12px] pt-[20px] px-4 flex flex-col">
30-
<div className="w-full max-w-[430px] h-[70px] fixed left-1/2 -translate-x-1/2">
31+
<div className="fixed left-1/2 -translate-x-1/2 top-[235px] w-full max-w-[375px] h-[530px] bg-white rounded-t-[12px] pt-[20px] px-4 flex flex-col">
32+
<div className="w-full max-w-[375px] h-[70px] fixed left-1/2 -translate-x-1/2">
3133
<div className="px-4 text-Medium text-text-black mb-3">정렬 필터</div>
3234

3335
<div className="bg-[#F3F4F8] px-4 py-3">
@@ -38,30 +40,19 @@ export function SortFilterSheet({
3840
onClick={() => onChange("latest")}
3941
/>
4042
<SortOptionButton
41-
label="매칭"
42-
active={value === "matching"}
43-
onClick={() => onChange("matching")}
44-
/>
45-
<SortOptionButton
46-
label="검토중"
47-
active={value === "reviewing"}
48-
onClick={() => onChange("reviewing")}
49-
/>
50-
<SortOptionButton
51-
label="거절"
52-
active={value === "rejected"}
53-
onClick={() => onChange("rejected")}
43+
label="협업 중"
44+
active={value === "collaborating"}
45+
onClick={() => onChange("collaborating")}
5446
/>
5547
</div>
5648
</div>
5749
</div>
58-
5950
<div className="flex-1" />
6051

61-
<div className="px-4 pb-6">
52+
<div className="flex justify-center py-6">
6253
<button
6354
onClick={onApply}
64-
className="w-full max-w-[420px] h-11 rounded-[12px] bg-[#6666E5] text-white text-SemiBold"
55+
className="w-full max-w-[327px] h-11 rounded-[12px] bg-[#6666E5] text-white text-SemiBold"
6556
>
6657
적용하기
6758
</button>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const SORT_LABEL = {
2+
latest: "최신순",
3+
collaborating: "협업 중",
4+
} as const;
5+
6+
export type SortOption = keyof typeof SORT_LABEL;

‎app/routes/chat/types/ChatRoom.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ export type ChatRoom = {
55
updatedAt: string;
66
unreadCount: number;
77
status: RoomStatus;
8-
logoUrl: string;
8+
logoUrl: string;
99
type: "sent" | "received";
10+
isCollaborating: boolean;
1011
};
1112

1213
export type RoomStatus = "matching" | "reviewing" | "rejected";

0 commit comments

Comments
 (0)