diff --git a/apis/data-contracts.ts b/apis/data-contracts.ts index 21b7d865..feb4d50b 100644 --- a/apis/data-contracts.ts +++ b/apis/data-contracts.ts @@ -383,10 +383,10 @@ export interface CreateReservationRequest { */ reservationDates: string[]; /** - * 운영 시간 (형식: HH:MM ~ HH:MM) + * 운영 시간 (형식: HH:MM-HH:MM) * @minLength 1 - * @pattern ^([01]\d|2[0-3]):([0-5]\d) ~ ([01]\d|2[0-3]):([0-5]\d)$ - * @example "15:00 ~ 16:00" + * @pattern ^([01]\d|2[0-3]):([0-5]\d)-([01]\d|2[0-3]):([0-5]\d)$ + * @example "15:00-16:00" */ operationHour: string; /** @@ -788,8 +788,8 @@ export interface ReservationResponse { */ reservationDates?: string[]; /** - * 운영 시간 (형식: HH:MM ~ HH:MM) - * @example "15:00 ~ 16:00" + * 운영 시간 (형식: HH:MM-HH:MM) + * @example "15:00-16:00" */ operationHour?: string; /** @@ -1461,11 +1461,8 @@ export interface FoodTruckDetailResponse { * @example "서울 광진구, 서울 강남구, 서울 영등포구" */ serviceAreas?: string; - /** - * 호출 가능 지역 코드 - * @example [1,2] - */ - regionCodes?: number[]; + /** 호출 가능 지역 정보 리스트 */ + regionCodes?: RegionResponse[]; /** * 푸드트럭 메뉴 카테고리 (라벨 리스트) * @example ["한식","분식"] @@ -1689,12 +1686,24 @@ export interface ChatRoomMetaDataResponse { * @example "맛있는푸드트럭" */ foodTruckName?: string; + /** + * 푸드트럭 식별자 + * @format int64 + * @example 1 + */ + foodTruckId?: number; /** * 채팅방과 관련된 예약 ID (있는 경우: ID 반환, 없는 경우: null) * @format int64 * @example 1 */ reservationId?: number; + /** + * 예약자(일반 유저) 식별자 + * @format int64 + * @example 2 + */ + memberId?: number; } export interface BaseResponseListChatMessageResponse { diff --git a/src/pages/chat-list/ChatList.tsx b/src/pages/chat-list/ChatList.tsx index e8fa3075..1f21adde 100644 --- a/src/pages/chat-list/ChatList.tsx +++ b/src/pages/chat-list/ChatList.tsx @@ -6,16 +6,21 @@ import { Icon } from '@icon/Icon'; import OverlayModal from '@layout/overlay/Overlay'; import Navigation from '@layout/navigation/Navigation'; import Button from '@ui/button/Button'; +import { useGetChatList } from '@pages/chat-list/api/chat-list-api'; +import Loading from '@layout/loading/Loading'; export default function ChatList() { const navigate = useNavigate(); const handleClickBack = () => navigate(-1); + //TODO: 유저,오너 구분 확인 로직 필요 + const isOwner = true; + const { data: chatListData, isPending, isError } = useGetChatList(isOwner); + const { isEditing, activeFilter, setActiveFilter, - chatList, selectChatList, handleToggleEdit, handleCheckChange, @@ -25,6 +30,13 @@ export default function ChatList() { handleCloseModal, } = useChatList(); + //TODO: 에러 처리 필요 + if (isPending) { + return ; + } + if (isError) { + return
채팅 목록을 불러오는 중에 오류가 발생했습니다.
; + } return ( <> -
- {(chatList ?? []).map(item => { +
+ {(chatListData?.content ?? []).map(item => { return ( handleCheckChange(item.clientId)} + name={item.name ?? ''} + foodTruckName={item.foodTruckName ?? ''} + lastMessage={item.lastMessage ?? ''} + lastMessageSendTime={item.lastMessageSendTime ?? ''} + unreadCount={item.unreadCount ?? 0} + isChecked={selectChatList.has(item.id ?? 0)} + handleCheckChange={() => handleCheckChange(item.id ?? 0)} /> ); })} diff --git a/src/pages/chat-list/api/chat-list-api.ts b/src/pages/chat-list/api/chat-list-api.ts new file mode 100644 index 00000000..c64e2998 --- /dev/null +++ b/src/pages/chat-list/api/chat-list-api.ts @@ -0,0 +1,66 @@ +import { apiRequest } from '@api/apiRequest'; +import { USER_INFO } from '@shared/querykey/user-info'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import type { + CreateChatRoomData, + GetChatRoomsData, + MarkMessagesAsReadData, +} from 'apis/data-contracts'; + +const getChatList = async (isOwner: boolean) => { + const response = await apiRequest({ + endPoint: '/chat/rooms', + method: 'GET', + params: { + isOwner, + }, + }); + return response.data; +}; + +const postChatRoom = async (foodTruckId: number) => { + const response = await apiRequest({ + endPoint: '/chat/rooms', + method: 'POST', + params: { + foodTruckId, + }, + }); + return response.data; +}; + +const patchChatRoom = async (chatRoomId: number) => { + const response = await apiRequest({ + endPoint: `/chat/rooms/${chatRoomId}/read`, + method: 'PATCH', + }); + return response.data; +}; + +export const useGetChatList = (isOwner: boolean) => { + return useQuery({ + // eslint-disable-next-line @tanstack/query/exhaustive-deps + queryKey: USER_INFO.CHATS(), + queryFn: () => getChatList(isOwner), + }); +}; + +export const usePostChatRoom = (foodTruckId: number) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => postChatRoom(foodTruckId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: USER_INFO.CHATS() }); + }, + }); +}; + +export const usePatchChatRoom = (chatRoomId: number) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: () => patchChatRoom(chatRoomId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: USER_INFO.CHATS() }); + }, + }); +}; diff --git a/src/pages/chat-list/constant/mocks.ts b/src/pages/chat-list/constant/mocks.ts deleted file mode 100644 index 5b7336d7..00000000 --- a/src/pages/chat-list/constant/mocks.ts +++ /dev/null @@ -1,77 +0,0 @@ -import type { Chat } from '@pages/chat-list/types/chat-list-type'; - -export const mockup: Chat[] = [ - { - clientId: 1, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 12, - }, - { - clientId: 2, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 122, - }, - { - clientId: 3, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, - { - clientId: 4, - clientName: '고객이름고객이름고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: - '혹시 예약 가능할까요? 아 안된다고요. 알겠습니다. 몇자 제한입니까', - lastChatTime: '오후 5:40', - unreadCount: 12, - }, - { - clientId: 5, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, - { - clientId: 6, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, - { - clientId: 7, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, - { - clientId: 8, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, - { - clientId: 9, - clientName: '고객이름', - tagTitle: '오소리 푸드트럭', - lastChat: '혹시 예약 가능할까요?', - lastChatTime: '오후 5:40', - unreadCount: 0, - }, -]; diff --git a/src/pages/chat-list/hooks/use-chat-list.tsx b/src/pages/chat-list/hooks/use-chat-list.tsx index 2be5ef30..7df9f806 100644 --- a/src/pages/chat-list/hooks/use-chat-list.tsx +++ b/src/pages/chat-list/hooks/use-chat-list.tsx @@ -1,11 +1,9 @@ -import { mockup } from '@pages/chat-list/constant/mocks'; -import type { Chat } from '@pages/chat-list/types/chat-list-type'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; export const useChatList = () => { const [isEditing, setIsEditing] = useState(false); const [activeFilter, setActiveFilter] = useState('전체보기'); - const [chatList, setChatList] = useState([]); + const [selectChatList, setSelectChatList] = useState(new Set()); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); @@ -36,16 +34,10 @@ export const useChatList = () => { const handleCloseModal = () => setIsDeleteModalOpen(false); - useEffect(() => { - /** API 준비 전 더미 데이터 사용*/ - setChatList(mockup); - }, []); - return { isEditing, activeFilter, setActiveFilter, - chatList, selectChatList, handleToggleEdit, handleCheckChange, diff --git a/src/pages/chat-list/types/chat-list-type.ts b/src/pages/chat-list/types/chat-list-type.ts deleted file mode 100644 index 7afd2db2..00000000 --- a/src/pages/chat-list/types/chat-list-type.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface Chat { - clientId: number; - clientName: string; - tagTitle: string; - lastChat: string; - lastChatTime: string; - unreadCount: number; -}; \ No newline at end of file diff --git a/src/shared/components/chat/chat-list-item/ChatListItem.stories.tsx b/src/shared/components/chat/chat-list-item/ChatListItem.stories.tsx index 18afb099..eb9e8fdc 100644 --- a/src/shared/components/chat/chat-list-item/ChatListItem.stories.tsx +++ b/src/shared/components/chat/chat-list-item/ChatListItem.stories.tsx @@ -10,7 +10,7 @@ const meta: Meta = { }, tags: ['autodocs'], argTypes: { - profileImage: { + profileImageUrl: { control: 'text', description: '프로필 이미지 URL', }, @@ -19,19 +19,19 @@ const meta: Meta = { description: '편집 모드 활성화 여부. true인 경우에 컴포넌트 전체에 클릭 이벤트 발생', }, - clientName: { + name: { control: 'text', description: '고객 이름', }, - tagTitle: { + foodTruckName: { control: 'text', description: '고객에게 붙은 태그', }, - lastChat: { + lastMessage: { control: 'text', description: '마지막으로 수신된 채팅 메시지', }, - lastChatTime: { + lastMessageSendTime: { control: 'text', description: '마지막 채팅 수신 시간', }, @@ -54,10 +54,10 @@ export default meta; type Story = StoryObj; const defaultArgs = { - profileImage: 'https://via.placeholder.com/52', - clientName: '고객이름', - tagTitle: '고고 푸드트럭', - lastChatTime: '오후 3:40', + profileImageUrl: 'https://via.placeholder.com/52', + name: '고객이름', + foodTruckName: '고고 푸드트럭', + lastMessageSendTime: '오후 3:40', isEditing: false, isChecked: false, }; @@ -65,7 +65,7 @@ const defaultArgs = { export const Default: Story = { args: { ...defaultArgs, - lastChat: '네, 확인했습니다. 내일 연락드리겠습니다.', + lastMessage: '네, 확인했습니다. 내일 연락드리겠습니다.', unreadCount: 3, }, }; @@ -73,7 +73,7 @@ export const Default: Story = { export const Read: Story = { args: { ...defaultArgs, - lastChat: '감사합니다! 좋은 하루 되세요.', + lastMessage: '감사합니다! 좋은 하루 되세요.', unreadCount: 0, }, }; @@ -81,8 +81,8 @@ export const Read: Story = { export const LongText: Story = { args: { ...defaultArgs, - clientName: '건국대학교 총학생회 축제준비위원회 부팀장 고객님', - lastChat: + name: '건국대학교 총학생회 축제준비위원회 부팀장 고객님', + lastMessage: '안녕하세요, 문의주신 내용에 대한 답변입니다. 저희 학교의 축제를 맞이하여 총 7대의 푸드트럭을 각 건물 앞에 요청드리려고 합니다.', unreadCount: 1, }, @@ -91,8 +91,8 @@ export const LongText: Story = { export const NoProfileImage: Story = { args: { ...defaultArgs, - profileImage: '', - lastChat: '프로필 이미지가 없는 사용자입니다.', + profileImageUrl: '', + lastMessage: '프로필 이미지가 없는 사용자입니다.', unreadCount: 0, }, }; diff --git a/src/shared/components/chat/chat-list-item/ChatListItem.tsx b/src/shared/components/chat/chat-list-item/ChatListItem.tsx index 43ee5fbe..e06e6ac9 100644 --- a/src/shared/components/chat/chat-list-item/ChatListItem.tsx +++ b/src/shared/components/chat/chat-list-item/ChatListItem.tsx @@ -3,24 +3,24 @@ import Tag from '@ui/tag/Tag'; import { cn } from '@utils/cn'; interface ChatListItemProps { - profileImage?: string; + profileImageUrl?: string; isEditing: boolean; - clientName: string; - tagTitle: string; - lastChat: string; - lastChatTime: string; + name: string; + foodTruckName: string; + lastMessage: string; + lastMessageSendTime: string; unreadCount: number; isChecked: boolean; handleCheckChange: (_checked: boolean) => void; } export default function ChatListItem({ - profileImage, + profileImageUrl, isEditing, - clientName, - tagTitle, - lastChat, - lastChatTime, + name, + foodTruckName, + lastMessage, + lastMessageSendTime, unreadCount, isChecked, handleCheckChange, @@ -51,10 +51,10 @@ export default function ChatListItem({ )}
- {profileImage ? ( + {profileImageUrl ? ( {`${clientName} ) : ( @@ -66,20 +66,20 @@ export default function ChatListItem({
- {clientName} + {name} - +
0 ? 'text-grayscale-900' : 'text-grayscale-500'}`} > - {lastChat} + {lastMessage}
- {lastChatTime} + {lastMessageSendTime} {unreadCount > 0 && (