Skip to content

Commit 3b83467

Browse files
authored
feat : QA반영 (#134)
* feat : 편지 작성 임시저장 편지 전용 편지 전송 api 적용 * feat : 편지 금칙어 api 연결 밑작업 * feat : 관리자 신고 관리 페이지 신고 처리 상태가 PENDING이 아닌 값들 신고 처리 버튼 제거 * design : 랜덤편지 리스트 새로고침 레이아웃 조절 * feat : 토스트UI 포지션 위치 수정 + write 페이지에 토스트UI 적용 * feat : 공유 알림, 편지 도착 알림 모달UI 작업
1 parent f748ef3 commit 3b83467

File tree

19 files changed

+306
-55
lines changed

19 files changed

+306
-55
lines changed

src/apis/admin.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ const patchReport = async (reportId: number, patchReportRequest: PatchReportRequ
4141
};
4242

4343
// badwords
44-
const getBadWords = async (setBadWords: React.Dispatch<React.SetStateAction<BadWords[]>>) => {
44+
const getBadWords = async () => {
4545
try {
4646
const res = await client.get('/api/bad-words');
47-
setBadWords(res.data.data);
47+
if (!res) throw new Error('금칙어 조회 도중 에러가 발생했습니다.');
4848
console.log(res);
49+
return res;
4950
} catch (error) {
5051
console.error(error);
5152
}
@@ -63,14 +64,34 @@ const postBadWords = async (badWordsRequest: BadWords) => {
6364
};
6465

6566
// 내 상상대로 만든 필터링 단어 취소 버튼
66-
const patchBadWords = async (badWordId: number) => {
67+
const patchBadWordsUsed = async (badWordId: string) => {
6768
try {
6869
const res = await client.patch(`/api/bad-words/${badWordId}/status`, { isUsed: false });
6970
if (!res) throw new Error('검열 단어 삭제 도중 에러가 발생했습니다.');
7071
console.log(res);
72+
return res;
73+
} catch (error) {
74+
console.error(error);
75+
}
76+
};
77+
78+
const patchBadWords = async (badWordId: string, word: string) => {
79+
try {
80+
const res = await client.patch(`/api/bad-words/${badWordId}`, { word: word });
81+
if (!res) throw new Error('검열 단어 삭제 도중 에러가 발생했습니다.');
82+
console.log(res);
83+
return res;
7184
} catch (error) {
7285
console.error(error);
7386
}
7487
};
7588

76-
export { postReports, getReports, patchReport, getBadWords, postBadWords, patchBadWords };
89+
export {
90+
postReports,
91+
getReports,
92+
patchReport,
93+
getBadWords,
94+
postBadWords,
95+
patchBadWordsUsed,
96+
patchBadWords,
97+
};

src/apis/write.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ const postLetter = async (data: LetterRequest) => {
77
if (!res) throw new Error('편지 전송과정에서 오류가 발생했습니다.');
88
return res;
99
} catch (error) {
10+
const errorWithStatus = error as unknown as { status: number };
1011
console.error(error);
12+
return errorWithStatus;
1113
}
1214
};
1315

@@ -42,4 +44,17 @@ const postTemporarySave = async (data: TemporaryRequest) => {
4244
}
4345
};
4446

45-
export { postLetter, postFirstReply, getPrevLetter, postTemporarySave };
47+
const postTemporaryLetter = async (data: TemporaryRequest) => {
48+
console.log('Temporary request', data);
49+
try {
50+
const res = await client.post('/api/letters', data);
51+
if (!res) throw new Error('편지 전송과정에서 오류가 발생했습니다.');
52+
return res;
53+
} catch (error) {
54+
const errorWithStatus = error as unknown as { status: number };
55+
console.error(error);
56+
return errorWithStatus;
57+
}
58+
};
59+
60+
export { postLetter, postFirstReply, getPrevLetter, postTemporarySave, postTemporaryLetter };

src/assets/icons/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import SnowIcon from './snow.svg?react';
2525
import StampIcon from './stamp.svg?react';
2626
import ThermostatIcon from './thermostat.svg?react';
2727
import WarmIcon from './warm.svg?react';
28+
import ToggleOff from './toggle-off.svg?react';
29+
import ToggleOn from './toggle-on.svg?react';
2830

2931
export {
3032
AddIcon,
@@ -54,4 +56,6 @@ export {
5456
DeleteIcon,
5557
CancelIcon,
5658
PencilIcon,
59+
ToggleOff,
60+
ToggleOn,
5761
};

src/assets/icons/toggle-off.svg

Lines changed: 3 additions & 0 deletions
Loading

src/assets/icons/toggle-on.svg

Lines changed: 3 additions & 0 deletions
Loading

src/components/ToastItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function ToastItem({ toastObj, index }: { toastObj: ToastObj; ind
2121

2222
const TOAST_POSITION = {
2323
Top: 'top-20',
24-
Bottom: 'bottom-5',
24+
Bottom: 'bottom-20',
2525
};
2626

2727
const animation = `toast-blink ${toastObj.time}s ease-in-out forwards`;

src/pages/Admin/Filtering.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
11
import { useEffect, useState } from 'react';
22

3-
import { getBadWords } from '@/apis/admin';
3+
import { getBadWords, patchBadWordsUsed } from '@/apis/admin';
44
import { AddIcon, AlarmIcon, CancelIcon } from '@/assets/icons';
55

66
import AddInputButton from './components/AddInputButton';
77
import AdminPageTitle from './components/AdminPageTitle';
88
import WrapperFrame from './components/WrapperFrame';
99
import WrapperTitle from './components/WrapperTitle';
10+
import FilterTextItem from './components/FilterTextItem';
1011

1112
export default function FilteringManage() {
12-
const [badWords, setBadWords] = useState<BadWords[]>([]);
13+
const [badWords, setBadWords] = useState<BadWordsData[]>([]);
1314
const [addInputShow, setAddInputShow] = useState<boolean>(false);
1415

16+
const handleGetBadWords = async () => {
17+
const res = await getBadWords();
18+
if (res?.status === 200) {
19+
setBadWords(res.data.data);
20+
} else {
21+
console.log('검열 조회 오류 발생');
22+
}
23+
};
24+
1525
useEffect(() => {
16-
getBadWords(setBadWords);
26+
handleGetBadWords();
1727
}, []);
1828
return (
1929
<>
2030
<AdminPageTitle>검열 관리 / 필터링 단어 설정</AdminPageTitle>
2131
<WrapperFrame>
2232
<WrapperTitle title="필터링 단어" Icon={AlarmIcon} />
2333
<div className="mt-5 flex w-full flex-wrap gap-4">
24-
{badWords.map((badWord, idx) => {
25-
return (
26-
<span
27-
key={idx}
28-
className="flex items-center gap-1.5 rounded-2xl bg-[#C1C1C1] px-4 py-1.5"
29-
>
30-
{badWord.word}
31-
<button>
32-
<CancelIcon className="h-4 w-4" />
33-
</button>
34-
</span>
35-
);
34+
{badWords.map((badWord) => {
35+
return <FilterTextItem key={badWord.id} badWord={badWord} setBadWords={setBadWords} />;
3636
})}
3737
{addInputShow ? (
3838
<AddInputButton setAddInputShow={setAddInputShow} setBadWords={setBadWords} />
3939
) : (
40-
<span className="flex items-center gap-1.5 rounded-2xl bg-[#C1C1C1] px-4 py-1.5">
40+
<span className="bg-primary-3 flex items-center gap-1.5 rounded-2xl px-4 py-1.5">
4141
추가하기
4242
<button
4343
onClick={() => {

src/pages/Admin/Report.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default function ReportManage() {
2828
reportType: null,
2929
status: 'PENDING',
3030
page: '1',
31-
size: '1',
31+
size: '8',
3232
});
3333

3434
const handleGetReports = async (reportQueryString: ReportQueryString) => {

src/pages/Admin/components/AddInputButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default function AddInputButton({
88
setBadWords,
99
}: {
1010
setAddInputShow: React.Dispatch<React.SetStateAction<boolean>>;
11-
setBadWords: React.Dispatch<React.SetStateAction<BadWords[]>>;
11+
setBadWords: React.Dispatch<React.SetStateAction<BadWordsData[]>>;
1212
}) {
1313
const [inputText, setInputText] = useState<BadWords>({ word: '' });
1414
const inputRef = useRef<HTMLInputElement>(null);
@@ -23,7 +23,7 @@ export default function AddInputButton({
2323
if (inputText.word === '') return setAddInputShow(false);
2424
const res = await postBadWords(inputText);
2525
if (res?.status === 200) {
26-
setBadWords((cur) => [...cur, inputText]);
26+
setBadWords((cur) => [...cur, res.data.data]);
2727
setAddInputShow(false);
2828
}
2929
};
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { patchBadWordsUsed } from '@/apis/admin';
2+
import { DeleteIcon, PencilIcon, ToggleOff, ToggleOn } from '@/assets/icons';
3+
import { useState } from 'react';
4+
import PatchInput from './PatchInput';
5+
6+
export default function FilterTextItem({
7+
badWord,
8+
setBadWords,
9+
}: {
10+
badWord: BadWordsData;
11+
setBadWords: React.Dispatch<React.SetStateAction<BadWordsData[]>>;
12+
}) {
13+
const [patchInputShow, setPatchInputShow] = useState<boolean>(false);
14+
15+
return (
16+
<span className="bg-primary-3 flex items-center gap-1.5 rounded-2xl px-4 py-1.5">
17+
{patchInputShow ? (
18+
<PatchInput
19+
badWordId={badWord.id}
20+
setPatchInputShow={setPatchInputShow}
21+
setBadWords={setBadWords}
22+
/>
23+
) : (
24+
badWord.word
25+
)}
26+
27+
<button onClick={() => setPatchInputShow(true)}>
28+
<PencilIcon className="h-4 w-4" />
29+
</button>
30+
31+
<button onClick={() => {}}>
32+
<DeleteIcon className="h-5 w-5" />
33+
</button>
34+
35+
<button onClick={() => patchBadWordsUsed(badWord.id)}>
36+
{true ? <ToggleOn className="h-5 w-5" /> : <ToggleOff className="h-5 w-5" />}
37+
</button>
38+
</span>
39+
);
40+
}

0 commit comments

Comments
 (0)