From 3062c19d5d99f5d6277239b1b19c2267692a89f9 Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 17:09:58 +0900 Subject: [PATCH 01/70] =?UTF-8?q?rename(apply):=20useAnnounce=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/components/home/HomeAnnounce.tsx | 4 ++-- service-apply/src/hooks/react-query/useAnnounce.tsx | 6 +++--- service-apply/src/pages/Anouncement/Announcement.page.tsx | 4 ++-- .../src/pages/Anouncement/AnnouncementList.page.tsx | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/service-apply/src/components/home/HomeAnnounce.tsx b/service-apply/src/components/home/HomeAnnounce.tsx index 344f27f..e93250d 100644 --- a/service-apply/src/components/home/HomeAnnounce.tsx +++ b/service-apply/src/components/home/HomeAnnounce.tsx @@ -1,10 +1,10 @@ import { Txt } from '@quokka/design-system'; import { Link } from 'react-router-dom'; -import { useAnounceQuery } from '../../hooks/react-query/useAnnounce'; +import { useAnnounceQuery } from '../../hooks/react-query/useAnnounce'; import clsx from 'clsx'; export const HomeAnnounce = () => { - const { announceData } = useAnounceQuery(); + const { announceData } = useAnnounceQuery(); return (
diff --git a/service-apply/src/hooks/react-query/useAnnounce.tsx b/service-apply/src/hooks/react-query/useAnnounce.tsx index 180b06b..b5812d6 100644 --- a/service-apply/src/hooks/react-query/useAnnounce.tsx +++ b/service-apply/src/hooks/react-query/useAnnounce.tsx @@ -5,7 +5,7 @@ import { getAnnounceById, } from '../../apis/announce.apis'; -export const useAnounceQuery = () => { +export const useAnnounceQuery = () => { const { data: announceData } = useSuspenseQuery({ queryKey: ['anounce'], queryFn: getAnnounceLast, @@ -13,7 +13,7 @@ export const useAnounceQuery = () => { return { announceData }; }; -export const useAnounceListQuery = (page: number) => { +export const useAnnounceListQuery = (page: number) => { const { data: announceListData } = useSuspenseQuery({ queryKey: ['anounceList', page], queryFn: () => getAllAnnounce(page), @@ -21,7 +21,7 @@ export const useAnounceListQuery = (page: number) => { return { announceListData }; }; -export const useAnounceDetailQuery = (announceId: number) => { +export const useAnnounceDetailQuery = (announceId: number) => { const { data: announceDetailData } = useSuspenseQuery({ queryKey: ['anounceDetail', announceId], queryFn: () => getAnnounceById(announceId), diff --git a/service-apply/src/pages/Anouncement/Announcement.page.tsx b/service-apply/src/pages/Anouncement/Announcement.page.tsx index 90bf98c..08a216c 100644 --- a/service-apply/src/pages/Anouncement/Announcement.page.tsx +++ b/service-apply/src/pages/Anouncement/Announcement.page.tsx @@ -1,6 +1,6 @@ import { Container, Txt } from '@quokka/design-system'; import { useNavigate, useParams } from 'react-router-dom'; -import { useAnounceDetailQuery } from '../../hooks/react-query/useAnnounce'; +import { useAnnounceDetailQuery } from '../../hooks/react-query/useAnnounce'; import { Icon } from '../../components/announcement/Icon'; export const AnnouncementPage = () => { @@ -9,7 +9,7 @@ export const AnnouncementPage = () => { if (isNaN(+announcementId)) throw new Error('announcementId must be number'); const navigate = useNavigate(); - const { announceDetailData } = useAnounceDetailQuery(+announcementId); + const { announceDetailData } = useAnnounceDetailQuery(+announcementId); const handleGoBack = () => { navigate(-1); }; diff --git a/service-apply/src/pages/Anouncement/AnnouncementList.page.tsx b/service-apply/src/pages/Anouncement/AnnouncementList.page.tsx index cff004a..558a3ea 100644 --- a/service-apply/src/pages/Anouncement/AnnouncementList.page.tsx +++ b/service-apply/src/pages/Anouncement/AnnouncementList.page.tsx @@ -2,7 +2,7 @@ import { AnnouncementList } from '../../components/announcement/AnnouncementList import { useLocation } from 'react-router-dom'; import { useMemo } from 'react'; import { PageNav } from '../../components/announcement/PageNav'; -import { useAnounceListQuery } from '../../hooks/react-query/useAnnounce'; +import { useAnnounceListQuery } from '../../hooks/react-query/useAnnounce'; const useQueryParameter = () => { const { search } = useLocation(); @@ -14,7 +14,7 @@ export const AnnouncementListPage = () => { const currentPage = query.get('pages') ?? '1'; const { announceListData: { announces, lastPage }, - } = useAnounceListQuery(+currentPage); + } = useAnnounceListQuery(+currentPage); return ( <> From 1331b6e9abda81962bb1e7dfb029cefb99f8b6f8 Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 17:11:22 +0900 Subject: [PATCH 02/70] =?UTF-8?q?rename(apply):=20HomeInformation=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/components/home/HomeInformation.tsx | 4 ++-- service-apply/src/pages/Home.page.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/service-apply/src/components/home/HomeInformation.tsx b/service-apply/src/components/home/HomeInformation.tsx index c52d1b7..db01efa 100644 --- a/service-apply/src/components/home/HomeInformation.tsx +++ b/service-apply/src/components/home/HomeInformation.tsx @@ -1,7 +1,7 @@ import { Txt } from '@quokka/design-system'; import { useInformationQuery } from '../../hooks/react-query/useInformation'; -export const HomeInforamtion = () => { +export const HomeInformation = () => { const { information } = useInformationQuery(); return ( @@ -20,4 +20,4 @@ export const HomeInforamtion = () => { ); }; -export default HomeInforamtion; +export default HomeInformation; diff --git a/service-apply/src/pages/Home.page.tsx b/service-apply/src/pages/Home.page.tsx index d959c11..824fb4e 100644 --- a/service-apply/src/pages/Home.page.tsx +++ b/service-apply/src/pages/Home.page.tsx @@ -1,7 +1,7 @@ import { Suspense, useEffect } from 'react'; import { Footer } from '../components/common/Footer'; import { MainContainer } from '../components/common/MainContainer'; -import { HomeInforamtion } from '../components/home/HomeInformation'; +import { HomeInformation } from '../components/home/HomeInformation'; import { HomeLogin } from '../components/home/HomeLogin'; import { HomeAnnounce } from '../components/home/HomeAnnounce'; import { HomeTitle } from '../components/home/HomeTitle'; @@ -21,7 +21,7 @@ export const HomePage = () => {
- + From 76a34cba99f0e9748a1330465bf37e6251defc8a Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:15:31 +0900 Subject: [PATCH 03/70] =?UTF-8?q?fix(apply):=20https.ts=EC=9D=98=20fetcher?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20return=20=EA=B0=92=EC=97=90=EC=84=9C=20?= =?UTF-8?q?Response=20dto=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/functions/https.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/service-apply/src/functions/https.ts b/service-apply/src/functions/https.ts index b1a651b..0eb8c71 100644 --- a/service-apply/src/functions/https.ts +++ b/service-apply/src/functions/https.ts @@ -21,7 +21,8 @@ const fetcher = async (url: string, req: RequestInit) => { if (response.status >= 400) { return await errorStatusResult(response); } - return await response.json().then((data) => new Response(data)); + + return await response.json(); }; const errorStatusResult = async (response: globalThis.Response) => { From 3d539eb393ee63810a132f1404e55d128f7b954c Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:16:32 +0900 Subject: [PATCH 04/70] =?UTF-8?q?fix(apply):=20getNotice=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20Notice=20=EB=A7=A4=EA=B0=9C?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/notice.apis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-apply/src/apis/notice.apis.ts b/service-apply/src/apis/notice.apis.ts index fc26f24..d7e0bf0 100644 --- a/service-apply/src/apis/notice.apis.ts +++ b/service-apply/src/apis/notice.apis.ts @@ -7,5 +7,5 @@ export const getNotice = async () => { if (isErrorResponse(response)) { return new Notice({ noticeContent: '' }); } - return new Notice(response.data); + return new Notice(response); }; From 4fbc605a01c14911ce0f56e74c9bd18c18067f2b Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:19:28 +0900 Subject: [PATCH 05/70] =?UTF-8?q?fix(apply):=20getAnnounceLast=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20LastAnnounce=20=EB=A7=A4=EA=B0=9C?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/announce.apis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-apply/src/apis/announce.apis.ts b/service-apply/src/apis/announce.apis.ts index ff6ef5a..d726061 100644 --- a/service-apply/src/apis/announce.apis.ts +++ b/service-apply/src/apis/announce.apis.ts @@ -18,7 +18,7 @@ export const getAnnounceLast = async () => { announceTitle: '게시글이 없습니다', }); } - return new LastAnnounce(response.data); + return new LastAnnounce(response); }; export const getAnnounceById = async (announceId: number) => { From 94bd4db73c4eb6d4fe99ce49c6471484388b723e Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:20:05 +0900 Subject: [PATCH 06/70] =?UTF-8?q?fix(apply):=20postLoin=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20UserToken=20=EB=A7=A4=EA=B0=9C?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/user.apis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-apply/src/apis/user.apis.ts b/service-apply/src/apis/user.apis.ts index e95b1c7..0c0ed4e 100644 --- a/service-apply/src/apis/user.apis.ts +++ b/service-apply/src/apis/user.apis.ts @@ -13,7 +13,7 @@ export const postLogin = async (data: UserLoginRequest) => { if (isErrorResponse(response)) { throw new Error(response.reason); } - return new UserToken(response.data); + return new UserToken(response); }; export interface PasswordFindRequest { From 9fbf98b07f349074648ccfb543cc9dc79018de88 Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:22:11 +0900 Subject: [PATCH 07/70] =?UTF-8?q?fix(apply):=20getRegistration=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20RegistrationOptionResponse=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=B0=8F=20api=20=EC=8A=A4=ED=8E=99=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=9D=BC=20RegistrationOptionResponse=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EB=B0=8F=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/dtos/registration.dtos.ts | 6 +++--- service-apply/src/apis/registration.apis.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/service-apply/src/apis/dtos/registration.dtos.ts b/service-apply/src/apis/dtos/registration.dtos.ts index a0b59dc..345e82b 100644 --- a/service-apply/src/apis/dtos/registration.dtos.ts +++ b/service-apply/src/apis/dtos/registration.dtos.ts @@ -50,7 +50,7 @@ interface RegistrationResponseProps { carNum: string; isLight: boolean; phoneNum: string; - sector: { + sectors: { sectorId: number; sectorNum: string; sectorName: string; @@ -79,7 +79,7 @@ export class RegistrationOptionsResponse { isLight, name, phoneNum, - sector, + sectors, studentNum, selectSectorId, affiliation, @@ -89,7 +89,7 @@ export class RegistrationOptionsResponse { this.isCompact = isLight; this.studentName = name; this.phoneNumber = phoneNum; - this.sector = sector; + this.sector = sectors; this.studentNumber = studentNum; this.selectSectorId = selectSectorId; this.affiliation = affiliation; diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index b18f914..c93aff0 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -36,12 +36,12 @@ export const getRegistration = email: '', isLight: false, phoneNum: '', - sector: [], + sectors: [], studentNum: '', name: '', selectSectorId: -1, affiliation: '', }); } - return new RegistrationOptionsResponse(response.data); + return new RegistrationOptionsResponse(response); }; From 1b1c4a1cfc6515cdc8b3cec438e347c0f79d315a Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:41:50 +0900 Subject: [PATCH 08/70] =?UTF-8?q?fix(apply):=20postRegistration=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20Response=20dto=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EC=95=88=20=ED=95=A8=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20RegistrationResponse=20=EB=A7=A4?= =?UTF-8?q?=EA=B0=9C=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/registration.apis.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index c93aff0..1b62ed6 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -12,16 +12,18 @@ export const postRegistration = async ( ): Promise => { const { isRegistration, ...rest } = registration; const response = await https.post( - `/v1/registration/${registration.isRegistration}`, + `/v1/registration${registration.isRegistration || '/temporary'}`, rest, ); if (isErrorResponse(response)) { - if (response.status === 401 || response.status === 403) { - return reissueToken(() => postRegistration(registration)); - } - throw new Error(response.reason); + // TODO: response 값에 status와 reason이 없기 때문에 토큰 재발행 로직 불가능 + // if (response.status === 401 || response.status === 403) { + // return reissueToken(() => postRegistration(registration)); + // } + // throw new Error(response.reason); + throw new Error('주차권 임시저장에 실패했습니다'); } - return new RegistrationResponse(response.data); + return new RegistrationResponse(response); }; export const getRegistration = From 24d68ac74ee000c07a855cddc17f6b8ee7d68244 Mon Sep 17 00:00:00 2001 From: j8won Date: Sat, 9 Dec 2023 19:46:46 +0900 Subject: [PATCH 09/70] =?UTF-8?q?feat(apply):=20postRegistration=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20=EC=A3=BC=EC=B0=A8?= =?UTF-8?q?=EA=B6=8C=20=EC=8B=A0=EC=B2=AD=20=ED=98=B9=EC=9D=80=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=EC=A0=80=EC=9E=A5=20=EC=97=AC=EB=B6=80=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=97=90=EB=9F=AC=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/registration.apis.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index 1b62ed6..9c7bff5 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -12,7 +12,7 @@ export const postRegistration = async ( ): Promise => { const { isRegistration, ...rest } = registration; const response = await https.post( - `/v1/registration${registration.isRegistration || '/temporary'}`, + `/v1/registration${registration.isRegistration ? '' : '/temporary'}`, rest, ); if (isErrorResponse(response)) { @@ -21,6 +21,8 @@ export const postRegistration = async ( // return reissueToken(() => postRegistration(registration)); // } // throw new Error(response.reason); + if (registration.isRegistration) + throw new Error('주차권 신청에 실패했습니다'); throw new Error('주차권 임시저장에 실패했습니다'); } return new RegistrationResponse(response); From 1a16387954c0c26ef933714e3271a5b5e312b144 Mon Sep 17 00:00:00 2001 From: j8won Date: Sun, 10 Dec 2023 00:39:50 +0900 Subject: [PATCH 10/70] =?UTF-8?q?fix(apply):=20useAnnounceListQuery=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20getAllAnnounce=20?= =?UTF-8?q?=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EA=B0=92=201=20?= =?UTF-8?q?=EA=B0=90=EC=86=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(=EC=B4=88=EA=B8=B0=EA=B0=92=EC=9D=B4=200)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/hooks/react-query/useAnnounce.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-apply/src/hooks/react-query/useAnnounce.tsx b/service-apply/src/hooks/react-query/useAnnounce.tsx index b5812d6..55d5d62 100644 --- a/service-apply/src/hooks/react-query/useAnnounce.tsx +++ b/service-apply/src/hooks/react-query/useAnnounce.tsx @@ -16,7 +16,7 @@ export const useAnnounceQuery = () => { export const useAnnounceListQuery = (page: number) => { const { data: announceListData } = useSuspenseQuery({ queryKey: ['anounceList', page], - queryFn: () => getAllAnnounce(page), + queryFn: () => getAllAnnounce(page - 1), }); return { announceListData }; }; From 302b99f42dcade365b4299c0b284f01461a61c59 Mon Sep 17 00:00:00 2001 From: j8won Date: Sun, 10 Dec 2023 00:42:08 +0900 Subject: [PATCH 11/70] =?UTF-8?q?fix(apply):=20postPasswordFind=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20PasswordF?= =?UTF-8?q?ind=20=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/user.apis.ts | 6 ++++-- .../src/hooks/password-reset/useRequestPasswordForm.tsx | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/service-apply/src/apis/user.apis.ts b/service-apply/src/apis/user.apis.ts index 0c0ed4e..e564714 100644 --- a/service-apply/src/apis/user.apis.ts +++ b/service-apply/src/apis/user.apis.ts @@ -23,9 +23,11 @@ export interface PasswordFindRequest { export const postPasswordFind = async ({ email }: PasswordFindRequest) => { const response = await https.post(`/v1/user/password/find`, { email }); if (isErrorResponse(response)) { - throw new Error(response.reason); + throw new Error('이메일 전송에 실패했습니다'); + // TODO: error response에 따른 에러 발생 + // throw new Error(response.reason); } - return new PasswordFind(response.data); + return new PasswordFind(response); }; export interface PasswordResetRequest { diff --git a/service-apply/src/hooks/password-reset/useRequestPasswordForm.tsx b/service-apply/src/hooks/password-reset/useRequestPasswordForm.tsx index eabcef9..c97acd1 100644 --- a/service-apply/src/hooks/password-reset/useRequestPasswordForm.tsx +++ b/service-apply/src/hooks/password-reset/useRequestPasswordForm.tsx @@ -29,7 +29,7 @@ export const useRequestPasswordForm = () => { { email }, { onSuccess: () => { - alert('이메일을 확인해주세요.'); + alert('본인인증 이메일이 전송되었습니다.'); navigate('/'); }, }, From 187a6bc7d4936418e846a5b7e61cd855b7bee1eb Mon Sep 17 00:00:00 2001 From: j8won Date: Sun, 10 Dec 2023 00:43:48 +0900 Subject: [PATCH 12/70] =?UTF-8?q?fix(apply):=20getAllAnnounce,=20getAnnoun?= =?UTF-8?q?ceById=20=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=A7=A4?= =?UTF-8?q?=EA=B0=9C=EB=B3=80=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/announce.apis.ts | 8 +++++--- service-apply/src/components/home/HomeLogin.tsx | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/service-apply/src/apis/announce.apis.ts b/service-apply/src/apis/announce.apis.ts index d726061..face862 100644 --- a/service-apply/src/apis/announce.apis.ts +++ b/service-apply/src/apis/announce.apis.ts @@ -7,7 +7,7 @@ export const getAllAnnounce = async (page: number) => { if (isErrorResponse(response)) { return new AllAnnounce({ announces: [], lastPage: 0, nextPage: 0 }); } - return new AllAnnounce(response.data); + return new AllAnnounce(response); }; export const getAnnounceLast = async () => { @@ -24,7 +24,9 @@ export const getAnnounceLast = async () => { export const getAnnounceById = async (announceId: number) => { const response = await https.get(`/v1/announce/${announceId}`); if (isErrorResponse(response)) { - throw new Error(response.reason); + throw new Error('게시글을 조회할 수 없습니다'); + // TODO: error response에 따른 에러 발생 + // throw new Error(response.reason); } - return new Announce(response.data); + return new Announce(response); }; diff --git a/service-apply/src/components/home/HomeLogin.tsx b/service-apply/src/components/home/HomeLogin.tsx index 36a442d..c7e7ad6 100644 --- a/service-apply/src/components/home/HomeLogin.tsx +++ b/service-apply/src/components/home/HomeLogin.tsx @@ -33,7 +33,6 @@ export const HomeLogin = () => { { email, pwd: password }, { onError: (error) => { - console.error(error); alert(error.message); setIsError(true); setErrorMessage(error.message); From 509c0cd9636fb45fecd8089f8b9896914debfc68 Mon Sep 17 00:00:00 2001 From: j8won Date: Sun, 10 Dec 2023 00:50:46 +0900 Subject: [PATCH 13/70] =?UTF-8?q?fix(apply):=20api=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/announce.apis.ts | 2 +- service-apply/src/apis/registration.apis.ts | 9 +++++---- service-apply/src/apis/user.apis.ts | 16 +++++++++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/service-apply/src/apis/announce.apis.ts b/service-apply/src/apis/announce.apis.ts index face862..b432d55 100644 --- a/service-apply/src/apis/announce.apis.ts +++ b/service-apply/src/apis/announce.apis.ts @@ -25,7 +25,7 @@ export const getAnnounceById = async (announceId: number) => { const response = await https.get(`/v1/announce/${announceId}`); if (isErrorResponse(response)) { throw new Error('게시글을 조회할 수 없습니다'); - // TODO: error response에 따른 에러 발생 + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 // throw new Error(response.reason); } return new Announce(response); diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index 9c7bff5..37d0eb7 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -16,7 +16,7 @@ export const postRegistration = async ( rest, ); if (isErrorResponse(response)) { - // TODO: response 값에 status와 reason이 없기 때문에 토큰 재발행 로직 불가능 + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 // if (response.status === 401 || response.status === 403) { // return reissueToken(() => postRegistration(registration)); // } @@ -32,9 +32,10 @@ export const getRegistration = async (): Promise => { const response = await https.get('/v1/registration'); if (isErrorResponse(response)) { - if (response.status === 401 || response.status === 403) { - return reissueToken(getRegistration); - } + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 + // if (response.status === 401 || response.status === 403) { + // return reissueToken(getRegistration); + // } return new RegistrationOptionsResponse({ carNum: '', email: '', diff --git a/service-apply/src/apis/user.apis.ts b/service-apply/src/apis/user.apis.ts index e564714..6916dbd 100644 --- a/service-apply/src/apis/user.apis.ts +++ b/service-apply/src/apis/user.apis.ts @@ -11,7 +11,9 @@ export interface UserLoginRequest { export const postLogin = async (data: UserLoginRequest) => { const response = await https.post(`/v1/auth/login`, data); if (isErrorResponse(response)) { - throw new Error(response.reason); + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 + // throw new Error(response.reason); + throw new Error('로그인을 실패했습니다'); } return new UserToken(response); }; @@ -24,7 +26,7 @@ export const postPasswordFind = async ({ email }: PasswordFindRequest) => { const response = await https.post(`/v1/user/password/find`, { email }); if (isErrorResponse(response)) { throw new Error('이메일 전송에 실패했습니다'); - // TODO: error response에 따른 에러 발생 + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 // throw new Error(response.reason); } return new PasswordFind(response); @@ -43,9 +45,11 @@ export const postPasswordReset = async ({ password, }); if (isErrorResponse(response)) { - throw new Error(response.reason); + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 + // throw new Error(response.reason); + throw new Error('비밀번호 초기화를 실패했습니다'); } - return new PasswordReset(response.data); + return new PasswordReset(response); }; export const reissueToken = async (retryCallback: () => T): Promise => { @@ -57,7 +61,9 @@ export const reissueToken = async (retryCallback: () => T): Promise => { const response = await https.post(`/v1/auth/login`, { refreshtoken: token }); if (isErrorResponse(response)) { removeToken(); - throw new Error(response.reason); + // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 + // throw new Error(response.reason); + throw new Error('토큰 재발급에 실패했습니다. 다시 로그인 해주세요.'); } setToken(new UserToken(response.data)); From 28e9e557898a7270b1d7e60c05707de98d0aac55 Mon Sep 17 00:00:00 2001 From: j8won Date: Sun, 10 Dec 2023 00:53:34 +0900 Subject: [PATCH 14/70] =?UTF-8?q?fix(apply):=20PasswordResetForm=20input?= =?UTF-8?q?=20label=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/password-reset/PasswordResetForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-apply/src/components/password-reset/PasswordResetForm.tsx b/service-apply/src/components/password-reset/PasswordResetForm.tsx index da07591..6aa900f 100644 --- a/service-apply/src/components/password-reset/PasswordResetForm.tsx +++ b/service-apply/src/components/password-reset/PasswordResetForm.tsx @@ -28,7 +28,7 @@ export const PasswordResetForm = ({ code }: { code: string }) => { Date: Sun, 10 Dec 2023 16:33:15 +0900 Subject: [PATCH 15/70] =?UTF-8?q?feat(manager):=20admin=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20Route=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/common/NavContainer.tsx | 2 +- service-manager/src/pages/Admin.page.tsx | 13 +++++++++++++ service-manager/src/router/index.tsx | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 service-manager/src/pages/Admin.page.tsx diff --git a/service-manager/src/components/common/NavContainer.tsx b/service-manager/src/components/common/NavContainer.tsx index 9846b7b..a7ce8e6 100644 --- a/service-manager/src/components/common/NavContainer.tsx +++ b/service-manager/src/components/common/NavContainer.tsx @@ -15,7 +15,7 @@ export const NavContainer = () => { 안내 사항 - + 관리자 설정 diff --git a/service-manager/src/pages/Admin.page.tsx b/service-manager/src/pages/Admin.page.tsx new file mode 100644 index 0000000..ef13247 --- /dev/null +++ b/service-manager/src/pages/Admin.page.tsx @@ -0,0 +1,13 @@ +import { NavContainer } from '../components/common/NavContainer'; +import { NavTitle } from '../components/common/NavTitle'; + +export const AdminPage = () => { + return ( + <> + + + ); +}; diff --git a/service-manager/src/router/index.tsx b/service-manager/src/router/index.tsx index 10bc404..b87395a 100644 --- a/service-manager/src/router/index.tsx +++ b/service-manager/src/router/index.tsx @@ -1,19 +1,18 @@ import { Routes, Route } from 'react-router-dom'; import { MainPage } from '../pages/Main.page'; import { SignUpPage } from '../pages/SignUp.page'; -import { MainUserPage } from '../pages/Main.user'; import { AnnouncementPage } from '../pages/announcement/Announcement.page'; import { AnnouncementCreatePage } from '../pages/announcement/AnnouncementCreate.page'; import { ApplyListPage } from '../pages/ApplyList.page'; import { PasswordResetPage } from '../pages/PasswordReset.page'; import { NoticeView } from '../pages/notice/NoticeView.page'; import { NoticeCreate } from '../pages/notice/NoticeCreate.page'; +import { AdminPage } from '../pages/Admin.page'; export default function Router() { return ( } /> - } /> } /> } /> } /> @@ -22,6 +21,7 @@ export default function Router() { } /> } /> } /> + } /> ); } From 0a84b6af25897d3831902a748c810327952d609c Mon Sep 17 00:00:00 2001 From: 2yunseong Date: Sun, 10 Dec 2023 21:29:41 +0900 Subject: [PATCH 16/70] =?UTF-8?q?feat(manager):=20Admin=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A7=88=ED=81=AC=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/admin/MemberItem.tsx | 28 +++++++++++++ .../src/components/admin/MemberList.tsx | 22 ++++++++++ .../src/components/admin/MemberRole.tsx | 41 +++++++++++++++++++ service-manager/src/pages/Admin.page.tsx | 22 ++++++++++ service-manager/src/types/admin/index.ts | 1 + 5 files changed, 114 insertions(+) create mode 100644 service-manager/src/components/admin/MemberItem.tsx create mode 100644 service-manager/src/components/admin/MemberList.tsx create mode 100644 service-manager/src/components/admin/MemberRole.tsx create mode 100644 service-manager/src/types/admin/index.ts diff --git a/service-manager/src/components/admin/MemberItem.tsx b/service-manager/src/components/admin/MemberItem.tsx new file mode 100644 index 0000000..da9236b --- /dev/null +++ b/service-manager/src/components/admin/MemberItem.tsx @@ -0,0 +1,28 @@ +import type { Role } from '../../types/admin'; +import { MemberRole } from './MemberRole'; + +// FIXME: [/v1/admin/councils] Response Body 에서 파생된 타입임. 현재 타입은 스웨거 참고함. +interface MemberItemProps { + userId: number; + name: string; + studentNum: string; + phoneNum: string; + role: Role; +} + +export const MemberItem = ({ + userId, + name, + studentNum, + phoneNum, + role, +}: MemberItemProps) => { + return ( + + {name} + {studentNum} + {phoneNum} + + + ); +}; diff --git a/service-manager/src/components/admin/MemberList.tsx b/service-manager/src/components/admin/MemberList.tsx new file mode 100644 index 0000000..1d844a3 --- /dev/null +++ b/service-manager/src/components/admin/MemberList.tsx @@ -0,0 +1,22 @@ +import { MemberItem } from './MemberItem'; + +export const MemberList = () => { + return ( + <> + + + + ); +}; diff --git a/service-manager/src/components/admin/MemberRole.tsx b/service-manager/src/components/admin/MemberRole.tsx new file mode 100644 index 0000000..69f59ff --- /dev/null +++ b/service-manager/src/components/admin/MemberRole.tsx @@ -0,0 +1,41 @@ +import clsx from 'clsx'; +import type { Role } from '../../types/admin'; + +interface MemberRole { + userId: number; + role: Role; +} + +const styleBySelectedRole = (selected: Role, role: Role) => { + return selected === role + ? 'bg-[#CEDCFF] text-[#2160FF]' + : 'bg-[#D0D0D0]/[0.38] text-[#B5B5B5]'; +}; + +export const MemberRole = ({ role, userId }: MemberRole) => { + return ( + + + + + + ); +}; diff --git a/service-manager/src/pages/Admin.page.tsx b/service-manager/src/pages/Admin.page.tsx index ef13247..a58b135 100644 --- a/service-manager/src/pages/Admin.page.tsx +++ b/service-manager/src/pages/Admin.page.tsx @@ -1,5 +1,7 @@ +import { Txt } from '@quokka/design-system'; import { NavContainer } from '../components/common/NavContainer'; import { NavTitle } from '../components/common/NavTitle'; +import { MemberList } from '../components/admin/MemberList'; export const AdminPage = () => { return ( @@ -8,6 +10,26 @@ export const AdminPage = () => { +
+
+ + 회원가입 + +
+ + + + + + + + + + + + +
이름학번휴대전화역할
+
); }; diff --git a/service-manager/src/types/admin/index.ts b/service-manager/src/types/admin/index.ts new file mode 100644 index 0000000..251e1a3 --- /dev/null +++ b/service-manager/src/types/admin/index.ts @@ -0,0 +1 @@ +export type Role = 'ADMIN' | 'COUNCIL' | 'USER'; From 32f5556aeba57b8109f2d1f6f54e05c1c1b08cbf Mon Sep 17 00:00:00 2001 From: j8won Date: Mon, 11 Dec 2023 16:38:24 +0900 Subject: [PATCH 17/70] =?UTF-8?q?feat(apply):=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EB=B0=A9=EC=A7=80=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=B0=BD=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/apply/ApplyCaptchaModal.tsx | 70 +++++++++++++++++++ .../src/components/apply/ApplyForm.tsx | 16 ++++- 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 service-apply/src/components/apply/ApplyCaptchaModal.tsx diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx new file mode 100644 index 0000000..f27bb2b --- /dev/null +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -0,0 +1,70 @@ +import { Button, InputText, Modal, Txt } from '@quokka/design-system'; +import { PropsWithChildren, Suspense, useState } from 'react'; +import ErrorBoundary from '../common/ErrorBoundray'; +import Delete from '../../assets/delete.svg'; + +interface ApplyCaptchaModalProps extends PropsWithChildren { + isOpen: boolean; + onRequestClose: () => void; + onSave: () => void; +} + +const CaptchaForm = ({ onSubmit }: { onSubmit: () => void }) => { + return ( +
+ + 자동 신청 방지 + + +
+ +
+
+ +
+
+ ); +}; + +export const ApplyCaptchaModal = ({ + isOpen, + onRequestClose, + onSave, +}: ApplyCaptchaModalProps) => { + const [isLoading, setIsLoading] = useState(false); + + return ( + + {isLoading ?
loading
: } +
+ ); +}; diff --git a/service-apply/src/components/apply/ApplyForm.tsx b/service-apply/src/components/apply/ApplyForm.tsx index b623114..1ce10db 100644 --- a/service-apply/src/components/apply/ApplyForm.tsx +++ b/service-apply/src/components/apply/ApplyForm.tsx @@ -4,11 +4,14 @@ import { InputTextProps, Radio, Txt, + Modal, } from '@quokka/design-system'; import { useApplyForm } from '../../hooks/useApplyForm'; import { ApplySelector } from './ApplySelector'; import { clsx } from 'clsx'; import { useApplyQuery } from '../../hooks/react-query/useApply'; +import { useState } from 'react'; +import { ApplyCaptchaModal } from './ApplyCaptchaModal'; export const ApplyInputText = ({ className, ...props }: InputTextProps) => { return ( @@ -25,6 +28,7 @@ export const ApplyForm = () => { section: selectSectorId ?? 0, ...rest, }); + const [isCaptchaModalOpen, setIsCaptchaModalOpen] = useState(false); const parkingSection = sector.map((item) => ({ sectionNumber: item.sectorId, @@ -123,10 +127,20 @@ export const ApplyForm = () => { + { + setIsCaptchaModalOpen(false); + }} + onSave={() => onSave({ isRegistration: true })} + />
); From 3752d5bcd9af4c697ce81da76ad9cf753713747a Mon Sep 17 00:00:00 2001 From: j8won Date: Mon, 11 Dec 2023 16:39:15 +0900 Subject: [PATCH 18/70] =?UTF-8?q?feat(apply):=20api=20=EC=8A=A4=ED=8E=99?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=A3=BC?= =?UTF-8?q?=EC=B0=A8=EA=B6=8C=20=EC=8B=A0=EC=B2=AD=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?Dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/apis/dtos/registration.dtos.ts | 43 ++++++++++++++----- service-apply/src/hooks/useApplyForm.tsx | 30 ++++++++++--- 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/service-apply/src/apis/dtos/registration.dtos.ts b/service-apply/src/apis/dtos/registration.dtos.ts index 345e82b..15231b5 100644 --- a/service-apply/src/apis/dtos/registration.dtos.ts +++ b/service-apply/src/apis/dtos/registration.dtos.ts @@ -1,38 +1,59 @@ export interface RegistrationRequestProps { - email: string; + isRegistration: boolean; name: string; studentNumber: string; + affiliation: string; carNumber: string; isLightCar: boolean; phoneNumber: string; selectSectorId: number; - isRegistration: boolean; + captchaPendingCode?: string; + captchaAnswer?: string; + email: string; } -export class RegistrationRequest { +export interface RegistrationRequestDto { name: string; - studentNum: string; + studentNumber: string; + affiliation: string; carNum: string; isLight: boolean; phoneNum: string; selectSectorId: number; + captchaPendingCode?: string; + captchaAnswer?: string; +} + +export class RegistrationRequest { isRegistration: boolean; + requestDto: RegistrationRequestDto; + email: string; constructor({ + isRegistration, name, studentNumber, + affiliation, carNumber, isLightCar, phoneNumber, selectSectorId, - isRegistration, + captchaPendingCode, + captchaAnswer, + email, }: RegistrationRequestProps) { - this.name = name; - this.studentNum = studentNumber; - this.carNum = carNumber; - this.isLight = isLightCar; - this.phoneNum = phoneNumber; - this.selectSectorId = selectSectorId; this.isRegistration = isRegistration; + this.requestDto = { + name: name, + studentNumber: studentNumber, + affiliation: affiliation, + carNum: carNumber, + isLight: isLightCar, + phoneNum: phoneNumber, + selectSectorId: selectSectorId, + captchaPendingCode: captchaPendingCode, + captchaAnswer: captchaAnswer, + }; + this.email = email; } } diff --git a/service-apply/src/hooks/useApplyForm.tsx b/service-apply/src/hooks/useApplyForm.tsx index fb17e9f..8e5f906 100644 --- a/service-apply/src/hooks/useApplyForm.tsx +++ b/service-apply/src/hooks/useApplyForm.tsx @@ -7,6 +7,7 @@ import { import { useApplyMutate } from './react-query/useApply'; import { useNavigate } from 'react-router-dom'; import { removeToken } from '../functions/jwt'; +import { RegistrationRequest } from '../apis/dtos/registration.dtos'; type AppFormInputAction = | { @@ -62,23 +63,38 @@ const initValue = { isCompact: false, }; +interface registrationInfo { + isRegistration: boolean; + captchaPendingCode?: string; + captchaAnswer?: string; +} + export const useApplyForm = (init?: ApplyFormInput) => { init ||= initValue; const [state, dispatch] = useReducer(applyFormReducer, init); const { postRegistration } = useApplyMutate(); const navigate = useNavigate(); - const onSave = ({ isRegistration }: { isRegistration: boolean }) => { + const onSave = ({ + isRegistration, + captchaPendingCode, + captchaAnswer, + }: registrationInfo) => { + // TODO: affiliation, email 추가 postRegistration( - { + new RegistrationRequest({ isRegistration: isRegistration, - carNum: state.carNumber, name: state.studentName, - phoneNum: state.phoneNumber, + studentNumber: state.studentNumber, + affiliation: '공과대학', + isLightCar: state.isCompact, + carNumber: state.carNumber, + phoneNumber: state.phoneNumber, selectSectorId: +state.section, - studentNum: state.studentNumber, - isLight: state.isCompact, - }, + captchaPendingCode: captchaPendingCode, + captchaAnswer: captchaAnswer, + email: '', + }), { onError: (error) => { console.error(error); From a5c9eae1a6800aed3a05a134d1c6b26ab95806b7 Mon Sep 17 00:00:00 2001 From: loopy-lim Date: Mon, 11 Dec 2023 07:47:46 +0000 Subject: [PATCH 19/70] =?UTF-8?q?feat(apply):=20error=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=B4=20=EA=B5=AC=EB=B6=84=ED=95=98=EB=8A=94=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A5=BC=20=EB=A7=8C=EB=93=A0=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/functions/error.ts | 87 ++++++++++++++++++++++++++++ service-apply/src/functions/https.ts | 4 -- 2 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 service-apply/src/functions/error.ts diff --git a/service-apply/src/functions/error.ts b/service-apply/src/functions/error.ts new file mode 100644 index 0000000..80d202c --- /dev/null +++ b/service-apply/src/functions/error.ts @@ -0,0 +1,87 @@ +interface ModalErrorContent { + type: 'MODAL'; + content: string; + button: string; +} + +interface AlertErrorContent { + type: 'ALERT'; +} + +interface ReissueErrorContent { + type: 'REISSUE'; +} + +interface AlertWithRedirectErrorContent { + type: 'ALERT_WITH_REDIRECT'; + content: string; + redirect: string; +} + +interface NoneErrorContent { + type: 'NONE'; +} + +type ERROR_TYPE = + | ModalErrorContent + | AlertErrorContent + | ReissueErrorContent + | AlertWithRedirectErrorContent + | NoneErrorContent; + +type ERROR_CODE = + | 'GLOBAL_401_1' + | 'AUTH_403_6' + | 'AUTH_404_1' + | 'USER_404_1' + | 'USER_400_2' + | 'COUNCIL_400_2' + | 'COUNCIL_400_1' + | 'COUPON_400_6' + | 'COUPON_404_2' + | 'SECTOR_404_2' + | 'CAPTCHA_400_1' + | 'ANNOUNCE_404_2' + | 'DECRYPTION_500_1' + | 'ENCRYPTION_500_1' + | 'NOTICE_404_1'; + +export const getErrorContent = (error: ERROR_CODE): ERROR_TYPE => { + switch (error) { + case 'GLOBAL_401_1': + case 'AUTH_403_6': + case 'AUTH_404_1': + return { + type: 'REISSUE', + }; + case 'USER_404_1': + return { + type: 'ALERT_WITH_REDIRECT', + content: '존재하지 않는 유저 입니다.', + redirect: '/', + }; + case 'USER_400_2': + return { + type: 'ALERT_WITH_REDIRECT', + content: '정상적인 인증 링크가 아닙니다.', + redirect: '/', + }; + case 'NOTICE_404_1': + return { + type: 'NONE', + }; + case 'ENCRYPTION_500_1': + case 'DECRYPTION_500_1': + case 'COUNCIL_400_2': + case 'COUNCIL_400_1': + case 'COUPON_400_6': + case 'COUPON_404_2': + case 'SECTOR_404_2': + case 'ANNOUNCE_404_2': + case 'CAPTCHA_400_1': + default: + return { + type: 'ALERT', + }; + } +}; diff --git a/service-apply/src/functions/https.ts b/service-apply/src/functions/https.ts index 76b4200..48621ba 100644 --- a/service-apply/src/functions/https.ts +++ b/service-apply/src/functions/https.ts @@ -28,10 +28,6 @@ const errorStatusResult = async (response: globalThis.Response) => { const errorResponse = await response .json() .then((data) => new ErrorResponse(data)); - if (errorResponse.status === 401 || errorResponse.status === 403) { - alert('로그인이 필요합니다.'); - window.location.href = '/'; - } return errorResponse; }; From c2b4da7d12f0a2918561888a48042b15b8b89185 Mon Sep 17 00:00:00 2001 From: loopy-lim Date: Mon, 11 Dec 2023 08:15:50 +0000 Subject: [PATCH 20/70] =?UTF-8?q?feat(workspace):=20error=EB=A5=BC=20be?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EB=8C=80=EC=9D=91=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/functions/error.ts | 19 ++++- service-manager/src/functions/error.ts | 102 +++++++++++++++++++++++++ service-manager/src/functions/https.ts | 4 - 3 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 service-manager/src/functions/error.ts diff --git a/service-apply/src/functions/error.ts b/service-apply/src/functions/error.ts index 80d202c..21f332f 100644 --- a/service-apply/src/functions/error.ts +++ b/service-apply/src/functions/error.ts @@ -31,14 +31,19 @@ type ERROR_TYPE = type ERROR_CODE = | 'GLOBAL_401_1' + | 'AUTH_400_1' + | 'AUTH_401_1' + | 'AUTH_403_1' + | 'AUTH_403_2' + | 'AUTH_403_3' | 'AUTH_403_6' | 'AUTH_404_1' | 'USER_404_1' | 'USER_400_2' | 'COUNCIL_400_2' | 'COUNCIL_400_1' - | 'COUPON_400_6' | 'COUPON_404_2' + | 'COUPON_400_6' | 'SECTOR_404_2' | 'CAPTCHA_400_1' | 'ANNOUNCE_404_2' @@ -48,11 +53,20 @@ type ERROR_CODE = export const getErrorContent = (error: ERROR_CODE): ERROR_TYPE => { switch (error) { + case 'AUTH_401_1': + return { + type: 'REISSUE', + }; case 'GLOBAL_401_1': case 'AUTH_403_6': + case 'AUTH_403_1': + case 'AUTH_403_2': + case 'AUTH_403_3': case 'AUTH_404_1': return { - type: 'REISSUE', + type: 'ALERT_WITH_REDIRECT', + content: '로그인이 필요합니다.', + redirect: '/', }; case 'USER_404_1': return { @@ -79,6 +93,7 @@ export const getErrorContent = (error: ERROR_CODE): ERROR_TYPE => { case 'SECTOR_404_2': case 'ANNOUNCE_404_2': case 'CAPTCHA_400_1': + case 'AUTH_400_1': default: return { type: 'ALERT', diff --git a/service-manager/src/functions/error.ts b/service-manager/src/functions/error.ts new file mode 100644 index 0000000..21f332f --- /dev/null +++ b/service-manager/src/functions/error.ts @@ -0,0 +1,102 @@ +interface ModalErrorContent { + type: 'MODAL'; + content: string; + button: string; +} + +interface AlertErrorContent { + type: 'ALERT'; +} + +interface ReissueErrorContent { + type: 'REISSUE'; +} + +interface AlertWithRedirectErrorContent { + type: 'ALERT_WITH_REDIRECT'; + content: string; + redirect: string; +} + +interface NoneErrorContent { + type: 'NONE'; +} + +type ERROR_TYPE = + | ModalErrorContent + | AlertErrorContent + | ReissueErrorContent + | AlertWithRedirectErrorContent + | NoneErrorContent; + +type ERROR_CODE = + | 'GLOBAL_401_1' + | 'AUTH_400_1' + | 'AUTH_401_1' + | 'AUTH_403_1' + | 'AUTH_403_2' + | 'AUTH_403_3' + | 'AUTH_403_6' + | 'AUTH_404_1' + | 'USER_404_1' + | 'USER_400_2' + | 'COUNCIL_400_2' + | 'COUNCIL_400_1' + | 'COUPON_404_2' + | 'COUPON_400_6' + | 'SECTOR_404_2' + | 'CAPTCHA_400_1' + | 'ANNOUNCE_404_2' + | 'DECRYPTION_500_1' + | 'ENCRYPTION_500_1' + | 'NOTICE_404_1'; + +export const getErrorContent = (error: ERROR_CODE): ERROR_TYPE => { + switch (error) { + case 'AUTH_401_1': + return { + type: 'REISSUE', + }; + case 'GLOBAL_401_1': + case 'AUTH_403_6': + case 'AUTH_403_1': + case 'AUTH_403_2': + case 'AUTH_403_3': + case 'AUTH_404_1': + return { + type: 'ALERT_WITH_REDIRECT', + content: '로그인이 필요합니다.', + redirect: '/', + }; + case 'USER_404_1': + return { + type: 'ALERT_WITH_REDIRECT', + content: '존재하지 않는 유저 입니다.', + redirect: '/', + }; + case 'USER_400_2': + return { + type: 'ALERT_WITH_REDIRECT', + content: '정상적인 인증 링크가 아닙니다.', + redirect: '/', + }; + case 'NOTICE_404_1': + return { + type: 'NONE', + }; + case 'ENCRYPTION_500_1': + case 'DECRYPTION_500_1': + case 'COUNCIL_400_2': + case 'COUNCIL_400_1': + case 'COUPON_400_6': + case 'COUPON_404_2': + case 'SECTOR_404_2': + case 'ANNOUNCE_404_2': + case 'CAPTCHA_400_1': + case 'AUTH_400_1': + default: + return { + type: 'ALERT', + }; + } +}; diff --git a/service-manager/src/functions/https.ts b/service-manager/src/functions/https.ts index 76b4200..48621ba 100644 --- a/service-manager/src/functions/https.ts +++ b/service-manager/src/functions/https.ts @@ -28,10 +28,6 @@ const errorStatusResult = async (response: globalThis.Response) => { const errorResponse = await response .json() .then((data) => new ErrorResponse(data)); - if (errorResponse.status === 401 || errorResponse.status === 403) { - alert('로그인이 필요합니다.'); - window.location.href = '/'; - } return errorResponse; }; From 393fb27e5ff1c6c694029b76e6a730058cc33647 Mon Sep 17 00:00:00 2001 From: j8won Date: Mon, 11 Dec 2023 18:08:54 +0900 Subject: [PATCH 21/70] =?UTF-8?q?feat(apply):=20ApplyForm=EC=97=90=20?= =?UTF-8?q?=EC=86=8C=EC=86=8D=EB=8C=80=ED=95=99=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/apis/dtos/registration.dtos.ts | 32 ++++++++----------- .../src/components/apply/ApplyForm.tsx | 10 ++++++ service-apply/src/functions/validator.ts | 1 + service-apply/src/hooks/useApplyForm.tsx | 6 ++-- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/service-apply/src/apis/dtos/registration.dtos.ts b/service-apply/src/apis/dtos/registration.dtos.ts index 15231b5..8fa2870 100644 --- a/service-apply/src/apis/dtos/registration.dtos.ts +++ b/service-apply/src/apis/dtos/registration.dtos.ts @@ -12,9 +12,11 @@ export interface RegistrationRequestProps { email: string; } -export interface RegistrationRequestDto { +export class RegistrationRequest { + isRegistration: boolean; + // requestDto: RegistrationRequestDto; name: string; - studentNumber: string; + studentNum: string; affiliation: string; carNum: string; isLight: boolean; @@ -22,12 +24,7 @@ export interface RegistrationRequestDto { selectSectorId: number; captchaPendingCode?: string; captchaAnswer?: string; -} -export class RegistrationRequest { - isRegistration: boolean; - requestDto: RegistrationRequestDto; - email: string; constructor({ isRegistration, name, @@ -42,18 +39,15 @@ export class RegistrationRequest { email, }: RegistrationRequestProps) { this.isRegistration = isRegistration; - this.requestDto = { - name: name, - studentNumber: studentNumber, - affiliation: affiliation, - carNum: carNumber, - isLight: isLightCar, - phoneNum: phoneNumber, - selectSectorId: selectSectorId, - captchaPendingCode: captchaPendingCode, - captchaAnswer: captchaAnswer, - }; - this.email = email; + this.name = name; + this.studentNum = studentNumber; + this.affiliation = affiliation; + this.carNum = carNumber; + this.isLight = isLightCar; + this.phoneNum = phoneNumber; + this.selectSectorId = selectSectorId; + this.captchaPendingCode = captchaPendingCode; + this.captchaAnswer = captchaAnswer; } } diff --git a/service-apply/src/components/apply/ApplyForm.tsx b/service-apply/src/components/apply/ApplyForm.tsx index 1ce10db..1938136 100644 --- a/service-apply/src/components/apply/ApplyForm.tsx +++ b/service-apply/src/components/apply/ApplyForm.tsx @@ -77,6 +77,16 @@ export const ApplyForm = () => { value={state.studentNumber} required /> + + dispatch({ type: 'affiliation', payload: e.target.value }) + } + value={state.affiliation} + required + /> { isRegistration: isRegistration, name: state.studentName, studentNumber: state.studentNumber, - affiliation: '공과대학', + affiliation: state.affiliation, isLightCar: state.isCompact, carNumber: state.carNumber, phoneNumber: state.phoneNumber, selectSectorId: +state.section, captchaPendingCode: captchaPendingCode, captchaAnswer: captchaAnswer, - email: '', + email: state.email, }), { onError: (error) => { From b0305f807d558a9284438f4171bfedd83424e7e8 Mon Sep 17 00:00:00 2001 From: 2yunseong Date: Mon, 11 Dec 2023 20:23:20 +0900 Subject: [PATCH 22/70] =?UTF-8?q?fix(manager):=20API=20=EC=8A=A4=ED=8E=99?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20dto=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/apis/dtos/user.dtos.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/service-manager/src/apis/dtos/user.dtos.ts b/service-manager/src/apis/dtos/user.dtos.ts index 9ce7a78..a261b83 100644 --- a/service-manager/src/apis/dtos/user.dtos.ts +++ b/service-manager/src/apis/dtos/user.dtos.ts @@ -1,3 +1,5 @@ +import type { Role } from '../../types/admin'; + export class UserToken { accessToken: string; refreshToken: string; @@ -18,21 +20,25 @@ export class Council { name: string; studentNumber: string; phoneNumber: string; + role: Role; constructor({ userId, name, studentNum, phoneNum, + role, }: { userId: number; name: string; studentNum: string; phoneNum: string; + role: Role; }) { this.userId = userId; this.name = name; this.studentNumber = studentNum; this.phoneNumber = phoneNum; + this.role = role; } } From 2aa13fcd4cbc856c9fa97c5177b2493a907ec4ef Mon Sep 17 00:00:00 2001 From: 2yunseong Date: Mon, 11 Dec 2023 20:36:23 +0900 Subject: [PATCH 23/70] =?UTF-8?q?feat(manager):=20=ED=95=99=EC=83=9D?= =?UTF-8?q?=ED=9A=8C=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/apis/dtos/user.dtos.ts | 8 ++++---- service-manager/src/apis/user.apis.ts | 4 ++-- .../src/components/admin/MemberList.tsx | 20 ++++++------------- .../src/hooks/react-query/useCouncils.tsx | 11 ++++++++++ 4 files changed, 23 insertions(+), 20 deletions(-) create mode 100644 service-manager/src/hooks/react-query/useCouncils.tsx diff --git a/service-manager/src/apis/dtos/user.dtos.ts b/service-manager/src/apis/dtos/user.dtos.ts index a261b83..f45abec 100644 --- a/service-manager/src/apis/dtos/user.dtos.ts +++ b/service-manager/src/apis/dtos/user.dtos.ts @@ -18,8 +18,8 @@ export class UserToken { export class Council { userId: number; name: string; - studentNumber: string; - phoneNumber: string; + studentNum: string; + phoneNum: string; role: Role; constructor({ userId, @@ -36,8 +36,8 @@ export class Council { }) { this.userId = userId; this.name = name; - this.studentNumber = studentNum; - this.phoneNumber = phoneNum; + this.studentNum = studentNum; + this.phoneNum = phoneNum; this.role = role; } } diff --git a/service-manager/src/apis/user.apis.ts b/service-manager/src/apis/user.apis.ts index 65bc477..33b94f1 100644 --- a/service-manager/src/apis/user.apis.ts +++ b/service-manager/src/apis/user.apis.ts @@ -93,12 +93,12 @@ export const putAdminRole = async (userId: number, role: string) => { return response; }; -export const getAllCouncil = async () => { +export const getAllCouncils = async () => { const response = await https.get(`/v1/admin/councils`); if (isErrorResponse(response)) { throw new Error(response.reason); } - return response.users.map((data: any) => new Council(data)); + return response.users.map((data: any) => new Council(data)) as Council[]; }; export const postCheckEmail = async (email: string) => { diff --git a/service-manager/src/components/admin/MemberList.tsx b/service-manager/src/components/admin/MemberList.tsx index 1d844a3..1c4c53d 100644 --- a/service-manager/src/components/admin/MemberList.tsx +++ b/service-manager/src/components/admin/MemberList.tsx @@ -1,22 +1,14 @@ +import { useCouncils } from '../../hooks/react-query/useCouncils'; import { MemberItem } from './MemberItem'; export const MemberList = () => { + const { councils } = useCouncils(); + return ( <> - - + {councils.map((council) => ( + + ))} ); }; diff --git a/service-manager/src/hooks/react-query/useCouncils.tsx b/service-manager/src/hooks/react-query/useCouncils.tsx new file mode 100644 index 0000000..9a96642 --- /dev/null +++ b/service-manager/src/hooks/react-query/useCouncils.tsx @@ -0,0 +1,11 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getAllCouncils } from '../../apis/user.apis'; + +export const useCouncils = () => { + const { data: councils } = useSuspenseQuery({ + queryKey: ['council'], + queryFn: () => getAllCouncils(), + }); + + return { councils }; +}; From fed0091ad49415c5a2b19adb77e8036ae24cb9b5 Mon Sep 17 00:00:00 2001 From: 2yunseong Date: Mon, 11 Dec 2023 21:15:30 +0900 Subject: [PATCH 24/70] =?UTF-8?q?feat(manager):=20=EA=B6=8C=ED=95=9C=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/apis/user.apis.ts | 9 +++++++- .../src/components/admin/MemberRole.tsx | 8 +++++++ .../src/hooks/react-query/useCouncils.tsx | 2 +- .../src/hooks/react-query/useUser.tsx | 22 ++++++++++++++++++- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/service-manager/src/apis/user.apis.ts b/service-manager/src/apis/user.apis.ts index 33b94f1..2a7d9ea 100644 --- a/service-manager/src/apis/user.apis.ts +++ b/service-manager/src/apis/user.apis.ts @@ -1,5 +1,6 @@ import { https } from '../functions/https'; import { removeToken, setToken } from '../functions/jwt'; +import type { Role } from '../types/admin'; import { isErrorResponse } from './dtos/response.dtos'; import { UserToken, @@ -85,7 +86,13 @@ export const reissueToken = async (retryCallback: () => T): Promise => { return retryCallback(); }; -export const putAdminRole = async (userId: number, role: string) => { +export const putAdminRole = async ({ + userId, + role, +}: { + userId: number; + role: Role; +}) => { const response = await https.put(`/v1/admin/role/${userId}`, { role }); if (isErrorResponse(response)) { throw new Error(response.reason); diff --git a/service-manager/src/components/admin/MemberRole.tsx b/service-manager/src/components/admin/MemberRole.tsx index 69f59ff..96451c0 100644 --- a/service-manager/src/components/admin/MemberRole.tsx +++ b/service-manager/src/components/admin/MemberRole.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx'; import type { Role } from '../../types/admin'; +import { useAdminRoleMutate } from '../../hooks/react-query/useUser'; interface MemberRole { userId: number; @@ -13,9 +14,14 @@ const styleBySelectedRole = (selected: Role, role: Role) => { }; export const MemberRole = ({ role, userId }: MemberRole) => { + const { putAdminRoleMutate } = useAdminRoleMutate(); + const onChangeAdminRole = (changeRole: Role) => { + putAdminRoleMutate({ userId, role: changeRole }); + }; return ( - { - setIsCaptchaModalOpen(false); - }} - onSave={() => onSave({ isRegistration: true })} - /> + {isCaptchaModalOpen && ( + { + setIsCaptchaModalOpen(false); + }} + handleSave={({ inputCode, answerCode }) => + onSave({ + isRegistration: true, + captchaPendingCode: answerCode, + captchaAnswer: inputCode, + }) + } + /> + )} ); diff --git a/service-apply/src/hooks/useApplyForm.tsx b/service-apply/src/hooks/useApplyForm.tsx index feaa0dd..d2fafed 100644 --- a/service-apply/src/hooks/useApplyForm.tsx +++ b/service-apply/src/hooks/useApplyForm.tsx @@ -1,4 +1,4 @@ -import { useReducer } from 'react'; +import { useReducer, useState } from 'react'; import { ApplyFormInput } from '../functions/validator'; import { phoneNumberReplace, @@ -77,6 +77,8 @@ export const useApplyForm = (init?: ApplyFormInput) => { const { postRegistration } = useApplyMutate(); const navigate = useNavigate(); + const [isCaptchaModalOpen, setIsCaptchaModalOpen] = useState(false); + const onSave = ({ isRegistration, captchaPendingCode, @@ -98,11 +100,12 @@ export const useApplyForm = (init?: ApplyFormInput) => { }), { onError: (error) => { - console.error(error); + isCaptchaModalOpen && setIsCaptchaModalOpen(false); alert(error.message); throw new Error(error.message); }, onSuccess: (data) => { + isCaptchaModalOpen && setIsCaptchaModalOpen(false); if (!data) throw new Error('data is undefined'); dispatch({ type: 'reset', payload: null }); alert(data.message); @@ -113,5 +116,5 @@ export const useApplyForm = (init?: ApplyFormInput) => { ); }; - return { state, dispatch, onSave }; + return { state, dispatch, onSave, isCaptchaModalOpen, setIsCaptchaModalOpen }; }; From 0837223cb52a2b5a4e3f860035ac24233e66e977 Mon Sep 17 00:00:00 2001 From: j8won Date: Tue, 12 Dec 2023 00:57:03 +0900 Subject: [PATCH 39/70] =?UTF-8?q?feat(apply):=20=EC=BA=A1=EC=B1=A0=20get?= =?UTF-8?q?=20query=EB=A5=BC=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/hooks/react-query/useCaptcha.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 service-apply/src/hooks/react-query/useCaptcha.tsx diff --git a/service-apply/src/hooks/react-query/useCaptcha.tsx b/service-apply/src/hooks/react-query/useCaptcha.tsx new file mode 100644 index 0000000..9215bc6 --- /dev/null +++ b/service-apply/src/hooks/react-query/useCaptcha.tsx @@ -0,0 +1,14 @@ +import { useSuspenseQuery } from '@tanstack/react-query'; +import { getCaptcha } from '../../apis/captcha.apis'; + +export const useCaptchaQuery = () => { + const { data } = useSuspenseQuery({ + queryKey: ['captcha'], + queryFn: getCaptcha, + }); + + return { + captchaCode: data.captchaCode, + captchaImageUrl: data.captchaImageUrl, + }; +}; From 68cc12afa43a5f538a8b0de2aa16f24f72489c37 Mon Sep 17 00:00:00 2001 From: j8won Date: Tue, 12 Dec 2023 00:57:51 +0900 Subject: [PATCH 40/70] =?UTF-8?q?feat(apply):=20=EC=BA=A1=EC=B1=A0=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=EC=B0=BD=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=ED=9B=85?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/apply/ApplyCaptchaModal.tsx | 66 +++++++++---------- .../src/components/apply/CaptchaForm.tsx | 56 ++++++++++++++++ .../src/hooks/apply/useCaptchaForm.tsx | 27 ++++++++ 3 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 service-apply/src/components/apply/CaptchaForm.tsx create mode 100644 service-apply/src/hooks/apply/useCaptchaForm.tsx diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx index f27bb2b..aea264a 100644 --- a/service-apply/src/components/apply/ApplyCaptchaModal.tsx +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -1,47 +1,24 @@ -import { Button, InputText, Modal, Txt } from '@quokka/design-system'; +import { Modal } from '@quokka/design-system'; import { PropsWithChildren, Suspense, useState } from 'react'; -import ErrorBoundary from '../common/ErrorBoundray'; -import Delete from '../../assets/delete.svg'; +import { CaptchaForm } from './CaptchaForm'; +import { Spinner } from '../../assets/Spinner'; interface ApplyCaptchaModalProps extends PropsWithChildren { isOpen: boolean; onRequestClose: () => void; - onSave: () => void; + handleSave: ({ + inputCode, + answerCode, + }: { + inputCode: string; + answerCode: string; + }) => void; } -const CaptchaForm = ({ onSubmit }: { onSubmit: () => void }) => { - return ( -
- - 자동 신청 방지 - - -
- -
-
- -
-
- ); -}; - export const ApplyCaptchaModal = ({ isOpen, onRequestClose, - onSave, + handleSave, }: ApplyCaptchaModalProps) => { const [isLoading, setIsLoading] = useState(false); @@ -64,7 +41,26 @@ export const ApplyCaptchaModal = ({ borderRadius: '1rem', }} > - {isLoading ?
loading
: } + {isLoading ? ( +
+ + 신청 접수 중입니다. 잠시만 기다려주세요. + + + 새로고침 시 신청이 취소됩니다. + +
+ +
+
+ ) : ( + + setIsLoading(true)} + /> + + )} ); }; diff --git a/service-apply/src/components/apply/CaptchaForm.tsx b/service-apply/src/components/apply/CaptchaForm.tsx new file mode 100644 index 0000000..e9a25b0 --- /dev/null +++ b/service-apply/src/components/apply/CaptchaForm.tsx @@ -0,0 +1,56 @@ +import { useCaptchaForm } from '../../hooks/apply/useCaptchaForm'; +import { Txt, InputText, Button } from '@quokka/design-system'; + +interface CaptchaFormProps { + handleSave: ({ + inputCode, + answerCode, + }: { + inputCode: string; + answerCode: string; + }) => void; + handleSubmitLoading: () => void; +} + +export const CaptchaForm = ({ + handleSave, + handleSubmitLoading, +}: CaptchaFormProps) => { + const { input, handleInput, captchaImageUrl, handleSubmit } = useCaptchaForm({ + postRegistration: handleSave, + }); + + return ( +
+ + 자동 신청 방지 + +
+ +
+
+ +
+
+ +
+
+ ); +}; diff --git a/service-apply/src/hooks/apply/useCaptchaForm.tsx b/service-apply/src/hooks/apply/useCaptchaForm.tsx new file mode 100644 index 0000000..52fe754 --- /dev/null +++ b/service-apply/src/hooks/apply/useCaptchaForm.tsx @@ -0,0 +1,27 @@ +import { ChangeEventHandler, FormEventHandler, useState } from 'react'; +import { useCaptchaQuery } from '../react-query/useCaptcha'; + +export const useCaptchaForm = ({ + postRegistration, +}: { + postRegistration: ({ + inputCode, + answerCode, + }: { + inputCode: string; + answerCode: string; + }) => void; +}) => { + const [input, setInput] = useState(''); + const { captchaCode, captchaImageUrl } = useCaptchaQuery(); + + const handleInput: ChangeEventHandler = (e) => { + setInput(e.target.value.replace(/[^0-9]/g, '')); + }; + + const handleSubmit = () => { + postRegistration({ inputCode: input, answerCode: captchaCode }); + }; + + return { input, handleInput, captchaImageUrl, handleSubmit }; +}; From 1ec19514b1f2b9e00d01bf603f22d9d28b5da2b0 Mon Sep 17 00:00:00 2001 From: Kwon770 Date: Tue, 12 Dec 2023 10:12:00 +0900 Subject: [PATCH 41/70] chore: Add deploy.yaml for cicd --- .github/workflows/deploy.yaml | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/deploy.yaml diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..e720025 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,42 @@ +name: React App CI/CD +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + + steps: + - uses: actions/checkout@v2 + - name: Node.js ${{ matrix.node-version }} 사용 + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + + - name: env 파일 생성 + run: | + touch .env + echo VITE_PUBLIC_API_URL=${{ secrets.VITE_PUBLIC_API_URL }} >> .env + cat .env + + - name: Dependencies 설치 + run: npm install + + - name: 프로젝트 npm 빌드 + run: npm run build + + - name: S3 배포 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read --delete + env: + AWS_S3_BUCKET: ${{ secrets.AWS_STAGING_BUCKET_NAME }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + SOURCE_DIR: "build" \ No newline at end of file From 2645173116621bcff728061497e1490243218771 Mon Sep 17 00:00:00 2001 From: j8won Date: Tue, 12 Dec 2023 15:19:10 +0900 Subject: [PATCH 42/70] =?UTF-8?q?refactor(apply):=20=EC=BA=A1=EC=B1=A0=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20api=20=ED=95=A8=EC=88=98=20=EB=B0=8F=20Dto?= =?UTF-8?q?=EB=A5=BC=20registration=20=ED=8C=8C=EC=9D=BC=EC=97=90=EC=84=9C?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/captcha.apis.ts | 12 ------------ service-apply/src/apis/dtos/captcha.dtos.ts | 15 --------------- service-apply/src/apis/dtos/registration.dtos.ts | 16 ++++++++++++++++ service-apply/src/apis/registration.apis.ts | 10 ++++++++++ .../src/hooks/react-query/useCaptcha.tsx | 2 +- 5 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 service-apply/src/apis/captcha.apis.ts delete mode 100644 service-apply/src/apis/dtos/captcha.dtos.ts diff --git a/service-apply/src/apis/captcha.apis.ts b/service-apply/src/apis/captcha.apis.ts deleted file mode 100644 index cc3b58f..0000000 --- a/service-apply/src/apis/captcha.apis.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { https } from '../functions/https'; -import { Captcha } from './dtos/captcha.dtos'; -import { isErrorResponse } from './dtos/response.dtos'; - -export const getCaptcha = async () => { - const response = await https.get('/v1/captcha'); - if (isErrorResponse(response)) { - throw new Error('자동 신청 방지 이미지 조회를 실패했습니다.'); - } - - return new Captcha(response); -}; diff --git a/service-apply/src/apis/dtos/captcha.dtos.ts b/service-apply/src/apis/dtos/captcha.dtos.ts deleted file mode 100644 index 30925f7..0000000 --- a/service-apply/src/apis/dtos/captcha.dtos.ts +++ /dev/null @@ -1,15 +0,0 @@ -export class Captcha { - captchaCode: string; - captchaImageUrl: string; - - constructor({ - captchaCode, - captchaImageUrl, - }: { - captchaCode: string; - captchaImageUrl: string; - }) { - this.captchaCode = captchaCode; - this.captchaImageUrl = captchaImageUrl; - } -} diff --git a/service-apply/src/apis/dtos/registration.dtos.ts b/service-apply/src/apis/dtos/registration.dtos.ts index 7d80f03..55bf6f1 100644 --- a/service-apply/src/apis/dtos/registration.dtos.ts +++ b/service-apply/src/apis/dtos/registration.dtos.ts @@ -109,3 +109,19 @@ export class RegistrationOptionsResponse { this.affiliation = affiliation; } } + +export class CaptchaResponse { + captchaCode: string; + captchaImageUrl: string; + + constructor({ + captchaCode, + captchaImageUrl, + }: { + captchaCode: string; + captchaImageUrl: string; + }) { + this.captchaCode = captchaCode; + this.captchaImageUrl = captchaImageUrl; + } +} diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index 37d0eb7..274d1f3 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -1,5 +1,6 @@ import { https } from '../functions/https'; import { + CaptchaResponse, RegistrationOptionsResponse, RegistrationRequest, RegistrationResponse, @@ -50,3 +51,12 @@ export const getRegistration = } return new RegistrationOptionsResponse(response); }; + +export const getCaptcha = async () => { + const response = await https.get('/v1/captcha'); + if (isErrorResponse(response)) { + throw new Error('자동 신청 방지 이미지 조회를 실패했습니다.'); + } + + return new CaptchaResponse(response); +}; diff --git a/service-apply/src/hooks/react-query/useCaptcha.tsx b/service-apply/src/hooks/react-query/useCaptcha.tsx index 9215bc6..e7bf3ce 100644 --- a/service-apply/src/hooks/react-query/useCaptcha.tsx +++ b/service-apply/src/hooks/react-query/useCaptcha.tsx @@ -1,5 +1,5 @@ import { useSuspenseQuery } from '@tanstack/react-query'; -import { getCaptcha } from '../../apis/captcha.apis'; +import { getCaptcha } from 'service-apply/src/apis/registration.apis'; export const useCaptchaQuery = () => { const { data } = useSuspenseQuery({ From f9559acb4c2bb32223303b97c303fd30ea6b10a8 Mon Sep 17 00:00:00 2001 From: Kwon770 Date: Tue, 12 Dec 2023 17:32:13 +0900 Subject: [PATCH 43/70] refactor: Refactor deploy.yaml to change npm based workflow to pnpm based workflow --- .github/workflows/deploy.yaml | 49 +++++++++++++++++++++++++++++------ pnpm-lock.yaml | 8 +++--- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e720025..553cbd8 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -12,25 +12,58 @@ jobs: node-version: [18.x] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - name: Node.js ${{ matrix.node-version }} 사용 - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} + - name: pnpm 설치 + uses: pnpm/action-setup@v2 + with: + version: 8 + run_install: false + + - name: pnpm store 경로 설정 + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: pnpm cache 설정 + uses: actions/cache@v3 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: dependencies 설치 + run: pnpm install + - name: env 파일 생성 run: | touch .env echo VITE_PUBLIC_API_URL=${{ secrets.VITE_PUBLIC_API_URL }} >> .env cat .env - - name: Dependencies 설치 - run: npm install + - name: 프로젝트 pnpm 빌드 + run: | + pnpm build:apply + pnpm build:manager - - name: 프로젝트 npm 빌드 - run: npm run build + - name: service-apply 모듈 배포 + uses: jakejarvis/s3-sync-action@master + with: + args: --acl public-read --delete + env: + AWS_S3_BUCKET: ${{ secrets.AWS_STAGING_BUCKET_NAME }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + SOURCE_DIR: "dist/service-apply" - - name: S3 배포 + - name: service-manager 모듈 배포 uses: jakejarvis/s3-sync-action@master with: args: --acl public-read --delete @@ -39,4 +72,4 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - SOURCE_DIR: "build" \ No newline at end of file + SOURCE_DIR: "dist/service-manager" \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee202b8..17c70c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,5 @@ lockfileVersion: '6.0' -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - dependencies: '@swc/helpers': specifier: ~0.5.2 @@ -9179,3 +9175,7 @@ packages: /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: false + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false From 6f030f77f2c855a57ac880b9b457b5273c8ea77e Mon Sep 17 00:00:00 2001 From: Kwon770 Date: Tue, 12 Dec 2023 17:34:52 +0900 Subject: [PATCH 44/70] refactor: Distinguish buckets name per web server --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 553cbd8..58cdd1d 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -57,7 +57,7 @@ jobs: with: args: --acl public-read --delete env: - AWS_S3_BUCKET: ${{ secrets.AWS_STAGING_BUCKET_NAME }} + AWS_S3_BUCKET: ${{ secrets.AWS_APPLY_WS_BUCKET_NAME }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} @@ -68,7 +68,7 @@ jobs: with: args: --acl public-read --delete env: - AWS_S3_BUCKET: ${{ secrets.AWS_STAGING_BUCKET_NAME }} + AWS_S3_BUCKET: ${{ secrets.AWS_MANAGER_WS_BUCKET_NAME }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} From 4e25cc934abf59382a01a363c2840e606b816891 Mon Sep 17 00:00:00 2001 From: Kwon770 Date: Tue, 12 Dec 2023 17:53:11 +0900 Subject: [PATCH 45/70] fix: Add lcokfile parameter at pnpm install --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 58cdd1d..95067cc 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -39,7 +39,7 @@ jobs: ${{ runner.os }}-pnpm-store- - name: dependencies 설치 - run: pnpm install + run: pnpm install --lockfile - name: env 파일 생성 run: | From e984ef1840e37e151300f36f3bcf23e8a74a0132 Mon Sep 17 00:00:00 2001 From: sckwon770 Date: Tue, 12 Dec 2023 18:21:53 +0900 Subject: [PATCH 46/70] fix: Fix deploy.yaml compile error --- .github/workflows/deploy.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 95067cc..0ce4b24 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -56,20 +56,20 @@ jobs: uses: jakejarvis/s3-sync-action@master with: args: --acl public-read --delete - env: - AWS_S3_BUCKET: ${{ secrets.AWS_APPLY_WS_BUCKET_NAME }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - SOURCE_DIR: "dist/service-apply" + env: + AWS_S3_BUCKET: ${{ secrets.AWS_APPLY_WS_BUCKET_NAME }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + SOURCE_DIR: "dist/service-apply" - name: service-manager 모듈 배포 uses: jakejarvis/s3-sync-action@master with: args: --acl public-read --delete - env: - AWS_S3_BUCKET: ${{ secrets.AWS_MANAGER_WS_BUCKET_NAME }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} - SOURCE_DIR: "dist/service-manager" \ No newline at end of file + env: + AWS_S3_BUCKET: ${{ secrets.AWS_MANAGER_WS_BUCKET_NAME }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ secrets.AWS_DEFAULT_REGION }} + SOURCE_DIR: "dist/service-manager" From 507e5d4944a1d5f3b5ac422dd4aa54b8ae92a4f2 Mon Sep 17 00:00:00 2001 From: sckwon770 Date: Tue, 12 Dec 2023 18:39:46 +0900 Subject: [PATCH 47/70] fix: Remove pmpm caching workflow to fix runtime error --- .github/workflows/deploy.yaml | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 0ce4b24..ae4fc72 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -19,27 +19,13 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: pnpm 설치 - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v2 with: version: 8 - run_install: false - - - name: pnpm store 경로 설정 - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: pnpm cache 설정 - uses: actions/cache@v3 - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - - name: dependencies 설치 - run: pnpm install --lockfile + run_install: | + - recursive: true + args: [--frozen-lockfile, --strict-peer-dependencies] + - args: [--global, gulp, prettier, typescript] - name: env 파일 생성 run: | From e17d0c17bf62ce3ce8954c72af3a786a054c6838 Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 00:32:46 +0900 Subject: [PATCH 48/70] =?UTF-8?q?refactor(apply):=20=EC=8B=A0=EC=B2=AD=20a?= =?UTF-8?q?pi=20=ED=95=A8=EC=88=98=EB=A5=BC=20=EC=9E=84=EC=8B=9C=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=EA=B3=BC=20=EC=8B=A0=EC=B2=AD=20api=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/apis/dtos/registration.dtos.ts | 51 +++++++++++++------ service-apply/src/apis/registration.apis.ts | 35 ++++++++----- .../src/hooks/react-query/useApply.tsx | 44 ++++++++++++++++ .../src/hooks/react-query/useCaptcha.tsx | 14 ----- 4 files changed, 101 insertions(+), 43 deletions(-) delete mode 100644 service-apply/src/hooks/react-query/useCaptcha.tsx diff --git a/service-apply/src/apis/dtos/registration.dtos.ts b/service-apply/src/apis/dtos/registration.dtos.ts index 55bf6f1..5a50e9f 100644 --- a/service-apply/src/apis/dtos/registration.dtos.ts +++ b/service-apply/src/apis/dtos/registration.dtos.ts @@ -1,5 +1,4 @@ -export interface RegistrationRequestProps { - isRegistration: boolean; +export interface TemporarySaveRequestProps { name: string; studentNumber: string; affiliation: string; @@ -7,13 +6,9 @@ export interface RegistrationRequestProps { isLightCar: boolean; phoneNumber: string; selectSectorId: number; - captchaPendingCode?: string; - captchaAnswer?: string; - email: string; } -export class RegistrationRequest { - isRegistration: boolean; +export class TemporarySaveRequest { name: string; studentNum: string; affiliation: string; @@ -21,11 +16,8 @@ export class RegistrationRequest { isLight: boolean; phoneNum: string; selectSectorId: number; - captchaCode?: string; - captchaAnswer?: string; constructor({ - isRegistration, name, studentNumber, affiliation, @@ -33,11 +25,7 @@ export class RegistrationRequest { isLightCar, phoneNumber, selectSectorId, - captchaPendingCode, - captchaAnswer, - email, - }: RegistrationRequestProps) { - this.isRegistration = isRegistration; + }: TemporarySaveRequestProps) { this.name = name; this.studentNum = studentNumber; this.affiliation = affiliation; @@ -45,6 +33,39 @@ export class RegistrationRequest { this.isLight = isLightCar; this.phoneNum = phoneNumber; this.selectSectorId = selectSectorId; + } +} + +export interface RegistrationRequestProps extends TemporarySaveRequestProps { + captchaPendingCode: string; + captchaAnswer: string; +} + +export class RegistrationRequest extends TemporarySaveRequest { + captchaCode: string; + captchaAnswer: string; + + constructor({ + name, + studentNumber, + affiliation, + carNumber, + isLightCar, + phoneNumber, + selectSectorId, + captchaPendingCode, + captchaAnswer, + }: RegistrationRequestProps) { + super({ + name, + studentNumber, + affiliation, + carNumber, + isLightCar, + phoneNumber, + selectSectorId, + }); + this.captchaCode = captchaPendingCode; this.captchaAnswer = captchaAnswer; } diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index 274d1f3..9defd9d 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -4,27 +4,34 @@ import { RegistrationOptionsResponse, RegistrationRequest, RegistrationResponse, + TemporarySaveRequest, } from './dtos/registration.dtos'; import { isErrorResponse } from './dtos/response.dtos'; import { reissueToken } from './user.apis'; export const postRegistration = async ( - registration: RegistrationRequest, + data: RegistrationRequest, ): Promise => { - const { isRegistration, ...rest } = registration; - const response = await https.post( - `/v1/registration${registration.isRegistration ? '' : '/temporary'}`, - rest, - ); + const response = await https.post('/v1/registration', data); if (isErrorResponse(response)) { - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // if (response.status === 401 || response.status === 403) { - // return reissueToken(() => postRegistration(registration)); - // } - // throw new Error(response.reason); - if (registration.isRegistration) - throw new Error('주차권 신청에 실패했습니다'); - throw new Error('주차권 임시저장에 실패했습니다'); + if (response.status === 401 || response.status === 403) { + return reissueToken(() => postRegistration(data)); + } + console.log(response.reason); + throw new Error(response.reason); + } + return new RegistrationResponse(response); +}; + +export const postTemporarySave = async ( + data: TemporarySaveRequest, +): Promise => { + const response = await https.post('/v1/registration/temporary', data); + if (isErrorResponse(response)) { + if (response.status === 401 || response.status === 403) { + return reissueToken(() => postTemporarySave(data)); + } + throw new Error(response.reason); } return new RegistrationResponse(response); }; diff --git a/service-apply/src/hooks/react-query/useApply.tsx b/service-apply/src/hooks/react-query/useApply.tsx index 8113737..9aadfa0 100644 --- a/service-apply/src/hooks/react-query/useApply.tsx +++ b/service-apply/src/hooks/react-query/useApply.tsx @@ -6,10 +6,13 @@ import { import { RegistrationRequest, RegistrationResponse, + TemporarySaveRequest, } from '../../apis/dtos/registration.dtos'; import { + getCaptcha, getRegistration, postRegistration, + postTemporarySave, } from '../../apis/registration.apis'; export const useApplyMutate = () => { @@ -41,6 +44,35 @@ export const useApplyMutate = () => { }; }; +export const useTemporarySaveMutate = () => { + const { mutate } = useMutation({ + mutationKey: ['applyTemporarySave'], + mutationFn: postTemporarySave, + }); + + return { + postTemporarySave: ( + temporarySaveRequest: TemporarySaveRequest, + mutateOption?: Omit< + MutateOptions< + RegistrationResponse, + Error, + TemporarySaveRequest, + unknown + >, + 'onSettled' + >, + ) => { + mutate(temporarySaveRequest, { + ...mutateOption, + onSettled: (data) => { + if (!data) throw new Error('data is undefined'); + }, + }); + }, + }; +}; + export const useApplyQuery = () => { const { data } = useSuspenseQuery({ queryKey: ['apply'], @@ -54,3 +86,15 @@ export const useApplyQuery = () => { registrationData: data, }; }; + +export const useCaptchaQuery = () => { + const { data } = useSuspenseQuery({ + queryKey: ['captcha'], + queryFn: getCaptcha, + }); + + return { + captchaCode: data.captchaCode, + captchaImageUrl: data.captchaImageUrl, + }; +}; diff --git a/service-apply/src/hooks/react-query/useCaptcha.tsx b/service-apply/src/hooks/react-query/useCaptcha.tsx deleted file mode 100644 index e7bf3ce..0000000 --- a/service-apply/src/hooks/react-query/useCaptcha.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; -import { getCaptcha } from 'service-apply/src/apis/registration.apis'; - -export const useCaptchaQuery = () => { - const { data } = useSuspenseQuery({ - queryKey: ['captcha'], - queryFn: getCaptcha, - }); - - return { - captchaCode: data.captchaCode, - captchaImageUrl: data.captchaImageUrl, - }; -}; From 8436d96813464aca4903ff96c497183162d5efe2 Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 00:34:03 +0900 Subject: [PATCH 49/70] =?UTF-8?q?refactor(apply):=20ApplyForm=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=EB=A5=BC=20context=20api=EB=A1=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/apply/ApplyCaptchaModal.tsx | 10 +- .../src/components/apply/ApplyForm.tsx | 255 +++++++++--------- .../src/components/apply/CaptchaForm.tsx | 12 +- .../src/hooks/apply/useApplyFormContext.tsx | 61 +++++ .../src/hooks/apply/useCaptchaForm.tsx | 54 +++- service-apply/src/hooks/useApplyForm.tsx | 111 ++------ service-apply/src/store/ApplyFormContext.tsx | 26 ++ 7 files changed, 282 insertions(+), 247 deletions(-) create mode 100644 service-apply/src/hooks/apply/useApplyFormContext.tsx create mode 100644 service-apply/src/store/ApplyFormContext.tsx diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx index aea264a..5e2cf95 100644 --- a/service-apply/src/components/apply/ApplyCaptchaModal.tsx +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -6,19 +6,11 @@ import { Spinner } from '../../assets/Spinner'; interface ApplyCaptchaModalProps extends PropsWithChildren { isOpen: boolean; onRequestClose: () => void; - handleSave: ({ - inputCode, - answerCode, - }: { - inputCode: string; - answerCode: string; - }) => void; } export const ApplyCaptchaModal = ({ isOpen, onRequestClose, - handleSave, }: ApplyCaptchaModalProps) => { const [isLoading, setIsLoading] = useState(false); @@ -56,8 +48,8 @@ export const ApplyCaptchaModal = ({ ) : ( setIsLoading(true)} + closeModal={onRequestClose} /> )} diff --git a/service-apply/src/components/apply/ApplyForm.tsx b/service-apply/src/components/apply/ApplyForm.tsx index cf61286..e5fc9fa 100644 --- a/service-apply/src/components/apply/ApplyForm.tsx +++ b/service-apply/src/components/apply/ApplyForm.tsx @@ -1,15 +1,14 @@ +import { clsx } from 'clsx'; import { InputText, Button, InputTextProps, Radio, Txt, - Modal, } from '@quokka/design-system'; import { useApplyForm } from '../../hooks/useApplyForm'; +import { ApplyFormContext } from '../../store/ApplyFormContext'; import { ApplySelector } from './ApplySelector'; -import { clsx } from 'clsx'; -import { useApplyQuery } from '../../hooks/react-query/useApply'; import { ApplyCaptchaModal } from './ApplyCaptchaModal'; export const ApplyInputText = ({ className, ...props }: InputTextProps) => { @@ -21,13 +20,14 @@ export const ApplyInputText = ({ className, ...props }: InputTextProps) => { }; export const ApplyForm = () => { - const { registrationData } = useApplyQuery(); - const { sector, selectSectorId, ...rest } = registrationData; - const { state, dispatch, onSave, isCaptchaModalOpen, setIsCaptchaModalOpen } = - useApplyForm({ - section: selectSectorId ?? 0, - ...rest, - }); + const { + sector, + state, + dispatch, + onTemporarySave, + isCaptchaModalOpen, + setIsCaptchaModalOpen, + } = useApplyForm(); const parkingSection = sector.map((item) => ({ sectionNumber: item.sectorId, @@ -36,128 +36,127 @@ export const ApplyForm = () => { parkingSection.unshift({ sectionMajor: '선택', sectionNumber: 0 }); return ( -
- - dispatch({ type: 'phoneNumber', payload: e.target.value }) - } - value={state.phoneNumber} - required - /> - - - dispatch({ type: 'studentName', payload: e.target.value }) - } - value={state.studentName} - required - /> - - dispatch({ type: 'studentNumber', payload: e.target.value }) - } - value={state.studentNumber} - required - /> - - dispatch({ type: 'affiliation', payload: e.target.value }) - } - value={state.affiliation} - required - /> - dispatch({ type: 'section', payload: e.target.value })} - value={state.section} - required - /> - - dispatch({ type: 'carNumber', payload: e.target.value }) - } - value={state.carNumber} - required - /> -
-
- 경차 여부 * + +
+ + dispatch({ type: 'phoneNumber', payload: e.target.value }) + } + value={state.phoneNumber} + required + /> + + + dispatch({ type: 'studentName', payload: e.target.value }) + } + value={state.studentName} + required + /> + + dispatch({ type: 'studentNumber', payload: e.target.value }) + } + value={state.studentNumber} + required + /> + + dispatch({ type: 'affiliation', payload: e.target.value }) + } + value={state.affiliation} + required + /> + + dispatch({ type: 'section', payload: e.target.value }) + } + value={state.section} + required + /> + + dispatch({ type: 'carNumber', payload: e.target.value }) + } + value={state.carNumber} + required + /> +
+
+ 경차 여부 * +
+
+ { + dispatch({ type: 'isCompact', payload: true }); + }} + /> + { + dispatch({ type: 'isCompact', payload: false }); + }} + /> +
-
- { - dispatch({ type: 'isCompact', payload: true }); +
+ + + {isCaptchaModalOpen && ( + { + setIsCaptchaModalOpen(false); + }} + /> + )}
-
- - - {isCaptchaModalOpen && ( - { - setIsCaptchaModalOpen(false); - }} - handleSave={({ inputCode, answerCode }) => - onSave({ - isRegistration: true, - captchaPendingCode: answerCode, - captchaAnswer: inputCode, - }) - } - /> - )} -
-
+
); }; diff --git a/service-apply/src/components/apply/CaptchaForm.tsx b/service-apply/src/components/apply/CaptchaForm.tsx index e9a25b0..6c35653 100644 --- a/service-apply/src/components/apply/CaptchaForm.tsx +++ b/service-apply/src/components/apply/CaptchaForm.tsx @@ -2,22 +2,16 @@ import { useCaptchaForm } from '../../hooks/apply/useCaptchaForm'; import { Txt, InputText, Button } from '@quokka/design-system'; interface CaptchaFormProps { - handleSave: ({ - inputCode, - answerCode, - }: { - inputCode: string; - answerCode: string; - }) => void; handleSubmitLoading: () => void; + closeModal: () => void; } export const CaptchaForm = ({ - handleSave, handleSubmitLoading, + closeModal, }: CaptchaFormProps) => { const { input, handleInput, captchaImageUrl, handleSubmit } = useCaptchaForm({ - postRegistration: handleSave, + closeModal, }); return ( diff --git a/service-apply/src/hooks/apply/useApplyFormContext.tsx b/service-apply/src/hooks/apply/useApplyFormContext.tsx new file mode 100644 index 0000000..11ea112 --- /dev/null +++ b/service-apply/src/hooks/apply/useApplyFormContext.tsx @@ -0,0 +1,61 @@ +import { useReducer } from 'react'; +import { + phoneNumberReplace, + studentNumberReplace, +} from '../../functions/replacer'; +import { + ApplyFormContextType, + initApplyFormValue, +} from '../../store/ApplyFormContext'; + +type AppFormInputAction = + | { + type: keyof ApplyFormContextType; + payload: ApplyFormContextType[keyof ApplyFormContextType]; + } + | { + type: 'reset'; + payload: null; + }; + +const applyFormReducer = ( + state: ApplyFormContextType, + action: AppFormInputAction, +): ApplyFormContextType => { + switch (action.type) { + case 'phoneNumber': + return { + ...state, + phoneNumber: phoneNumberReplace(action.payload.toString()), + }; + case 'studentNumber': + return { + ...state, + studentNumber: studentNumberReplace(action.payload.toString()), + }; + case 'reset': + return { + ...state, + ...initApplyFormValue, + }; + case 'affiliation': + case 'email': + case 'studentName': + case 'section': + case 'carNumber': + case 'isCompact': + return { + ...state, + [action.type]: action.payload, + }; + default: + return state; + } +}; + +export const useApplyFormContext = (init?: ApplyFormContextType) => { + init ||= initApplyFormValue; + const [state, dispatch] = useReducer(applyFormReducer, init); + + return { state, dispatch }; +}; diff --git a/service-apply/src/hooks/apply/useCaptchaForm.tsx b/service-apply/src/hooks/apply/useCaptchaForm.tsx index 52fe754..e6e1492 100644 --- a/service-apply/src/hooks/apply/useCaptchaForm.tsx +++ b/service-apply/src/hooks/apply/useCaptchaForm.tsx @@ -1,26 +1,52 @@ -import { ChangeEventHandler, FormEventHandler, useState } from 'react'; -import { useCaptchaQuery } from '../react-query/useCaptcha'; +import { ChangeEventHandler, useContext, useState } from 'react'; +import { useApplyMutate, useCaptchaQuery } from '../react-query/useApply'; +import { RegistrationRequest } from '../../apis/dtos/registration.dtos'; +import { ApplyFormContext } from '../../store/ApplyFormContext'; +import { removeToken } from '../../functions/jwt'; +import { useNavigate } from 'react-router-dom'; -export const useCaptchaForm = ({ - postRegistration, -}: { - postRegistration: ({ - inputCode, - answerCode, - }: { - inputCode: string; - answerCode: string; - }) => void; -}) => { +export const useCaptchaForm = ({ closeModal }: { closeModal: () => void }) => { + const navigate = useNavigate(); const [input, setInput] = useState(''); + const { captchaCode, captchaImageUrl } = useCaptchaQuery(); + const { postRegistration } = useApplyMutate(); + const state = useContext(ApplyFormContext); const handleInput: ChangeEventHandler = (e) => { setInput(e.target.value.replace(/[^0-9]/g, '')); }; const handleSubmit = () => { - postRegistration({ inputCode: input, answerCode: captchaCode }); + postRegistration( + new RegistrationRequest({ + name: state.studentName, + studentNumber: state.studentNumber, + affiliation: state.affiliation, + isLightCar: state.isCompact, + carNumber: state.carNumber, + phoneNumber: state.phoneNumber, + selectSectorId: +state.section, + captchaPendingCode: captchaCode, + captchaAnswer: input, + }), + { + // FIXME: onError, onSuccess 동작 안 함 (쿼리 devtools에서는 error 캐칭 됨) + onError: (error) => { + console.log(error); + alert(error.message); + closeModal(); + throw new Error(error.message); + }, + onSuccess: (data) => { + if (!data) throw new Error('data is undefined'); + alert(data.message); + removeToken(); + closeModal(); + navigate('/'); + }, + }, + ); }; return { input, handleInput, captchaImageUrl, handleSubmit }; diff --git a/service-apply/src/hooks/useApplyForm.tsx b/service-apply/src/hooks/useApplyForm.tsx index d2fafed..d12a96d 100644 --- a/service-apply/src/hooks/useApplyForm.tsx +++ b/service-apply/src/hooks/useApplyForm.tsx @@ -1,92 +1,27 @@ -import { useReducer, useState } from 'react'; -import { ApplyFormInput } from '../functions/validator'; -import { - phoneNumberReplace, - studentNumberReplace, -} from '../functions/replacer'; -import { useApplyMutate } from './react-query/useApply'; +import { useState } from 'react'; +import { useApplyQuery, useTemporarySaveMutate } from './react-query/useApply'; import { useNavigate } from 'react-router-dom'; import { removeToken } from '../functions/jwt'; -import { RegistrationRequest } from '../apis/dtos/registration.dtos'; +import { TemporarySaveRequest } from '../apis/dtos/registration.dtos'; +import { useApplyFormContext } from './apply/useApplyFormContext'; -type AppFormInputAction = - | { - type: keyof ApplyFormInput; - payload: ApplyFormInput[keyof ApplyFormInput]; - } - | { - type: 'reset'; - payload: null; - }; - -const applyFormReducer = ( - state: ApplyFormInput, - action: AppFormInputAction, -): ApplyFormInput => { - switch (action.type) { - case 'phoneNumber': - return { - ...state, - phoneNumber: phoneNumberReplace(action.payload.toString()), - }; - case 'studentNumber': - return { - ...state, - studentNumber: studentNumberReplace(action.payload.toString()), - }; - case 'reset': - return { - ...state, - ...initValue, - }; - case 'affiliation': - case 'email': - case 'studentName': - case 'section': - case 'carNumber': - case 'isCompact': - return { - ...state, - [action.type]: action.payload, - }; - default: - return state; - } -}; - -const initValue = { - phoneNumber: '', - studentNumber: '', - email: '', - studentName: '', - affiliation: '', - section: 0, - carNumber: '', - isCompact: false, -}; +export const useApplyForm = () => { + const navigate = useNavigate(); -interface registrationInfo { - isRegistration: boolean; - captchaPendingCode?: string; - captchaAnswer?: string; -} + const { registrationData } = useApplyQuery(); + const { sector, selectSectorId, ...rest } = registrationData; -export const useApplyForm = (init?: ApplyFormInput) => { - init ||= initValue; - const [state, dispatch] = useReducer(applyFormReducer, init); - const { postRegistration } = useApplyMutate(); - const navigate = useNavigate(); + const { state, dispatch } = useApplyFormContext({ + section: selectSectorId ?? 0, + ...rest, + }); + const { postTemporarySave } = useTemporarySaveMutate(); const [isCaptchaModalOpen, setIsCaptchaModalOpen] = useState(false); - const onSave = ({ - isRegistration, - captchaPendingCode, - captchaAnswer, - }: registrationInfo) => { - postRegistration( - new RegistrationRequest({ - isRegistration: isRegistration, + const onTemporarySave = () => { + postTemporarySave( + new TemporarySaveRequest({ name: state.studentName, studentNumber: state.studentNumber, affiliation: state.affiliation, @@ -94,18 +29,13 @@ export const useApplyForm = (init?: ApplyFormInput) => { carNumber: state.carNumber, phoneNumber: state.phoneNumber, selectSectorId: +state.section, - captchaPendingCode: captchaPendingCode, - captchaAnswer: captchaAnswer, - email: state.email, }), { onError: (error) => { - isCaptchaModalOpen && setIsCaptchaModalOpen(false); alert(error.message); throw new Error(error.message); }, onSuccess: (data) => { - isCaptchaModalOpen && setIsCaptchaModalOpen(false); if (!data) throw new Error('data is undefined'); dispatch({ type: 'reset', payload: null }); alert(data.message); @@ -116,5 +46,12 @@ export const useApplyForm = (init?: ApplyFormInput) => { ); }; - return { state, dispatch, onSave, isCaptchaModalOpen, setIsCaptchaModalOpen }; + return { + sector, + state, + dispatch, + onTemporarySave, + isCaptchaModalOpen, + setIsCaptchaModalOpen, + }; }; diff --git a/service-apply/src/store/ApplyFormContext.tsx b/service-apply/src/store/ApplyFormContext.tsx new file mode 100644 index 0000000..a00ff89 --- /dev/null +++ b/service-apply/src/store/ApplyFormContext.tsx @@ -0,0 +1,26 @@ +import { createContext } from 'react'; + +export interface ApplyFormContextType { + phoneNumber: string; + studentNumber: string; + email: string; + studentName: string; + affiliation: string; + section: number; + carNumber: string; + isCompact: boolean; +} + +export const initApplyFormValue = { + phoneNumber: '', + studentNumber: '', + email: '', + studentName: '', + affiliation: '', + section: 0, + carNumber: '', + isCompact: false, +}; + +export const ApplyFormContext = + createContext(initApplyFormValue); From 200d226a55bd397fe578f7dae1a0d42df47cdbaa Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 00:38:35 +0900 Subject: [PATCH 50/70] =?UTF-8?q?feat(apply):=20api=20=ED=86=B5=EC=8B=A0?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20reason=EC=9D=84=20=ED=99=9C?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/apis/registration.apis.ts | 7 +++---- service-apply/src/apis/user.apis.ts | 16 ++++------------ 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/service-apply/src/apis/registration.apis.ts b/service-apply/src/apis/registration.apis.ts index 9defd9d..6cfc212 100644 --- a/service-apply/src/apis/registration.apis.ts +++ b/service-apply/src/apis/registration.apis.ts @@ -40,10 +40,9 @@ export const getRegistration = async (): Promise => { const response = await https.get('/v1/registration'); if (isErrorResponse(response)) { - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // if (response.status === 401 || response.status === 403) { - // return reissueToken(getRegistration); - // } + if (response.status === 401 || response.status === 403) { + return reissueToken(getRegistration); + } return new RegistrationOptionsResponse({ carNum: '', email: '', diff --git a/service-apply/src/apis/user.apis.ts b/service-apply/src/apis/user.apis.ts index 6916dbd..88f4666 100644 --- a/service-apply/src/apis/user.apis.ts +++ b/service-apply/src/apis/user.apis.ts @@ -11,9 +11,7 @@ export interface UserLoginRequest { export const postLogin = async (data: UserLoginRequest) => { const response = await https.post(`/v1/auth/login`, data); if (isErrorResponse(response)) { - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // throw new Error(response.reason); - throw new Error('로그인을 실패했습니다'); + throw new Error(response.reason); } return new UserToken(response); }; @@ -25,9 +23,7 @@ export interface PasswordFindRequest { export const postPasswordFind = async ({ email }: PasswordFindRequest) => { const response = await https.post(`/v1/user/password/find`, { email }); if (isErrorResponse(response)) { - throw new Error('이메일 전송에 실패했습니다'); - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // throw new Error(response.reason); + throw new Error(response.reason); } return new PasswordFind(response); }; @@ -45,9 +41,7 @@ export const postPasswordReset = async ({ password, }); if (isErrorResponse(response)) { - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // throw new Error(response.reason); - throw new Error('비밀번호 초기화를 실패했습니다'); + throw new Error(response.reason); } return new PasswordReset(response); }; @@ -61,9 +55,7 @@ export const reissueToken = async (retryCallback: () => T): Promise => { const response = await https.post(`/v1/auth/login`, { refreshtoken: token }); if (isErrorResponse(response)) { removeToken(); - // TODO: response dto에 status와 reason을 추가해야 아래 로직 가능 - // throw new Error(response.reason); - throw new Error('토큰 재발급에 실패했습니다. 다시 로그인 해주세요.'); + throw new Error(response.reason); } setToken(new UserToken(response.data)); From 6be4aefef92ba31d789a867fbb2084c45e214aa4 Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 00:51:53 +0900 Subject: [PATCH 51/70] =?UTF-8?q?refactor(apply):=20ApplyCaptchaModalProps?= =?UTF-8?q?=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=83=81=EC=86=8D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/apply/ApplyCaptchaModal.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx index 5e2cf95..31eed3d 100644 --- a/service-apply/src/components/apply/ApplyCaptchaModal.tsx +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -2,8 +2,9 @@ import { Modal } from '@quokka/design-system'; import { PropsWithChildren, Suspense, useState } from 'react'; import { CaptchaForm } from './CaptchaForm'; import { Spinner } from '../../assets/Spinner'; +import ErrorBoundary from '../common/ErrorBoundray'; -interface ApplyCaptchaModalProps extends PropsWithChildren { +interface ApplyCaptchaModalProps { isOpen: boolean; onRequestClose: () => void; } @@ -47,10 +48,12 @@ export const ApplyCaptchaModal = ({
) : ( - setIsLoading(true)} - closeModal={onRequestClose} - /> + + setIsLoading(true)} + closeModal={onRequestClose} + /> + )} From 8352c07f6a7688b2fcc65f56f7da5e229b0e146c Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 17:12:17 +0900 Subject: [PATCH 52/70] =?UTF-8?q?fix(apply):=20useCaptchaForm=20=ED=9B=85?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=20=EC=9C=84=EC=B9=98=EB=A5=BC=20CaptchaFo?= =?UTF-8?q?rm=EC=97=90=EC=84=9C=20ApplyCaptchaModal=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/apply/ApplyCaptchaModal.tsx | 23 +++++++++------- .../src/components/apply/ApplyForm.tsx | 21 +++++++++------ .../src/components/apply/CaptchaForm.tsx | 27 +++++++++---------- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx index 31eed3d..55d7797 100644 --- a/service-apply/src/components/apply/ApplyCaptchaModal.tsx +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -1,8 +1,9 @@ import { Modal } from '@quokka/design-system'; -import { PropsWithChildren, Suspense, useState } from 'react'; +import { Suspense, useState } from 'react'; import { CaptchaForm } from './CaptchaForm'; import { Spinner } from '../../assets/Spinner'; import ErrorBoundary from '../common/ErrorBoundray'; +import { useCaptchaForm } from '../../hooks/apply/useCaptchaForm'; interface ApplyCaptchaModalProps { isOpen: boolean; @@ -14,6 +15,9 @@ export const ApplyCaptchaModal = ({ onRequestClose, }: ApplyCaptchaModalProps) => { const [isLoading, setIsLoading] = useState(false); + const { input, handleInput, captchaImageUrl, handleSubmit } = useCaptchaForm({ + closeModal: onRequestClose, + }); return (
) : ( - - - setIsLoading(true)} - closeModal={onRequestClose} - /> - - + { + setIsLoading(true); + handleSubmit(); + }} + /> )} ); diff --git a/service-apply/src/components/apply/ApplyForm.tsx b/service-apply/src/components/apply/ApplyForm.tsx index e5fc9fa..f6fe4a3 100644 --- a/service-apply/src/components/apply/ApplyForm.tsx +++ b/service-apply/src/components/apply/ApplyForm.tsx @@ -10,6 +10,8 @@ import { useApplyForm } from '../../hooks/useApplyForm'; import { ApplyFormContext } from '../../store/ApplyFormContext'; import { ApplySelector } from './ApplySelector'; import { ApplyCaptchaModal } from './ApplyCaptchaModal'; +import { Suspense } from 'react'; +import ErrorBoundary from '../common/ErrorBoundray'; export const ApplyInputText = ({ className, ...props }: InputTextProps) => { return ( @@ -147,14 +149,17 @@ export const ApplyForm = () => { > 신청하기 - {isCaptchaModalOpen && ( - { - setIsCaptchaModalOpen(false); - }} - /> - )} + + + + { + setIsCaptchaModalOpen(false); + }} + /> + +
diff --git a/service-apply/src/components/apply/CaptchaForm.tsx b/service-apply/src/components/apply/CaptchaForm.tsx index 6c35653..fc24ede 100644 --- a/service-apply/src/components/apply/CaptchaForm.tsx +++ b/service-apply/src/components/apply/CaptchaForm.tsx @@ -1,19 +1,19 @@ -import { useCaptchaForm } from '../../hooks/apply/useCaptchaForm'; +import { ChangeEventHandler } from 'react'; import { Txt, InputText, Button } from '@quokka/design-system'; interface CaptchaFormProps { - handleSubmitLoading: () => void; - closeModal: () => void; + codeInput: string; + handleCodeInput: ChangeEventHandler; + captchaImageUrl: string; + handleSubmit: () => void; } export const CaptchaForm = ({ - handleSubmitLoading, - closeModal, + codeInput, + handleCodeInput, + captchaImageUrl, + handleSubmit, }: CaptchaFormProps) => { - const { input, handleInput, captchaImageUrl, handleSubmit } = useCaptchaForm({ - closeModal, - }); - return (
@@ -25,8 +25,8 @@ export const CaptchaForm = ({
- - - ); -}; diff --git a/service-manager/src/pages/notice/NoticeCreate.page.tsx b/service-manager/src/pages/notice/NoticeCreate.page.tsx deleted file mode 100644 index 7ea384e..0000000 --- a/service-manager/src/pages/notice/NoticeCreate.page.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Txt } from '@quokka/design-system'; -import { NoticeForm } from '../../components/notice/NoticeForm'; - -export const NoticeCreate = () => { - return ( -
- 안내사항 작성 - -
- ); -}; From ffe7970a0f2593cf858480471a1e5c57e941936e Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 18:08:12 +0900 Subject: [PATCH 56/70] =?UTF-8?q?refactor(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B7=B0=EC=96=B4=EC=9D=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/notice/NoticeRead.tsx | 39 ------------------- .../src/components/notice/NoticeView.tsx | 33 ++++++++++++++++ .../src/pages/notice/NoticeView.page.tsx | 10 +++-- 3 files changed, 40 insertions(+), 42 deletions(-) delete mode 100644 service-manager/src/components/notice/NoticeRead.tsx create mode 100644 service-manager/src/components/notice/NoticeView.tsx diff --git a/service-manager/src/components/notice/NoticeRead.tsx b/service-manager/src/components/notice/NoticeRead.tsx deleted file mode 100644 index 596945f..0000000 --- a/service-manager/src/components/notice/NoticeRead.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Button, Txt } from '@quokka/design-system'; -import ReactQuill from 'react-quill'; -import { NOTICE_CONTENTS } from '../../constants/NoticeContents'; -import { Link } from 'react-router-dom'; - -interface NoticeReadProps { - title?: string; - content?: string; - createdAt?: string; - className?: string; -} - -export const NoticeRead = ({ - title = NOTICE_CONTENTS.title, - content = NOTICE_CONTENTS.content, - createdAt = NOTICE_CONTENTS.createdAt, - className, -}: NoticeReadProps) => { - return ( - <> -
- {title} - - {createdAt} - - -
- - - - - ); -}; diff --git a/service-manager/src/components/notice/NoticeView.tsx b/service-manager/src/components/notice/NoticeView.tsx new file mode 100644 index 0000000..02cd721 --- /dev/null +++ b/service-manager/src/components/notice/NoticeView.tsx @@ -0,0 +1,33 @@ +import { Viewer } from '@toast-ui/react-editor'; +import { useState, useRef, lazy, Suspense } from 'react'; +import { Button } from '@quokka/design-system'; +import { Link } from 'react-router-dom'; + +const ToastViewer = lazy(() => + import('@toast-ui/react-editor').then((module) => ({ + default: module.Viewer, + })), +); + +export const INIT_CONTENT = '## 안내사항 \n - 안내사항을 작성해주세요.'; + +interface NoticeFormProps { + content: string; +} +export const NoticeView = ({ + content: inintContent = INIT_CONTENT, +}: NoticeFormProps) => { + const editorRef = useRef(null); + const [content, setContent] = useState(inintContent); + + return ( + <> + Loading...
}> + + + + + + + ); +}; diff --git a/service-manager/src/pages/notice/NoticeView.page.tsx b/service-manager/src/pages/notice/NoticeView.page.tsx index 99f0258..1d1aaab 100644 --- a/service-manager/src/pages/notice/NoticeView.page.tsx +++ b/service-manager/src/pages/notice/NoticeView.page.tsx @@ -1,5 +1,9 @@ -import { NoticeRead } from '../../components/notice/NoticeRead'; +import { INIT_CONTENT, NoticeView } from '../../components/notice/NoticeView'; -export const NoticeView = () => { - return ; +export const NoticeViewPage = () => { + return ( +
+ +
+ ); }; From 9b0aeefc59a4bab66150c225cfc25681c52a8205 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 18:08:32 +0900 Subject: [PATCH 57/70] =?UTF-8?q?feat(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/notice/NoticeUpdate.tsx | 65 +++++++++++++++++ .../src/hooks/react-query/useNotice.tsx | 35 +++++++++ service-manager/src/hooks/useNoticeForm.tsx | 72 +++++++++++++++++++ .../src/pages/notice/NoticeUpdate.tsx | 11 +++ 4 files changed, 183 insertions(+) create mode 100644 service-manager/src/components/notice/NoticeUpdate.tsx create mode 100644 service-manager/src/hooks/react-query/useNotice.tsx create mode 100644 service-manager/src/hooks/useNoticeForm.tsx create mode 100644 service-manager/src/pages/notice/NoticeUpdate.tsx diff --git a/service-manager/src/components/notice/NoticeUpdate.tsx b/service-manager/src/components/notice/NoticeUpdate.tsx new file mode 100644 index 0000000..b987c71 --- /dev/null +++ b/service-manager/src/components/notice/NoticeUpdate.tsx @@ -0,0 +1,65 @@ +import { Button } from '@quokka/design-system'; +import { Editor } from '@toast-ui/react-editor'; +import { useState, useRef, lazy, Suspense } from 'react'; +import { + useNoticeQuery, + useNoticeMutate, +} from '../../hooks/react-query/useNotice'; +import { useNoticeForm } from '../../hooks/useNoticeForm'; +import '@toast-ui/editor/dist/toastui-editor.css'; +import '@toast-ui/editor/dist/theme/toastui-editor-dark.css'; + +const ToastEditor = lazy(() => + import('@toast-ui/react-editor').then((module) => ({ + default: module.Editor, + })), +); + +export const NoticeUpdate = () => { + const editorRef = useRef(null); + const { noticeData } = useNoticeQuery(); + const { state, dispatch, onSubmit } = useNoticeForm({ + content: noticeData?.noticeContent || '# 안내 사항', + }); + + const onSubmitNotice = (e: React.FormEvent) => { + e.preventDefault(); + const editorInstance = editorRef.current?.getInstance(); + const markdown = editorInstance?.getMarkdown() ?? ''; + dispatch({ type: 'content', payload: markdown }); + onSubmit(); + }; + + return ( + <> +
+ Loading...
}> + + +
+ +
+ + + ); +}; diff --git a/service-manager/src/hooks/react-query/useNotice.tsx b/service-manager/src/hooks/react-query/useNotice.tsx new file mode 100644 index 0000000..330117a --- /dev/null +++ b/service-manager/src/hooks/react-query/useNotice.tsx @@ -0,0 +1,35 @@ +import { getNotice, putNotice } from '../../apis/notice.apis'; +import { useQuery, useMutation, MutateOptions } from '@tanstack/react-query'; +import { Notice } from '../../apis/dtos/notice.dtos'; + +export const useNoticeQuery = () => { + const { data } = useQuery({ + queryKey: ['notice'], + queryFn: getNotice, + gcTime: Infinity, + refetchOnReconnect: false, + refetchOnWindowFocus: false, + }); + return { noticeData: data }; +}; + +export const useNoticeMutate = () => { + const { mutate } = useMutation({ + mutationKey: ['notice'], + mutationFn: putNotice, + }); + + return { + putNotice: ( + content: Notice, + mutateOption?: Omit, 'onSettled'>, + ) => { + mutate(content, { + ...mutateOption, + onSettled: (data) => { + if (!data) throw new Error('data is undefined'); + }, + }); + }, + }; +}; diff --git a/service-manager/src/hooks/useNoticeForm.tsx b/service-manager/src/hooks/useNoticeForm.tsx new file mode 100644 index 0000000..95f627a --- /dev/null +++ b/service-manager/src/hooks/useNoticeForm.tsx @@ -0,0 +1,72 @@ +import { useNoticeMutate, useNoticeQuery } from './react-query/useNotice'; +import { useNavigate } from 'react-router-dom'; +import { useReducer } from 'react'; + +interface NoticeFormProps { + content: string; +} + +type NoticeFormInputAction = + | { + type: keyof NoticeFormProps; + payload: NoticeFormProps[keyof NoticeFormProps]; + } + | { + type: 'reset'; + payload: null; + }; + +const noticeFormReducer = ( + state: NoticeFormProps, + action: NoticeFormInputAction, +): NoticeFormProps => { + switch (action.type) { + case 'content': + return { + ...state, + content: action.payload, + }; + case 'reset': + return { + ...state, + ...initValue, + }; + default: + return state; + } +}; + +const initValue = { + content: '', +}; + +export const useNoticeForm = (init?: NoticeFormProps) => { + init ||= initValue; + const [state, dispatch] = useReducer(noticeFormReducer, init); + const { putNotice } = useNoticeMutate(); + const navigate = useNavigate(); + + const onSubmit = () => { + putNotice( + { noticeContent: state.content }, + { + onError: (error) => { + console.log(error); + alert(error.message); + throw new Error(error.message); + }, + onSuccess: (data) => { + if (!data) throw new Error('data is undefined'); + dispatch({ type: 'reset', payload: null }); + alert('안내사항이 수정되었습니다.'); + navigate('/notice'); + }, + }, + ); + }; + return { + state, + dispatch, + onSubmit, + }; +}; diff --git a/service-manager/src/pages/notice/NoticeUpdate.tsx b/service-manager/src/pages/notice/NoticeUpdate.tsx new file mode 100644 index 0000000..304e5d2 --- /dev/null +++ b/service-manager/src/pages/notice/NoticeUpdate.tsx @@ -0,0 +1,11 @@ +import { Txt } from '@quokka/design-system'; +import { NoticeUpdate } from '../../components/notice/NoticeUpdate'; + +export const NoticeUpdatePage = () => { + return ( +
+ 안내사항 작성 + +
+ ); +}; From 77433e161d62d07d6cd9c2dee479ee4143f5eb5b Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 18:08:54 +0900 Subject: [PATCH 58/70] =?UTF-8?q?fix(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EC=B6=A9?= =?UTF-8?q?=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/router/index.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/service-manager/src/router/index.tsx b/service-manager/src/router/index.tsx index 00ee571..cfc682c 100644 --- a/service-manager/src/router/index.tsx +++ b/service-manager/src/router/index.tsx @@ -4,8 +4,7 @@ import { SignUpPage } from '../pages/SignUp.page'; import { AnnouncementPage } from '../pages/announcement/Announcement.page'; import { AnnouncementCreatePage } from '../pages/announcement/AnnouncementCreate.page'; import { ApplyListPage } from '../pages/ApplyList.page'; -import { NoticeView } from '../pages/notice/NoticeView.page'; -import { NoticeCreate } from '../pages/notice/NoticeCreate.page'; +import { NoticeViewPage } from '../pages/notice/NoticeView.page'; import { AdminPage } from '../pages/Admin.page'; import { PasswordResetLayout } from '../pages/PasswordReset/PasswordResetLayout.page'; import { RequestPasswordResetPage } from '../pages/PasswordReset/RequestPasswordReset.page'; @@ -40,9 +39,7 @@ export default function Router() { element={} /> } /> - } /> - } /> - } /> + } /> }> } /> } /> From 4ba2c38fc178629a1a1ace7306688b62663b9b07 Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 19:05:15 +0900 Subject: [PATCH 59/70] =?UTF-8?q?refactor(apply):=20=EC=BA=A1=EC=B1=A0=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=EC=B0=BD=20isLoading=EC=9D=84=20useCaptchaFo?= =?UTF-8?q?rm=EC=97=90=EC=84=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/apply/ApplyCaptchaModal.tsx | 13 +++++-------- service-apply/src/hooks/apply/useCaptchaForm.tsx | 10 ++++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/service-apply/src/components/apply/ApplyCaptchaModal.tsx b/service-apply/src/components/apply/ApplyCaptchaModal.tsx index 07df087..e36d280 100644 --- a/service-apply/src/components/apply/ApplyCaptchaModal.tsx +++ b/service-apply/src/components/apply/ApplyCaptchaModal.tsx @@ -14,10 +14,10 @@ export const ApplyCaptchaModal = ({ isOpen, onRequestClose, }: ApplyCaptchaModalProps) => { - const [isLoading, setIsLoading] = useState(false); - const { input, handleInput, captchaImageUrl, handleSubmit } = useCaptchaForm({ - closeModal: onRequestClose, - }); + const { isLoading, input, handleInput, captchaImageUrl, handleSubmit } = + useCaptchaForm({ + closeModal: onRequestClose, + }); return ( { - setIsLoading(true); - handleSubmit(); - }} + handleSubmit={handleSubmit} /> )} diff --git a/service-apply/src/hooks/apply/useCaptchaForm.tsx b/service-apply/src/hooks/apply/useCaptchaForm.tsx index e6e1492..401f493 100644 --- a/service-apply/src/hooks/apply/useCaptchaForm.tsx +++ b/service-apply/src/hooks/apply/useCaptchaForm.tsx @@ -13,11 +13,14 @@ export const useCaptchaForm = ({ closeModal }: { closeModal: () => void }) => { const { postRegistration } = useApplyMutate(); const state = useContext(ApplyFormContext); + const [isLoading, setIsLoading] = useState(false); + const handleInput: ChangeEventHandler = (e) => { setInput(e.target.value.replace(/[^0-9]/g, '')); }; const handleSubmit = () => { + setIsLoading(true); postRegistration( new RegistrationRequest({ name: state.studentName, @@ -31,11 +34,9 @@ export const useCaptchaForm = ({ closeModal }: { closeModal: () => void }) => { captchaAnswer: input, }), { - // FIXME: onError, onSuccess 동작 안 함 (쿼리 devtools에서는 error 캐칭 됨) onError: (error) => { - console.log(error); alert(error.message); - closeModal(); + setIsLoading(false); throw new Error(error.message); }, onSuccess: (data) => { @@ -43,11 +44,12 @@ export const useCaptchaForm = ({ closeModal }: { closeModal: () => void }) => { alert(data.message); removeToken(); closeModal(); + setIsLoading(false); navigate('/'); }, }, ); }; - return { input, handleInput, captchaImageUrl, handleSubmit }; + return { isLoading, input, handleInput, captchaImageUrl, handleSubmit }; }; From 9c5d2064a135a8b9df2d150c83a4a9a0065e60a3 Mon Sep 17 00:00:00 2001 From: j8won Date: Wed, 13 Dec 2023 19:05:51 +0900 Subject: [PATCH 60/70] =?UTF-8?q?rename(apply):=20useApplyForm=EC=9D=84=20?= =?UTF-8?q?hooks/apply=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-apply/src/components/apply/ApplyForm.tsx | 2 +- service-apply/src/hooks/{ => apply}/useApplyForm.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) rename service-apply/src/hooks/{ => apply}/useApplyForm.tsx (83%) diff --git a/service-apply/src/components/apply/ApplyForm.tsx b/service-apply/src/components/apply/ApplyForm.tsx index f6fe4a3..00d7729 100644 --- a/service-apply/src/components/apply/ApplyForm.tsx +++ b/service-apply/src/components/apply/ApplyForm.tsx @@ -6,7 +6,7 @@ import { Radio, Txt, } from '@quokka/design-system'; -import { useApplyForm } from '../../hooks/useApplyForm'; +import { useApplyForm } from '../../hooks/apply/useApplyForm'; import { ApplyFormContext } from '../../store/ApplyFormContext'; import { ApplySelector } from './ApplySelector'; import { ApplyCaptchaModal } from './ApplyCaptchaModal'; diff --git a/service-apply/src/hooks/useApplyForm.tsx b/service-apply/src/hooks/apply/useApplyForm.tsx similarity index 83% rename from service-apply/src/hooks/useApplyForm.tsx rename to service-apply/src/hooks/apply/useApplyForm.tsx index d12a96d..ea6aa8a 100644 --- a/service-apply/src/hooks/useApplyForm.tsx +++ b/service-apply/src/hooks/apply/useApplyForm.tsx @@ -1,9 +1,9 @@ import { useState } from 'react'; -import { useApplyQuery, useTemporarySaveMutate } from './react-query/useApply'; +import { useApplyQuery, useTemporarySaveMutate } from '../react-query/useApply'; import { useNavigate } from 'react-router-dom'; -import { removeToken } from '../functions/jwt'; -import { TemporarySaveRequest } from '../apis/dtos/registration.dtos'; -import { useApplyFormContext } from './apply/useApplyFormContext'; +import { removeToken } from '../../functions/jwt'; +import { TemporarySaveRequest } from '../../apis/dtos/registration.dtos'; +import { useApplyFormContext } from './useApplyFormContext'; export const useApplyForm = () => { const navigate = useNavigate(); From 8020b249f18d700507efa1885f083817b2dd9b37 Mon Sep 17 00:00:00 2001 From: loopy-lim Date: Wed, 13 Dec 2023 10:24:28 +0000 Subject: [PATCH 61/70] =?UTF-8?q?feat(manager):=20setting=20API=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/apis/coupon.apis.ts | 15 ------- service-manager/src/apis/dtos/times.dtos.ts | 8 ++++ .../{sectors.apis.ts => settings.apis.ts} | 21 ++++++++++ .../src/components/setting/SettingTable.tsx | 10 +---- .../src/components/setting/SettingTime.tsx | 36 ++++++++++++---- .../src/hooks/react-query/useSetting.tsx | 41 ++++++++++++++++++- .../hooks/useSetting/useSectionSetting.tsx | 23 +++++++---- .../useSetting/useSectionTimeSetting.tsx | 24 ++++++++++- 8 files changed, 137 insertions(+), 41 deletions(-) delete mode 100644 service-manager/src/apis/coupon.apis.ts create mode 100644 service-manager/src/apis/dtos/times.dtos.ts rename service-manager/src/apis/{sectors.apis.ts => settings.apis.ts} (64%) diff --git a/service-manager/src/apis/coupon.apis.ts b/service-manager/src/apis/coupon.apis.ts deleted file mode 100644 index 9536b27..0000000 --- a/service-manager/src/apis/coupon.apis.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { https } from '../functions/https'; -import { isErrorResponse } from './dtos/response.dtos'; - -interface CouponRequest { - startAt: string; - endAt: string; -} - -export const postCoupon = async (data: CouponRequest) => { - const response = await https.post(`/v1/coupon`, data); - if (isErrorResponse(response)) { - throw new Error(response.reason); - } - return response; -}; diff --git a/service-manager/src/apis/dtos/times.dtos.ts b/service-manager/src/apis/dtos/times.dtos.ts new file mode 100644 index 0000000..5cad9b3 --- /dev/null +++ b/service-manager/src/apis/dtos/times.dtos.ts @@ -0,0 +1,8 @@ +export class SettingTime { + startAt: Date; + endAt: Date; + constructor({ startAt, endAt }: { startAt: string; endAt: string }) { + this.startAt = new Date(startAt); + this.endAt = new Date(endAt); + } +} diff --git a/service-manager/src/apis/sectors.apis.ts b/service-manager/src/apis/settings.apis.ts similarity index 64% rename from service-manager/src/apis/sectors.apis.ts rename to service-manager/src/apis/settings.apis.ts index 12e82d9..a8c6e25 100644 --- a/service-manager/src/apis/sectors.apis.ts +++ b/service-manager/src/apis/settings.apis.ts @@ -1,6 +1,7 @@ import { https } from '../functions/https'; import { isErrorResponse } from './dtos/response.dtos'; import { Sector } from './dtos/sector.dtos'; +import { SettingTime } from './dtos/times.dtos'; interface SectorRequset { name: string; @@ -40,3 +41,23 @@ export const deleteSector = async (sectorNumber: string) => { } return response; }; + +export const getSettingTime = async () => { + const response = await https.get(`/v1/events/period`); + if (isErrorResponse(response)) { + throw new Error(response.reason); + } + return new SettingTime(response); +}; + +export const postSettingTime = async (date: SettingTime) => { + const datesString = { + startAt: date.startAt.toISOString().slice(0, 19), + endAt: date.endAt.toISOString().slice(0, 19), + }; + const response = await https.post(`/v1/events`, datesString); + if (isErrorResponse(response)) { + throw new Error(response.reason); + } + return response as { message: string }; +}; diff --git a/service-manager/src/components/setting/SettingTable.tsx b/service-manager/src/components/setting/SettingTable.tsx index 2e6586c..b472989 100644 --- a/service-manager/src/components/setting/SettingTable.tsx +++ b/service-manager/src/components/setting/SettingTable.tsx @@ -83,15 +83,7 @@ export const SettingTable = () => {
-
diff --git a/service-manager/src/components/setting/SettingTime.tsx b/service-manager/src/components/setting/SettingTime.tsx index b532c7d..a86ccd1 100644 --- a/service-manager/src/components/setting/SettingTime.tsx +++ b/service-manager/src/components/setting/SettingTime.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import DatePicker, { registerLocale, setDefaultLocale } from 'react-datepicker'; import { Button, Txt } from '@quokka/design-system'; import { ko } from 'date-fns/locale'; @@ -6,6 +6,7 @@ import { setMinutes, setHours } from 'date-fns'; import 'react-datepicker/dist/react-datepicker.css'; import './DateTime.css'; +import { useSectionTimeSetting } from '../../hooks/useSetting/useSectionTimeSetting'; registerLocale('ko', ko); setDefaultLocale('ko'); @@ -21,7 +22,7 @@ interface SettingTimeProps { const DateTimePicker = ({ date, setDate, title }: SettingTimeProps) => { const selectedYear = date.getFullYear(); - const selectedMonth = date.getMonth().toString().padStart(2, '0'); + const selectedMonth = (date.getMonth() + 1).toString().padStart(2, '0'); const selectedDay = date.getDate().toString().padStart(2, '0'); const selectedHour = date.getHours().toString().padStart(2, '0'); const selectedMinute = date.getMinutes().toString().padStart(2, '0'); @@ -48,10 +49,15 @@ const DateTimePicker = ({ date, setDate, title }: SettingTimeProps) => { }; export const SettingTime = () => { - const [opendate, setOpenDate] = useState(new Date()); - const [endDate, setEndDate] = useState( - setHours(setMinutes(new Date(), 59), 23), - ); + const { timeSettingData, updateSettingTime } = useSectionTimeSetting(); + + const [opendate, setOpenDate] = useState(timeSettingData.startAt); + const [endDate, setEndDate] = useState(timeSettingData.endAt); + + useEffect(() => { + setOpenDate(timeSettingData.startAt); + setEndDate(timeSettingData.endAt); + }, [timeSettingData]); return ( <> @@ -69,12 +75,28 @@ export const SettingTime = () => { date={endDate} setDate={(date) => { if (!date) return; + if (date < opendate) return; setEndDate(date); }} title="Close" />
- diff --git a/service-manager/src/hooks/react-query/useSetting.tsx b/service-manager/src/hooks/react-query/useSetting.tsx index b25ab4e..eb1c487 100644 --- a/service-manager/src/hooks/react-query/useSetting.tsx +++ b/service-manager/src/hooks/react-query/useSetting.tsx @@ -6,11 +6,14 @@ import { import { deleteSector, getSectors, + getSettingTime, postSectors, + postSettingTime, putSectors, -} from '../../apis/sectors.apis'; +} from '../../apis/settings.apis'; import { Sector } from '../../apis/dtos/sector.dtos'; import { useQueryClient } from '@tanstack/react-query'; +import { SettingTime } from 'service-manager/src/apis/dtos/times.dtos'; export const useSectorsQuery = () => { const { data } = useSuspenseQuery({ @@ -99,3 +102,39 @@ export const useSectorDeleteMutate = () => { }), }; }; + +export const useTimeSettingQuery = () => { + const { data } = useSuspenseQuery({ + queryKey: ['timeSetting'], + queryFn: getSettingTime, + gcTime: Infinity, + refetchOnWindowFocus: false, + }); + + return { timeSettingData: data }; +}; + +export const useTimeSettingUpdateMutate = () => { + const { mutate } = useMutation({ + mutationKey: ['timeSettingUpdate'], + mutationFn: postSettingTime, + }); + const queryClient = useQueryClient(); + + return { + postSettingTime: ( + times: SettingTime, + mutateOption?: Omit< + MutateOptions<{ message: string }, Error, SettingTime, unknown>, + 'onSettled' + >, + ) => + mutate(times, { + ...mutateOption, + onSettled: (data) => { + if (!data) throw new Error('data is undefined'); + queryClient.invalidateQueries({ queryKey: ['timeSetting'] }); + }, + }), + }; +}; diff --git a/service-manager/src/hooks/useSetting/useSectionSetting.tsx b/service-manager/src/hooks/useSetting/useSectionSetting.tsx index 47aa820..a11e836 100644 --- a/service-manager/src/hooks/useSetting/useSectionSetting.tsx +++ b/service-manager/src/hooks/useSetting/useSectionSetting.tsx @@ -5,7 +5,6 @@ import { useSectorUpdateMutate, useSectorsQuery, } from '../react-query/useSetting'; -import { Sector } from '../../apis/dtos/sector.dtos'; export const useSectionSettingTable = () => { const { sectorSettingData } = useSectorsQuery(); @@ -14,11 +13,6 @@ export const useSectionSettingTable = () => { const { putSectors } = useSectorUpdateMutate(); const [data, setData] = useState(sectorSettingData); - - useEffect(() => { - setData(sectorSettingData); - }, [sectorSettingData]); - const [isEdit, setIsEdit] = useState( sectorSettingData.map((data) => ({ id: data.id, @@ -26,6 +20,16 @@ export const useSectionSettingTable = () => { })), ); + useEffect(() => { + setData(sectorSettingData); + setIsEdit( + sectorSettingData.map((data) => ({ + id: data.id, + isEdit: false, + })), + ); + }, [sectorSettingData]); + const onEditValue = (id: number, type?: 'numeric') => (e: React.ChangeEvent) => { @@ -51,7 +55,7 @@ export const useSectionSettingTable = () => { const toggleEdit = (id: number) => { if (getIsEdit(id)) { - putSectors([data.find((data) => data.id === id)!], { + putSectors(data, { onError: (error) => { alert(error.message); }, @@ -90,7 +94,10 @@ export const useSectionSettingTable = () => { }); }; - const createSection = (section: Omit[]) => { + const createSection = () => { + const section = [ + { name: '', reserve: 0, sectorCapacity: 0, sectorNumber: '' }, + ]; postSectors(section, { onError: (error) => { alert(error.message); diff --git a/service-manager/src/hooks/useSetting/useSectionTimeSetting.tsx b/service-manager/src/hooks/useSetting/useSectionTimeSetting.tsx index a12170d..aa2546e 100644 --- a/service-manager/src/hooks/useSetting/useSectionTimeSetting.tsx +++ b/service-manager/src/hooks/useSetting/useSectionTimeSetting.tsx @@ -1 +1,23 @@ -export const useSectionTimeSetting = () => {}; +import { SettingTime } from 'service-manager/src/apis/dtos/times.dtos'; +import { + useTimeSettingQuery, + useTimeSettingUpdateMutate, +} from '../react-query/useSetting'; + +export const useSectionTimeSetting = () => { + const { timeSettingData } = useTimeSettingQuery(); + const { postSettingTime } = useTimeSettingUpdateMutate(); + + const updateSettingTime = (time: SettingTime) => { + postSettingTime(time, { + onSuccess: () => { + alert('수정되었습니다.'); + }, + onError: (error) => { + alert(error.message); + }, + }); + }; + + return { timeSettingData, updateSettingTime }; +}; From 8a24be6de849280c04b845011cbd798accb966f3 Mon Sep 17 00:00:00 2001 From: 2yunseong Date: Wed, 13 Dec 2023 19:30:38 +0900 Subject: [PATCH 62/70] =?UTF-8?q?fix(manager):=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/apis/user.apis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service-manager/src/apis/user.apis.ts b/service-manager/src/apis/user.apis.ts index 43ec387..efeb93e 100644 --- a/service-manager/src/apis/user.apis.ts +++ b/service-manager/src/apis/user.apis.ts @@ -100,9 +100,9 @@ export const putAdminRole = async ({ return response; }; -export const getAllCouncil = async () => { +export const getAllCouncils = async () => { const response = await https.get('/v1/admin/councils'); - + if (isErrorResponse(response)) { throw new Error(response.reason); } From de2e75785cfae66dfc71053dddef789c13667db1 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 20:07:23 +0900 Subject: [PATCH 63/70] =?UTF-8?q?fix(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=EC=9D=98=20=EB=82=B4=EC=9A=A9=EC=9D=84=20state=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/notice/NoticeUpdate.tsx | 27 +++++---- .../src/hooks/react-query/useNotice.tsx | 9 ++- service-manager/src/hooks/useNoticeForm.tsx | 58 +++---------------- 3 files changed, 29 insertions(+), 65 deletions(-) diff --git a/service-manager/src/components/notice/NoticeUpdate.tsx b/service-manager/src/components/notice/NoticeUpdate.tsx index b987c71..64ef54f 100644 --- a/service-manager/src/components/notice/NoticeUpdate.tsx +++ b/service-manager/src/components/notice/NoticeUpdate.tsx @@ -1,10 +1,7 @@ import { Button } from '@quokka/design-system'; import { Editor } from '@toast-ui/react-editor'; -import { useState, useRef, lazy, Suspense } from 'react'; -import { - useNoticeQuery, - useNoticeMutate, -} from '../../hooks/react-query/useNotice'; +import { useRef, lazy, Suspense } from 'react'; +import { useNoticeQuery } from '../../hooks/react-query/useNotice'; import { useNoticeForm } from '../../hooks/useNoticeForm'; import '@toast-ui/editor/dist/toastui-editor.css'; import '@toast-ui/editor/dist/theme/toastui-editor-dark.css'; @@ -18,16 +15,18 @@ const ToastEditor = lazy(() => export const NoticeUpdate = () => { const editorRef = useRef(null); const { noticeData } = useNoticeQuery(); - const { state, dispatch, onSubmit } = useNoticeForm({ - content: noticeData?.noticeContent || '# 안내 사항', - }); + const { content, onUpdate } = useNoticeForm(); - const onSubmitNotice = (e: React.FormEvent) => { + const onUpdateNotice = (e: React.FormEvent) => { e.preventDefault(); const editorInstance = editorRef.current?.getInstance(); - const markdown = editorInstance?.getMarkdown() ?? ''; - dispatch({ type: 'content', payload: markdown }); - onSubmit(); + if (!editorInstance) throw new Error('editorInstance is undefined'); + const markdown = editorInstance.getMarkdown(); + if (!markdown) { + alert('내용을 입력해주세요.'); + return; + } + onUpdate({ noticeContent: markdown }); }; return ( @@ -35,12 +34,12 @@ export const NoticeUpdate = () => {
Loading...}> { @@ -14,6 +19,7 @@ export const useNoticeQuery = () => { }; export const useNoticeMutate = () => { + const queryClient = useQueryClient(); const { mutate } = useMutation({ mutationKey: ['notice'], mutationFn: putNotice, @@ -28,6 +34,7 @@ export const useNoticeMutate = () => { ...mutateOption, onSettled: (data) => { if (!data) throw new Error('data is undefined'); + queryClient.invalidateQueries({ queryKey: ['notice'] }); }, }); }, diff --git a/service-manager/src/hooks/useNoticeForm.tsx b/service-manager/src/hooks/useNoticeForm.tsx index 95f627a..9bf8b96 100644 --- a/service-manager/src/hooks/useNoticeForm.tsx +++ b/service-manager/src/hooks/useNoticeForm.tsx @@ -1,72 +1,30 @@ import { useNoticeMutate, useNoticeQuery } from './react-query/useNotice'; import { useNavigate } from 'react-router-dom'; -import { useReducer } from 'react'; +import { useState } from 'react'; interface NoticeFormProps { - content: string; + noticeContent: string; } -type NoticeFormInputAction = - | { - type: keyof NoticeFormProps; - payload: NoticeFormProps[keyof NoticeFormProps]; - } - | { - type: 'reset'; - payload: null; - }; - -const noticeFormReducer = ( - state: NoticeFormProps, - action: NoticeFormInputAction, -): NoticeFormProps => { - switch (action.type) { - case 'content': - return { - ...state, - content: action.payload, - }; - case 'reset': - return { - ...state, - ...initValue, - }; - default: - return state; - } -}; - -const initValue = { - content: '', -}; - -export const useNoticeForm = (init?: NoticeFormProps) => { - init ||= initValue; - const [state, dispatch] = useReducer(noticeFormReducer, init); +export const useNoticeForm = () => { + const [content] = useState(''); const { putNotice } = useNoticeMutate(); const navigate = useNavigate(); - const onSubmit = () => { + const onUpdate = ({ noticeContent: content }: NoticeFormProps) => { putNotice( - { noticeContent: state.content }, + { noticeContent: content }, { onError: (error) => { - console.log(error); alert(error.message); - throw new Error(error.message); }, onSuccess: (data) => { if (!data) throw new Error('data is undefined'); - dispatch({ type: 'reset', payload: null }); - alert('안내사항이 수정되었습니다.'); + alert('수정되었습니다.'); navigate('/notice'); }, }, ); }; - return { - state, - dispatch, - onSubmit, - }; + return { content, onUpdate }; }; From a0484a15a446845f044ccb415ccaf643e0617b0f Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 20:07:42 +0900 Subject: [PATCH 64/70] =?UTF-8?q?fix(manager):=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=EA=B0=92=20=EC=83=81=EC=88=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/components/notice/NoticeView.tsx | 12 ++++++------ service-manager/src/pages/notice/NoticeView.page.tsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/service-manager/src/components/notice/NoticeView.tsx b/service-manager/src/components/notice/NoticeView.tsx index 02cd721..5ae2158 100644 --- a/service-manager/src/components/notice/NoticeView.tsx +++ b/service-manager/src/components/notice/NoticeView.tsx @@ -2,6 +2,7 @@ import { Viewer } from '@toast-ui/react-editor'; import { useState, useRef, lazy, Suspense } from 'react'; import { Button } from '@quokka/design-system'; import { Link } from 'react-router-dom'; +import { useNoticeQuery } from '../../hooks/react-query/useNotice'; const ToastViewer = lazy(() => import('@toast-ui/react-editor').then((module) => ({ @@ -9,16 +10,15 @@ const ToastViewer = lazy(() => })), ); -export const INIT_CONTENT = '## 안내사항 \n - 안내사항을 작성해주세요.'; - interface NoticeFormProps { content: string; } -export const NoticeView = ({ - content: inintContent = INIT_CONTENT, -}: NoticeFormProps) => { +export const NoticeView = () => { + const { noticeData } = useNoticeQuery(); const editorRef = useRef(null); - const [content, setContent] = useState(inintContent); + const [content] = useState( + noticeData?.noticeContent || '## 안내사항을 작성해주세요.', + ); return ( <> diff --git a/service-manager/src/pages/notice/NoticeView.page.tsx b/service-manager/src/pages/notice/NoticeView.page.tsx index 1d1aaab..efcda3e 100644 --- a/service-manager/src/pages/notice/NoticeView.page.tsx +++ b/service-manager/src/pages/notice/NoticeView.page.tsx @@ -1,9 +1,9 @@ -import { INIT_CONTENT, NoticeView } from '../../components/notice/NoticeView'; +import { NoticeView } from '../../components/notice/NoticeView'; export const NoticeViewPage = () => { return (
- +
); }; From e47b8c0ec93556cb16b004e662eda89b8ce4d614 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 20:17:16 +0900 Subject: [PATCH 65/70] =?UTF-8?q?refactor(manager):=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=20=EC=95=88=EB=82=B4=EC=82=AC=ED=95=AD=20=EC=83=81=EC=88=98?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/notice/NoticeUpdate.tsx | 4 +++- .../src/components/notice/NoticeView.tsx | 14 +++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/service-manager/src/components/notice/NoticeUpdate.tsx b/service-manager/src/components/notice/NoticeUpdate.tsx index 64ef54f..b1f2af4 100644 --- a/service-manager/src/components/notice/NoticeUpdate.tsx +++ b/service-manager/src/components/notice/NoticeUpdate.tsx @@ -12,6 +12,8 @@ const ToastEditor = lazy(() => })), ); +export const Default_Notice = '## 안내사항을 작성해주세요.'; + export const NoticeUpdate = () => { const editorRef = useRef(null); const { noticeData } = useNoticeQuery(); @@ -39,7 +41,7 @@ export const NoticeUpdate = () => { > Loading...}> import('@toast-ui/react-editor').then((module) => ({ @@ -10,24 +11,19 @@ const ToastViewer = lazy(() => })), ); -interface NoticeFormProps { - content: string; -} export const NoticeView = () => { const { noticeData } = useNoticeQuery(); const editorRef = useRef(null); - const [content] = useState( - noticeData?.noticeContent || '## 안내사항을 작성해주세요.', - ); + const [content] = useState(noticeData?.noticeContent || Default_Notice); return ( <> - Loading...}> - - + Loading...}> + + ); }; From 2c0a8538427c7879477713e744889c6dec09fb20 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 20:17:28 +0900 Subject: [PATCH 66/70] =?UTF-8?q?style(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/pages/notice/NoticeView.page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-manager/src/pages/notice/NoticeView.page.tsx b/service-manager/src/pages/notice/NoticeView.page.tsx index efcda3e..bdac436 100644 --- a/service-manager/src/pages/notice/NoticeView.page.tsx +++ b/service-manager/src/pages/notice/NoticeView.page.tsx @@ -2,7 +2,7 @@ import { NoticeView } from '../../components/notice/NoticeView'; export const NoticeViewPage = () => { return ( -
+
); From 40bf4dd69dc26c9b0b3b41788cdb1395decc641c Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 20:24:35 +0900 Subject: [PATCH 67/70] =?UTF-8?q?fix(manager):=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=ED=9B=84=20=EC=BF=BC=EB=A6=AC=EA=B0=92=EC=9D=84=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/hooks/react-query/useNotice.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-manager/src/hooks/react-query/useNotice.tsx b/service-manager/src/hooks/react-query/useNotice.tsx index 4a77e2d..9df4442 100644 --- a/service-manager/src/hooks/react-query/useNotice.tsx +++ b/service-manager/src/hooks/react-query/useNotice.tsx @@ -34,7 +34,7 @@ export const useNoticeMutate = () => { ...mutateOption, onSettled: (data) => { if (!data) throw new Error('data is undefined'); - queryClient.invalidateQueries({ queryKey: ['notice'] }); + queryClient.setQueryData(['notice'], data); }, }); }, From 09643fb695bf1e7d83d51f61b6f8688fd7bde504 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 21:55:57 +0900 Subject: [PATCH 68/70] =?UTF-8?q?refactor(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=97=90=EB=9F=AC=20=EB=B0=94=EC=9A=B4?= =?UTF-8?q?=EB=8D=94=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/notice/NoticeUpdate.tsx | 41 ++++++++++--------- .../src/components/notice/NoticeView.tsx | 11 +++-- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/service-manager/src/components/notice/NoticeUpdate.tsx b/service-manager/src/components/notice/NoticeUpdate.tsx index b1f2af4..4088142 100644 --- a/service-manager/src/components/notice/NoticeUpdate.tsx +++ b/service-manager/src/components/notice/NoticeUpdate.tsx @@ -5,6 +5,7 @@ import { useNoticeQuery } from '../../hooks/react-query/useNotice'; import { useNoticeForm } from '../../hooks/useNoticeForm'; import '@toast-ui/editor/dist/toastui-editor.css'; import '@toast-ui/editor/dist/theme/toastui-editor-dark.css'; +import ErrorBoundary from '../common/ErrorBoundary'; const ToastEditor = lazy(() => import('@toast-ui/react-editor').then((module) => ({ @@ -17,7 +18,7 @@ export const Default_Notice = '## 안내사항을 작성해주세요.'; export const NoticeUpdate = () => { const editorRef = useRef(null); const { noticeData } = useNoticeQuery(); - const { content, onUpdate } = useNoticeForm(); + const { onUpdate } = useNoticeForm(); const onUpdateNotice = (e: React.FormEvent) => { e.preventDefault(); @@ -39,24 +40,26 @@ export const NoticeUpdate = () => { onSubmit={onUpdateNotice} className="my-4" > - Loading...
}> - -
+ + Loading...}> + + +
diff --git a/service-manager/src/components/notice/NoticeView.tsx b/service-manager/src/components/notice/NoticeView.tsx index 307a06c..547d590 100644 --- a/service-manager/src/components/notice/NoticeView.tsx +++ b/service-manager/src/components/notice/NoticeView.tsx @@ -4,6 +4,7 @@ import { Button } from '@quokka/design-system'; import { Link } from 'react-router-dom'; import { useNoticeQuery } from '../../hooks/react-query/useNotice'; import { Default_Notice } from './NoticeUpdate'; +import ErrorBoundary from '../common/ErrorBoundary'; const ToastViewer = lazy(() => import('@toast-ui/react-editor').then((module) => ({ @@ -14,16 +15,18 @@ const ToastViewer = lazy(() => export const NoticeView = () => { const { noticeData } = useNoticeQuery(); const editorRef = useRef(null); - const [content] = useState(noticeData?.noticeContent || Default_Notice); + const content = noticeData.noticeContent ?? Default_Notice; return ( <> - Loading...}> - - + + Loading...}> + + + ); }; From 1fa3fc3632c6524535135106d8a91916796fc710 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 21:56:17 +0900 Subject: [PATCH 69/70] =?UTF-8?q?chore(manager):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/hooks/useNoticeForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/service-manager/src/hooks/useNoticeForm.tsx b/service-manager/src/hooks/useNoticeForm.tsx index 9bf8b96..63d6018 100644 --- a/service-manager/src/hooks/useNoticeForm.tsx +++ b/service-manager/src/hooks/useNoticeForm.tsx @@ -1,4 +1,4 @@ -import { useNoticeMutate, useNoticeQuery } from './react-query/useNotice'; +import { useNoticeMutate } from './react-query/useNotice'; import { useNavigate } from 'react-router-dom'; import { useState } from 'react'; From fe23e0454b45322b1ad3834e5abf4a3a1d72fa71 Mon Sep 17 00:00:00 2001 From: dxwwan Date: Wed, 13 Dec 2023 21:57:07 +0900 Subject: [PATCH 70/70] =?UTF-8?q?fix(manager):=20=EC=95=88=EB=82=B4?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=BF=BC=EB=A6=AC=EB=A5=BC=20suspense=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service-manager/src/hooks/react-query/useNotice.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service-manager/src/hooks/react-query/useNotice.tsx b/service-manager/src/hooks/react-query/useNotice.tsx index 9df4442..fe21c3b 100644 --- a/service-manager/src/hooks/react-query/useNotice.tsx +++ b/service-manager/src/hooks/react-query/useNotice.tsx @@ -1,14 +1,14 @@ import { getNotice, putNotice } from '../../apis/notice.apis'; import { - useQuery, useMutation, MutateOptions, useQueryClient, + useSuspenseQuery, } from '@tanstack/react-query'; import { Notice } from '../../apis/dtos/notice.dtos'; export const useNoticeQuery = () => { - const { data } = useQuery({ + const { data } = useSuspenseQuery({ queryKey: ['notice'], queryFn: getNotice, gcTime: Infinity,