diff --git a/src/assets/Location.svg b/src/assets/Location.svg
new file mode 100644
index 00000000..98546374
--- /dev/null
+++ b/src/assets/Location.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/rightArrow.svg b/src/assets/rightArrow.svg
new file mode 100644
index 00000000..66015e0c
--- /dev/null
+++ b/src/assets/rightArrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/components/Common/LetterListItem/Category.tsx b/src/components/Common/LetterListItem/Category.tsx
new file mode 100644
index 00000000..d7801dda
--- /dev/null
+++ b/src/components/Common/LetterListItem/Category.tsx
@@ -0,0 +1,24 @@
+type BoxType = 'SEND' | 'RECEIVE';
+type LetterType = 'LETTER' | 'REPLY_LETTER';
+
+interface CategoryProps {
+ boxType: BoxType;
+ letterType: LetterType;
+}
+
+const categoryStyle = 'text-sm';
+
+export const Category = ({ boxType, letterType }: CategoryProps) => {
+ const classification = `${boxType}-${letterType}`;
+ switch (classification) {
+ case 'SEND-LETTER':
+ return
보낸 편지
;
+ case 'SEND-REPLY_LETTER':
+ return 보낸 답장
;
+ case 'RECEIVE-LETTER':
+ return 받은 편지
;
+ case 'RECEIVE-REPLY_LETTER':
+ return 받은 답장
;
+ default:
+ }
+};
diff --git a/src/components/StoragePage/LetterDateGroup.tsx b/src/components/StoragePage/LetterDateGroup.tsx
index b5a99cde..944fbb7a 100644
--- a/src/components/StoragePage/LetterDateGroup.tsx
+++ b/src/components/StoragePage/LetterDateGroup.tsx
@@ -1,18 +1,9 @@
import { LetterListItem } from './LetterListItem';
-import { DeleteLetterType } from '@/types/letter';
-
-interface Letter {
- letterId: number;
- title: string;
- label: string;
- letterType: string;
- boxType: string;
- createdAt: string;
-}
+import { DeleteLetterType, StorageLetterDataType } from '@/types/letter';
type LetterDateGroupProps = {
date: string;
- letters: Letter[];
+ letters: StorageLetterDataType[];
checkedItems: DeleteLetterType[];
handleSingleCheck: (
checked: boolean,
diff --git a/src/components/StoragePage/LetterListItem.tsx b/src/components/StoragePage/LetterListItem.tsx
index df0ed9c4..662050eb 100644
--- a/src/components/StoragePage/LetterListItem.tsx
+++ b/src/components/StoragePage/LetterListItem.tsx
@@ -1,20 +1,22 @@
-import { Itembox } from '@/components/Common/Itembox/Itembox';
import { BottleLetter } from '@/components/Common/BottleLetter/BottleLetter';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
-import { DeleteLetterType, storageLetterType } from '@/types/letter';
-import { match } from 'ts-pattern';
+import {
+ DeleteLetterType,
+ StorageKeywordLetter,
+ StorageLetterDataType,
+ StorageMapArchivedLetter,
+ StorageMapReceivedLetter,
+ StorageMapSentLetter
+} from '@/types/letter';
+import { Category } from '../Common/LetterListItem/Category';
-interface Letter {
- letterId: number;
- title: string;
- label: string;
- letterType: string;
- boxType: string;
- createdAt: string;
-}
+export const ASSETS = {
+ LOCATION: '/src/assets/Location.svg',
+ RIGHT_ARROW: '/src/assets/rightArrow.svg'
+} as const;
type LetterListItemProps = {
- letter: Letter;
+ letter: StorageLetterDataType;
checked: boolean;
handleSingleCheck: (
checked: boolean,
@@ -28,39 +30,91 @@ export const LetterListItem = ({
handleSingleCheck
}: LetterListItemProps) => {
const navigate = useNavigate();
-
- const { letterType } = useParams();
+ const { selectedLetterType } = useParams();
const [searchParams] = useSearchParams();
const filterType = searchParams.get('filtertype');
- const renderCategory = (boxType: string, letterType: string) => {
- const condition = `${boxType}-${letterType}`;
- switch (condition) {
- case 'SEND-LETTER':
- return '보낸 편지';
- case 'SEND-REPLY_LETTER':
- return '보낸 답장';
- case 'RECEIVE-LETTER':
- return '받은 편지';
- case 'RECEIVE-REPLY_LETTER':
- return '받은 답장';
+ const getNavigatePath = (letter: StorageLetterDataType) => {
+ switch (selectedLetterType) {
+ case 'keyword':
+ return `/letter/keyword/${letter.letterType}/${filterType}/${letter.letterId}`;
+ case 'map':
+ return `/letter/map/${filterType}/${letter.letterId}`;
+ case 'bookmark':
+ return `/letter/map/${filterType}/bookmark/${letter.letterId}`;
default:
+ throw new Error(
+ `Unsupported letter type: ${selectedLetterType}`
+ );
}
};
- const getNavigatePath = (letter: Letter) => {
- return match(letterType as storageLetterType)
- .with(
- 'keyword',
- () =>
- `/letter/keyword/${letter.letterType}/${filterType}/${letter.letterId}`
- )
- .with('map', () => `/letter/map/${filterType}/${letter.letterId}`)
- .with(
- 'bookmark',
- () => `/letter/map/${filterType}/bookmark/${letter.letterId}`
- )
- .exhaustive();
+ const renderListContent = () => {
+ switch (selectedLetterType) {
+ case 'keyword': {
+ const keywordLetter = letter as StorageKeywordLetter;
+ return (
+ <>
+
+
+ {keywordLetter.title}
+
+ >
+ );
+ }
+ case 'map': {
+ const mapLetter = letter as
+ | StorageMapSentLetter
+ | StorageMapReceivedLetter;
+
+ const getUserNicknameLabel = (
+ letter: StorageMapSentLetter | StorageMapReceivedLetter
+ ) => {
+ if (letter.type === 'PUBLIC') return '';
+ // Target
+ const prefix =
+ 'targetUserNickname' in letter ? 'To.' : 'From.';
+ const nickname =
+ 'targetUserNickname' in letter
+ ? letter.targetUserNickname
+ : letter.senderNickname;
+
+ return nickname ? `${prefix} ${nickname}` : '';
+ };
+ return (
+ <>
+
+ {getUserNicknameLabel(mapLetter)}
+
+
+
+ {letter.title}
+
+
+
+
+
+ 서울특별시 동대문구 어디어디
+
+
+ >
+ );
+ }
+ case 'bookmark': {
+ const bookmarkLetter = letter as StorageMapArchivedLetter;
+ return (
+ <>
+
+ {bookmarkLetter.title}
+
+ 샘플 위치가 들어갑니다.
+ >
+ );
+ }
+ }
};
return (
@@ -81,15 +135,15 @@ export const LetterListItem = ({
className="flex flex-row gap-4 w-full h-[90px] items-center p-4 rounded-lg bg-sample-gray cursor-pointer"
onClick={() => navigate(getNavigatePath(letter))}
>
-
-
-
-
-
- {renderCategory(letter.boxType, letter.letterType)}
+
+
+
+
+
+ {renderListContent()}
-
{letter.title}
+
);
diff --git a/src/components/StoragePage/StorageList.tsx b/src/components/StoragePage/StorageList.tsx
index 435a0c75..0c7ee77a 100644
--- a/src/components/StoragePage/StorageList.tsx
+++ b/src/components/StoragePage/StorageList.tsx
@@ -15,7 +15,7 @@ const ROWS_PER_PAGE = 10;
export const StorageList = () => {
const queryClient = useQueryClient();
- const { letterType } = useParams();
+ const { selectedLetterType } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const filterType = searchParams.get('filtertype');
const { ref, inView } = useInView();
@@ -24,7 +24,7 @@ export const StorageList = () => {
const [checkedItems, setCheckedItems] = useState([]);
const getApiEndpoint = () => {
- return match(letterType as storageLetterType)
+ return match(selectedLetterType as storageLetterType)
.with('keyword', () => `/letters/saved/${filterType}`)
.with('map', () => `/map/${filterType}`)
.with('bookmark', () => '/map/archived')
diff --git a/src/hooks/useInfiniteStorageFetch.ts b/src/hooks/useInfiniteStorageFetch.ts
index 086d7025..5442adcd 100644
--- a/src/hooks/useInfiniteStorageFetch.ts
+++ b/src/hooks/useInfiniteStorageFetch.ts
@@ -1,15 +1,7 @@
import { useMemo } from 'react';
import { useInfiniteQuery } from '@tanstack/react-query';
import { getLetter } from '@/service/storage/getLetter';
-
-interface Letter {
- letterId: number;
- title: string;
- label: string;
- letterType: string;
- boxType: string;
- createdAt: string;
-}
+import { StorageLetterDataType } from '@/types/letter';
type useInfiniteStorageFetchParams = {
apiEndpoint: string;
@@ -65,7 +57,7 @@ export const useInfiniteStorageFetch = ({
}
const allLetters = data.pages.flatMap((page) => page.content);
const grouped = allLetters.reduce(
- (acc: { [key: string]: Letter[] }, letter) => {
+ (acc: { [key: string]: StorageLetterDataType[] }, letter) => {
const date = new Date(letter.createdAt)
.toISOString()
.split('T')[0];
diff --git a/src/pages/Storage/StoragePage.tsx b/src/pages/Storage/StoragePage.tsx
index 40cda7be..786fee1b 100644
--- a/src/pages/Storage/StoragePage.tsx
+++ b/src/pages/Storage/StoragePage.tsx
@@ -2,32 +2,44 @@ import React, { useEffect } from 'react';
import { StorageList } from '@/components/StoragePage/StorageList';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
-type LetterType = 'keyword' | 'map' | 'bookmark';
+export const StoragePage = () => {
+ const navigate = useNavigate();
+ const { selectedLetterType } = useParams();
+ const [searchParams, setSearchParams] = useSearchParams();
-const getTranslateX = (path: LetterType) => {
- const pathIndex =
+ const storageMenuList = [
+ {
+ type: 'keyword',
+ label: '키워드 편지'
+ },
{
- keyword: 0,
- map: 1,
- bookmark: 2
- }[path] || 0;
+ type: 'map',
+ label: '지도 편지'
+ },
+ {
+ type: 'bookmark',
+ label: '보관함'
+ }
+ ];
- return `${pathIndex * 100}%`;
-};
+ const getTranslateX = (path: string) => {
+ const pathIndex =
+ {
+ keyword: 0,
+ map: 1,
+ bookmark: 2
+ }[path] || 0;
-export const StoragePage = () => {
- const navigate = useNavigate();
- const { letterType } = useParams();
- const [searchParams, setSearchParams] = useSearchParams();
+ return `${pathIndex * 100}%`;
+ };
- const handleNavigate = (type: LetterType) => {
+ const handleNavigate = (type: string) => {
if (type === 'bookmark') return navigate(`/storage/${type}`);
navigate(`/storage/${type}?filtertype=sent`);
};
- // 마운트 될 때 초기 필터타입을 지정합니다
useEffect(() => {
- if (letterType === 'bookmark') return;
+ if (selectedLetterType === 'bookmark') return;
const currentFilter = searchParams.get('filtertype');
if (!currentFilter) {
setSearchParams({ filtertype: 'sent' });
@@ -41,30 +53,22 @@ export const StoragePage = () => {
- handleNavigate('keyword')}
- >
- 키워드 편지
-
- handleNavigate('map')}
- >
- 내 지도 편지
-
- handleNavigate('bookmark')}
- >
- 보관함
-
+ {storageMenuList.map((category) => {
+ return (
+ handleNavigate(category.type)}
+ >
+ {category.label}
+
+ );
+ })}
-
diff --git a/src/router.tsx b/src/router.tsx
index 79720abf..306367f1 100644
--- a/src/router.tsx
+++ b/src/router.tsx
@@ -32,7 +32,6 @@ const ProfileSharePage = lazy(
import { tokenStorage } from './service/auth/tokenStorage';
import { AuthProvider } from './AuthProvider';
import { Container } from '@/components/Common/Container/Container';
-
import { CreateMapLetterPage } from './pages/Map/Create/CreateMapLetterPage';
import { MapSelectItemPage } from './pages/Map/Select/MapSelectItemPage';
import { ErrorBoundary } from './ErrorBoundary';
@@ -113,7 +112,7 @@ export const router = createBrowserRouter([
element:
},
{
- path: 'storage/:letterType',
+ path: 'storage/:selectedLetterType',
element:
},
{
diff --git a/src/service/storage/getLetter.ts b/src/service/storage/getLetter.ts
index 0b10cfd9..db93124e 100644
--- a/src/service/storage/getLetter.ts
+++ b/src/service/storage/getLetter.ts
@@ -1,14 +1,6 @@
import { defaultApi } from '@/service/api';
import { ApiResponseType } from '@/types/apiResponse';
-
-type Letter = {
- letterId: number;
- title: string;
- label: string;
- letterType: string;
- boxType: string;
- createdAt: string;
-};
+import { StorageLetterDataType } from '@/types/letter';
type getLetterProps = {
apiEndpoint: string;
@@ -18,7 +10,7 @@ type getLetterProps = {
};
type getLetterResultType = {
- content: Letter[];
+ content: StorageLetterDataType[];
page: number;
size: number;
totalElements: number;
diff --git a/src/types/letter.ts b/src/types/letter.ts
index 05b99ebd..3712576e 100644
--- a/src/types/letter.ts
+++ b/src/types/letter.ts
@@ -8,6 +8,8 @@ export type LetterType = {
};
export type storageLetterType = 'keyword' | 'map' | 'bookmark';
+export type ApiBoxType = 'SEND' | 'RECEIVE';
+export type ApiLetterType = 'LETTER' | 'REPLY_LETTER';
export type DeleteLetterType = {
letterId: number;
@@ -15,33 +17,49 @@ export type DeleteLetterType = {
boxType: string;
};
-export type StorageKeywordLetterType = {
+export interface BaseLetter {
letterId: number;
title: string;
- description: string;
- latitude: number;
- longitude: number;
label: string;
+ letterType: ApiLetterType;
+ boxType: ApiBoxType;
createdAt: string;
+}
+
+export interface StorageKeywordLetter extends BaseLetter {
+ keywords?: string[]; // TODO : 키워드 관련 특수 필드 요청
+}
+
+export interface StorageMapSentLetter extends BaseLetter {
+ description: string;
+ targetUserNickname: string;
type: string;
sourceLetterId: number;
- senderNickname: string;
- senderProfileImg: string;
-};
+}
-export type StorageMapLetterType = {
- letterId: number;
- title: string;
+export interface StorageMapReceivedLetter extends BaseLetter {
description: string;
latitude: number;
longitude: number;
- label: string;
- createdAt: string;
type: string;
sourceLetterId: number;
senderNickname: string;
senderProfileImg: string;
-};
+}
+
+export interface StorageMapArchivedLetter extends BaseLetter {
+ archiveId: number;
+ description: string;
+ latitude: number;
+ longitude: number;
+ letterCreatedAt: string;
+}
+
+export type StorageLetterDataType =
+ | StorageKeywordLetter
+ | StorageMapSentLetter
+ | StorageMapReceivedLetter
+ | StorageMapArchivedLetter;
export type MapReplyType = {
sourceLetter: number;