Skip to content

Commit

Permalink
feat : 스토리지 누락된 삭제 기능 추가 (#399)
Browse files Browse the repository at this point in the history
* style : 라벨첩 상단바 디자인 수정

* fix : 바뀌지 않던 url 수정

* feat : 누락된 삭제 기능 추가
  • Loading branch information
cmlim0070 authored Dec 9, 2024
1 parent 16b0a5f commit fcaa8a1
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 45 deletions.
Binary file added public/라벨_샘플_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/라벨_샘플_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/라벨_샘플_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/라벨_샘플_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/라벨_샘플_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/라벨_샘플_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 10 additions & 2 deletions src/components/Common/BackButtonContainer/BackButtonCotainer.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { BackButton } from '@/components/Common/BackButton/BackButton';
import { useNavigate } from 'react-router-dom';

export const BackButtonCotainer = () => {
type BackButtonCotainerProps = {
width?: string;
};

export const BackButtonCotainer = ({
width = '100%'
}: BackButtonCotainerProps) => {
const navigate = useNavigate();

const handleBackClick = () => {
navigate(-1);
};

return (
<div className="w-full flex flex-row justify-start sticky top-0 z-10 pb-[15px]">
<div
className={`w-[${width}] flex flex-row justify-start sticky top-0 z-10 pb-[15px]`}
>
<BackButton onClick={handleBackClick} />
</div>
);
Expand Down
4 changes: 1 addition & 3 deletions src/components/Common/Container/Container.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ interface ContainerProps {

export const Container: React.FC<ContainerProps> = ({ px = 0, children }) => {
return (
<div
className={`relative flex flex-col px-${px} pt-3 py-6 w-full h-full`}
>
<div className={`relative flex flex-col px-${px} py-6 w-full h-full`}>
{children}
</div>
);
Expand Down
7 changes: 0 additions & 7 deletions src/components/MapPage/Maplibre/MaplibreWithSearch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import { useNearbyLetters } from '@/hooks/useNearbyLetters';
import { NearbyLettersResponseType } from '@/types/letter';
import { useLocationState } from '@/hooks/useLocationState';
import { BottleLetter } from '@/components/Common/BottleLetter/BottleLetter';
import { useNavigate } from 'react-router-dom';
import { TopBar } from '@/components/Common/TopBar/TopBar';

type MaplibreWithSearchProps = {
onFocus: () => void;
Expand All @@ -36,11 +34,6 @@ export const MaplibreWithSearch = ({
setNearbyLettersLength
);
const { nearbyLetters } = useNearbyLetters(currentLocation);
const navigate = useNavigate();

const onBackClick = () => {
navigate(-1);
};

return (
<div className="relative h-screen">
Expand Down
106 changes: 89 additions & 17 deletions src/components/StoragePage/StorageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { Itembox } from '../Common/Itembox/Itembox';
import { useNavigate } from 'react-router-dom';
import { useInfiniteStorageFetch } from '@/hooks/useInfiniteStorageFetch';
import { useInView } from 'react-intersection-observer';
import { deleteLetters } from '@/service/letter/delete/deleteLetters';
import { useModal, useToastStore } from '@/hooks';
import { useQueryClient } from '@tanstack/react-query';

type storageType = 'keyword' | 'map' | 'bookmark';
type FilterType = 'SEND' | 'RECEIVE';
Expand All @@ -12,13 +15,23 @@ type StorageListProps = {
type: storageType;
};

type DeleteLetterType = {
letterId: number;
letterType: string;
boxType: string;
};

const ROWS_PER_PAGE = 5;

export const StorageList = ({ type }: StorageListProps) => {
const navigate = useNavigate();
const queryClient = useQueryClient();

const { openModal, closeModal, ModalComponent } = useModal();
const { addToast } = useToastStore();

const [selectedFilter, setSelectedFilter] = useState<FilterType>('SEND');
const [checkedItems, setCheckedItems] = useState<number[]>([]);
const [checkedItems, setCheckedItems] = useState<DeleteLetterType[]>([]);

const { ref, inView } = useInView();

Expand Down Expand Up @@ -62,34 +75,64 @@ export const StorageList = ({ type }: StorageListProps) => {
isLoading,
isError,
fetchNextPage,
// isFetching,
isFetchingNextPage
} = useInfiniteStorageFetch(getApiEndpoint(), ROWS_PER_PAGE);

// 체크박스 단일 클릭
const handleSingleCheck = (checked: boolean, id: number) => {
const handleSingleCheck = (
checked: boolean,
{ letterId, letterType, boxType }: DeleteLetterType
) => {
if (checked) {
setCheckedItems((prev) => [...prev, id]);
setCheckedItems((prev) => [
...prev,
{ letterId, letterType, boxType }
]);
} else {
setCheckedItems(checkedItems.filter((el) => el !== id));
setCheckedItems(
checkedItems.filter((item) => item.letterId !== letterId)
);
}
console.log(checkedItems);
};

// 체크박스 전체 클릭
const handleAllCheck = (checked: boolean) => {
if (checked) {
const idArray: number[] = [];
const itemArray: {
letterId: number;
letterType: string;
boxType: string;
}[] = [];
groupedLetters.forEach((item) => {
item.letters.forEach((letter) => {
idArray.push(letter.letterId);
itemArray.push({
letterId: letter.letterId,
letterType: letter.letterType,
boxType: letter.boxType
});
});
});
setCheckedItems(idArray);
setCheckedItems(itemArray);
} else {
setCheckedItems([]);
}
};

const handleDelete = async () => {
const response = await deleteLetters(checkedItems);
if (response.isSuccess) {
addToast('삭제가 완료되었습니다.', 'success');
setCheckedItems([]);
queryClient.invalidateQueries({
queryKey: ['storageLetters', getApiEndpoint()]
});
return;
}
addToast('삭제에 실패했습니다.', 'warning');
return;
};

useEffect(() => {
if (inView) {
fetchNextPage();
Expand All @@ -101,12 +144,33 @@ export const StorageList = ({ type }: StorageListProps) => {
}

if (isError) {
return <></>;
return <>에러!</>;
}

const renderList = () => {
return (
<div className="flex flex-col gap-2">
<ModalComponent height="h-[100px]">
<div className="flex flex-col gqp-3">
<div className="text-lg text-bold">
정말 삭제하시겠습니까?
</div>
<div className="flex flex-row gap-1">
<button
onClick={handleDelete}
className="bg-sample-blue text-white"
>
</button>
<button
onClick={closeModal}
className="bg-white boder border-sample-blue"
>
아니오
</button>
</div>
</div>
</ModalComponent>
<div className="flex flex-row gap-2">
<button
className={`border border-sample-blue rounded-xl text-sm px-2 py-1
Expand Down Expand Up @@ -148,7 +212,12 @@ export const StorageList = ({ type }: StorageListProps) => {
/>
<label>전체</label>
</div>
<button className="px-2 py-1 bg-sample-gray">삭제</button>
<button
className="px-2 py-1 bg-sample-gray"
onClick={openModal}
>
삭제
</button>
</div>
{groupedLetters.map((dayGroup) => (
<div key={dayGroup.date} className="flex flex-col gap-3">
Expand All @@ -169,16 +238,19 @@ export const StorageList = ({ type }: StorageListProps) => {
onChange={(e) =>
handleSingleCheck(
e.target.checked,
letter.letterId
{
letterId: letter.letterId,
letterType:
letter.letterType,
boxType: letter.boxType
}
)
}
checked={
checkedItems.includes(
checked={checkedItems.some(
(item) =>
item.letterId ===
letter.letterId
)
? true
: false
}
)}
/>
<div
className="flex flex-row gap-4 w-full h-[90px] items-center p-4 rounded-lg bg-sample-gray cursor-pointer"
Expand Down
1 change: 1 addition & 0 deletions src/hooks/useModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function useModal() {

return {
openModal,
closeModal,
ModalComponent
};
}
48 changes: 35 additions & 13 deletions src/pages/Storage/LabelCollectionsPage.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import React, { useState } from 'react';
import { getUserLabel } from './../../service/label/get/getUserLabel';
import { useQuery } from '@tanstack/react-query';
// import { getUserLabel } from './../../service/label/get/getUserLabel';
// import { useQuery } from '@tanstack/react-query';
import { LabelType } from '@/types/label';
import { Itembox } from '@/components/Common/Itembox/Itembox';
import { Label } from '@/components/Common/BottleLetter/Label/Label';
import { Link } from 'react-router-dom';
import { useModal } from '@/hooks/useModal';
import { Modal } from '@/components/Common/Modal/Modal';
import { Margin } from '@/components/Common/Margin/Margin';
import { BackButtonCotainer } from '@/components/Common/BackButtonContainer/BackButtonCotainer';

const testLable: LabelType[] = [
{ labelId: 1, imageUrl: '/라벨_샘플_01.png' },
{ labelId: 2, imageUrl: '/라벨_샘플_02.png' },
{ labelId: 3, imageUrl: '/라벨_샘플_01.png' },
{ labelId: 4, imageUrl: '/라벨_샘플_02.png' },
{ labelId: 5, imageUrl: '/라벨_샘플_01.png' }
{ labelId: 1, imageUrl: '/라벨_샘플_1.png' },
{ labelId: 2, imageUrl: '/라벨_샘플_2.png' },
{ labelId: 3, imageUrl: '/라벨_샘플_3.png' },
{ labelId: 4, imageUrl: '/라벨_샘플_4.png' },
{ labelId: 5, imageUrl: '/라벨_샘플_5.png' },
{ labelId: 6, imageUrl: '/라벨_샘플_6.png' },
{ labelId: 7, imageUrl: '/라벨_샘플_1.png' },
{ labelId: 8, imageUrl: '/라벨_샘플_2.png' },
{ labelId: 9, imageUrl: '/라벨_샘플_3.png' },
{ labelId: 10, imageUrl: '/라벨_샘플_4.png' },
{ labelId: 11, imageUrl: '/라벨_샘플_5.png' },
{ labelId: 12, imageUrl: '/라벨_샘플_6.png' },
{ labelId: 13, imageUrl: '/라벨_샘플_1.png' },
{ labelId: 14, imageUrl: '/라벨_샘플_2.png' },
{ labelId: 15, imageUrl: '/라벨_샘플_3.png' },
{ labelId: 16, imageUrl: '/라벨_샘플_4.png' },
{ labelId: 17, imageUrl: '/라벨_샘플_5.png' },
{ labelId: 18, imageUrl: '/라벨_샘플_6.png' },
{ labelId: 19, imageUrl: '/라벨_샘플_1.png' },
{ labelId: 20, imageUrl: '/라벨_샘플_2.png' },
{ labelId: 21, imageUrl: '/라벨_샘플_3.png' },
{ labelId: 22, imageUrl: '/라벨_샘플_4.png' },
{ labelId: 23, imageUrl: '/라벨_샘플_5.png' },
{ labelId: 24, imageUrl: '/라벨_샘플_6.png' }
];

export const LabelCollectionsPage = () => {
Expand Down Expand Up @@ -68,22 +88,24 @@ export const LabelCollectionsPage = () => {

return (
<div className="h-full flex flex-col justify-between">
<div className="flex flex-row sticky top-0 z-10 pt-2 bg-white w-full">
<BackButtonCotainer width="30px" />
<h3 className="text-lg font-bold">라벨 모음</h3>
</div>
<ModalComponent height="h-[400px]">
{selectedLabel && (
<div className="h-full">
<Label imgSrc={selectedLabel.imageUrl} />
</div>
)}
</ModalComponent>
<div className="flex flex-col gap-5">
<h3 className="text-lg font-bold">라벨 모음</h3>
{renderList()}
</div>
<div className="flex flex-col gap-5">{renderList()}</div>
<Link to="/lottery">
<button className="btn-base bg-sample-blue text-white h-[50px] flex items-center justify-center">
<button className="fixed bottom-[6rem] w-[calc(100%-3rem)] max-w-[425px] btn-base bg-sample-blue text-white h-[50px] flex items-center justify-center rounded-lg shadow-lg">
라벨 더 뽑으러 가기
</button>
</Link>
<Margin bottom={60} />
</div>
);
};
17 changes: 14 additions & 3 deletions src/pages/Storage/StoragePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import { StorageList } from '@/components/StoragePage/StorageList';
import { useNavigate } from 'react-router-dom';

type storageType = 'keyword' | 'map' | 'bookmark';

Expand All @@ -20,6 +21,7 @@ const getTranslateX = (path: storageType) => {

export const StoragePage = ({ initialType }: StoragePageProps) => {
const [storageType, setStorageType] = useState<storageType>(initialType);
const navigate = useNavigate();

return (
<div className="flex flex-col h-full">
Expand All @@ -33,19 +35,28 @@ export const StoragePage = ({ initialType }: StoragePageProps) => {
></div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => setStorageType('keyword')}
onClick={() => {
setStorageType('keyword');
navigate('/storage/keyword');
}}
>
<span>키워드 편지</span>
</div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => setStorageType('map')}
onClick={() => {
setStorageType('map');
navigate('/storage/map');
}}
>
<span>내 지도 편지</span>
</div>
<div
className="flex items-center justify-center flex-1 h-full cursor-pointer"
onClick={() => setStorageType('bookmark')}
onClick={() => {
setStorageType('bookmark');
navigate('/storage/bookmark');
}}
>
<span>보관한 지도 편지</span>
</div>
Expand Down
21 changes: 21 additions & 0 deletions src/service/letter/delete/deleteLetters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { LetterType, CreateLetterResponseType } from '@/types/letter';
import { defaultApi } from '@/service/api';
import { ApiResponseType } from '@/types/apiResponse';

type DeleteLetterType = {
letterId: number;
letterType: string;
boxType: string;
};

type deleteLettersResponse = ApiResponseType<string>;

export const deleteLetters = async (
selectedList: DeleteLetterType[]
): Promise<deleteLettersResponse> => {
const api = defaultApi();
const response = await api.delete('/letters/saved', {
data: selectedList
});
return response.data;
};

0 comments on commit fcaa8a1

Please sign in to comment.