From 042096c68228a8dee86c93eaf0b46b014e55a499 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 13 Jun 2025 13:17:37 +0900 Subject: [PATCH 1/6] refactor: add an error alert conditional --- src/hooks/useInquiry.ts | 4 +++- src/hooks/useSurvey.ts | 14 ++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/hooks/useInquiry.ts b/src/hooks/useInquiry.ts index cd32c87..707b0e0 100644 --- a/src/hooks/useInquiry.ts +++ b/src/hooks/useInquiry.ts @@ -48,7 +48,9 @@ export function useInquiry( */ const errorRouter = (error: any) => { const status = error.response?.status - alert(error.response?.data.error) + if (error.response?.data.error) { + alert(error.response?.data.error) + } switch (status) { // session 없는 경우 case 401: diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 84f6cfa..bee304a 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -108,7 +108,9 @@ export function useSurvey( */ const errorRouter = (error: any) => { const status = error.response?.status - alert(error.response?.data.error) + if (error.response?.data.error) { + alert(error.response?.data.error) + } switch (status) { /** session 없는 경우 */ case 401: @@ -259,6 +261,9 @@ export function useSurvey( queryClient.invalidateQueries({ queryKey: ['survey', id] }) queryClient.invalidateQueries({ queryKey: ['survey', 'list'] }) }, + onError: (error: any) => { + alert(error.response?.data.error) + }, }) /** @@ -269,7 +274,7 @@ export function useSurvey( * * @example * - * + * */ const { mutateAsync: deleteSurvey, isPending: isDeletingSurvey } = useMutation({ mutationFn: async () => { @@ -370,8 +375,9 @@ export function useSurvey( `https://zipcloud.ibsnet.co.jp/api/search?${queryStringFormatter({ zipcode: zipCode.trim() })}`, ) return data.results - } catch (e) { - console.error('Failed to fetch zipcode data:', e) + } catch (error: any) { + console.error('Failed to fetch zipcode data:', error) + alert(error.response?.data.error) throw new Error('Failed to fetch zipcode data') } } From 5d5ba2e82a051d7d686db38a1dd5be8fcd66576e Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Mon, 16 Jun 2025 17:31:47 +0900 Subject: [PATCH 2/6] feat: add api logger to survey-sale api --- src/app/api/survey-sales/[id]/route.ts | 23 +++++++++++++++-------- src/app/api/survey-sales/route.ts | 11 ++++++++--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index aa8d2a5..c1f497c 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -6,6 +6,7 @@ import { sessionOptions } from '@/libs/session' import { cookies } from 'next/headers' import type { SessionData } from '@/types/Auth' import { Prisma } from '@prisma/client' +import { loggerWrapper } from '@/libs/api-wrapper' /** * @description 조사 매물 조회 에러 메시지 @@ -128,11 +129,11 @@ const fetchSurvey = async (id: number) => { * ... * } */ -export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { +export async function getSurveySaleDetail(request: NextRequest): Promise { try { const cookieStore = await cookies() const session = await getIronSession(cookieStore, sessionOptions) - const { id } = await params + const id = request.nextUrl.pathname.split('/').pop() ?? '' const { searchParams } = new URL(request.url) const isPdf = searchParams.get('isPdf') === 'true' @@ -228,9 +229,9 @@ const getNewSrlNo = async (srlNo: string, storeId: string, role: string) => { * ... * } * */ -export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { +export async function updateSurveySaleDetail(request: NextRequest): Promise { try { - const { id } = await params + const id = request.nextUrl.pathname.split('/').pop() ?? '' const body = await request.json() const { detailInfo, ...basicInfo } = body.survey @@ -257,6 +258,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 }) } } + /** * @api {DELETE} /api/survey-sales/:id 조사 매물 삭제 API * @apiName DELETE /api/survey-sales/:id @@ -275,9 +277,9 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ * { * "message": "success" */ -export async function DELETE(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { +export async function deleteSurveySaleDetail(request: NextRequest): Promise { try { - const { id } = await params + const id = request.nextUrl.pathname.split('/').pop() ?? '' await prisma.$transaction(async (tx: Prisma.TransactionClient) => { // @ts-ignore @@ -343,9 +345,9 @@ export async function DELETE(request: NextRequest, { params }: { params: Promise * } * } */ -export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { +export async function submitSurveySaleDetail(request: NextRequest): Promise { try { - const { id } = await params + const id = request.nextUrl.pathname.split('/').pop() ?? '' const body = await request.json() // @ts-ignore const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({ @@ -364,3 +366,8 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 }) } } + +export const GET = loggerWrapper(getSurveySaleDetail) +export const PUT = loggerWrapper(updateSurveySaleDetail) +export const DELETE = loggerWrapper(deleteSurveySaleDetail) +export const PATCH = loggerWrapper(submitSurveySaleDetail) diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index c257b05..6f3ebd3 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -1,6 +1,7 @@ import { NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' import { convertToSnakeCase } from '@/utils/common-utils' +import { loggerWrapper } from '@/libs/api-wrapper' /** * @description 검색 파라미터 타입 */ @@ -192,7 +193,7 @@ const checkSession = (params: SearchParams) => { * } * */ -export async function GET(request: Request) { +export async function getSurveySales(request: Request) { try { /** URL 파라미터 파싱 */ const { searchParams } = new URL(request.url) @@ -273,7 +274,7 @@ export async function GET(request: Request) { * * @apiError {Number} 500 서버 오류 */ -export async function PUT(request: Request) { +export async function updateSurveySales(request: Request) { try { /** 요청 바디 파싱 */ const body = await request.json() @@ -335,7 +336,7 @@ export async function PUT(request: Request) { * * @apiError {Number} 500 서버 오류 */ -export async function POST(request: Request) { +export async function createSurveySales(request: Request) { try { const body = await request.json() @@ -393,3 +394,7 @@ export async function POST(request: Request) { return NextResponse.json({ error: 'データ保存に失敗しました。' }, { status: 500 }) } } + +export const GET = loggerWrapper(getSurveySales) +export const PUT = loggerWrapper(updateSurveySales) +export const POST = loggerWrapper(createSurveySales) \ No newline at end of file From 19a11783d6593baa493c04fb3e75ce464c755194 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Mon, 16 Jun 2025 17:38:34 +0900 Subject: [PATCH 3/6] fix: fix type error --- src/app/api/survey-sales/[id]/route.ts | 8 ++++---- src/app/api/survey-sales/route.ts | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index c1f497c..c1a76bc 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -129,7 +129,7 @@ const fetchSurvey = async (id: number) => { * ... * } */ -export async function getSurveySaleDetail(request: NextRequest): Promise { +async function getSurveySaleDetail(request: NextRequest): Promise { try { const cookieStore = await cookies() const session = await getIronSession(cookieStore, sessionOptions) @@ -229,7 +229,7 @@ const getNewSrlNo = async (srlNo: string, storeId: string, role: string) => { * ... * } * */ -export async function updateSurveySaleDetail(request: NextRequest): Promise { +async function updateSurveySaleDetail(request: NextRequest): Promise { try { const id = request.nextUrl.pathname.split('/').pop() ?? '' const body = await request.json() @@ -277,7 +277,7 @@ export async function updateSurveySaleDetail(request: NextRequest): Promise { +async function deleteSurveySaleDetail(request: NextRequest): Promise { try { const id = request.nextUrl.pathname.split('/').pop() ?? '' @@ -345,7 +345,7 @@ export async function deleteSurveySaleDetail(request: NextRequest): Promise { +async function submitSurveySaleDetail(request: NextRequest): Promise { try { const id = request.nextUrl.pathname.split('/').pop() ?? '' const body = await request.json() diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index 6f3ebd3..1f2cfdb 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -193,7 +193,7 @@ const checkSession = (params: SearchParams) => { * } * */ -export async function getSurveySales(request: Request) { +async function getSurveySales(request: Request) { try { /** URL 파라미터 파싱 */ const { searchParams } = new URL(request.url) @@ -274,7 +274,7 @@ export async function getSurveySales(request: Request) { * * @apiError {Number} 500 서버 오류 */ -export async function updateSurveySales(request: Request) { +async function updateSurveySales(request: Request) { try { /** 요청 바디 파싱 */ const body = await request.json() @@ -336,7 +336,7 @@ export async function updateSurveySales(request: Request) { * * @apiError {Number} 500 서버 오류 */ -export async function createSurveySales(request: Request) { +async function createSurveySales(request: Request) { try { const body = await request.json() From cd28667f3ccbe016a4db95b46ba3b98f77efc695 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Mon, 16 Jun 2025 17:59:03 +0900 Subject: [PATCH 4/6] feat: add route error handling for invalid URL access in DetailForm component --- src/components/survey-sale/detail/DetailForm.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/components/survey-sale/detail/DetailForm.tsx b/src/components/survey-sale/detail/DetailForm.tsx index 06511c2..313a650 100644 --- a/src/components/survey-sale/detail/DetailForm.tsx +++ b/src/components/survey-sale/detail/DetailForm.tsx @@ -94,6 +94,14 @@ export default function DetailForm() { })) const [roofInfoData, setRoofInfoData] = useState(roofInfoForm) + /** route 에러 처리 - 잘못된 URL 접근 시 생성 페이지로 리다이렉트 */ + useEffect(() => { + if (modeset === 'CREATE' && pathname !== '/survey-sale/regist') { + router.replace('/survey-sale/regist') + } + return + }, [modeset, id, pathname]) + /** 제출 팝업 처리 - createSurvey 이후 popup 처리 시 노드 삽입 오류로 인해 별도 처리 */ useEffect(() => { const show = searchParams.get('show') From e806358d0eea8ce12e068a4a55c2735c90bc5ec9 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Tue, 17 Jun 2025 11:01:03 +0900 Subject: [PATCH 5/6] fix: update route at useTitle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - useTitle 헤더 제목, url 변경 - 임시저장 로직 수정 --- src/components/survey-sale/detail/ButtonForm.tsx | 3 +-- src/hooks/useTitle.ts | 8 +++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/components/survey-sale/detail/ButtonForm.tsx b/src/components/survey-sale/detail/ButtonForm.tsx index b7d1e5d..c806139 100644 --- a/src/components/survey-sale/detail/ButtonForm.tsx +++ b/src/components/survey-sale/detail/ButtonForm.tsx @@ -90,10 +90,9 @@ export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) { /** 저장 로직 */ const handleSave = (isTemporary: boolean, isSubmitProcess: boolean) => { const emptyField = validateSurveyDetail(data.roof) - const hasEmptyField = emptyField?.trim() !== '' if (isTemporary) { - hasEmptyField ? tempSaveProcess() : saveProcess(emptyField, false) + tempSaveProcess() } else { saveProcess(emptyField, isSubmitProcess) } diff --git a/src/hooks/useTitle.ts b/src/hooks/useTitle.ts index d2f2aa0..f738e7f 100644 --- a/src/hooks/useTitle.ts +++ b/src/hooks/useTitle.ts @@ -1,8 +1,8 @@ export const useTitle = () => { const getTitle = (pathname: string) => { // Handle dynamic routes first - if (pathname.startsWith('/survey-sale/') && pathname !== '/survey-sale/basic-info' && pathname !== '/survey-sale/roof-info') { - return '調査物件一覧' + if (pathname.startsWith('/survey-sale/') && pathname !== '/survey-sale/regist') { + return '調査物件詳細' } if (pathname.startsWith('/inquiry/') && pathname !== '/inquiry/list' && pathname !== '/inquiry/regist') { @@ -17,10 +17,8 @@ export const useTitle = () => { return '屋根材適合性の確認' case '/survey-sale': return '調査物件一覧' - case '/survey-sale/basic-info': + case '/survey-sale/regist': return '調査物件登録' - case '/survey-sale/roof-info': - return '調査物件新規登録' case '/inquiry/list': return '1:1お問い合わせ' case '/inquiry/regist': From 17d306bb1198a3bb2d39034c4d9bbb0c403dc54c Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Tue, 17 Jun 2025 13:20:18 +0900 Subject: [PATCH 6/6] fix: change submit data set logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 제출 폼 데이터 삽입 안되는 오류 해결 - api error 메세지 설정 - api 응답 객체 변경 --- src/app/api/submission/admin-sub/route.ts | 14 ++---- src/app/api/submission/admin/route.ts | 7 ++- src/app/api/submission/builder/route.ts | 12 +---- src/app/api/submission/super/route.ts | 9 ++-- .../popup/SurveySaleSubmitPopup.tsx | 44 ++++++++++++------- 5 files changed, 39 insertions(+), 47 deletions(-) diff --git a/src/app/api/submission/admin-sub/route.ts b/src/app/api/submission/admin-sub/route.ts index 28e3627..345a3ff 100644 --- a/src/app/api/submission/admin-sub/route.ts +++ b/src/app/api/submission/admin-sub/route.ts @@ -1,13 +1,8 @@ import { prisma } from '@/libs/prisma' import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' +import { SubmitTargetResponse } from '@/types/Survey' -type AdminSubPerson = { - storeId: string - userId: string - eMail: string - authority: string -} // 2차점이 자신에게 매핑 된 1차 판매점과 관리자 정보 조회 async function getSubMissionAdminSub(request: NextRequest): Promise { try { @@ -33,12 +28,11 @@ async function getSubMissionAdminSub(request: NextRequest): Promise { ; CLOSE SYMMETRIC KEY SYMMETRICKEY; ` - const suitable: SuperPerson[] = await prisma.$queryRawUnsafe(query) - - return NextResponse.json({ message: 'Hello, world!' }) + const data: SuperPerson[] = await prisma.$queryRawUnsafe(query) + return NextResponse.json(data) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) + return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }) } } diff --git a/src/app/api/submission/builder/route.ts b/src/app/api/submission/builder/route.ts index f1f0f93..cf43b08 100644 --- a/src/app/api/submission/builder/route.ts +++ b/src/app/api/submission/builder/route.ts @@ -3,13 +3,6 @@ import { SubmitTargetResponse } from '@/types/Survey' import { NextRequest, NextResponse } from 'next/server' import { loggerWrapper } from '@/libs/api-wrapper' -type BuilderPerson = { - agencyStoreId: string - userId: string - eMail: string - userAuthCd: string -} - // 2차점의 시공권한 user가 해당 판매점의 관리자 정보 조회 // N == 일반유저, S == 수퍼유저, B == 시공권한유저 async function getSubmissionBuilder(request: NextRequest): Promise { @@ -37,14 +30,11 @@ async function getSubmissionBuilder(request: NextRequest): Promise AND MCAS.DEL_YN = 'N'; CLOSE SYMMETRIC KEY SYMMETRICKEY; ` - // const suitable: BuilderPerson[] = await prisma.$queryRawUnsafe(query) - - // return NextResponse.json({ message: 'Hello, world!' }) const data: SubmitTargetResponse[] = await prisma.$queryRawUnsafe(query) return NextResponse.json(data) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) + return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }) } } diff --git a/src/app/api/submission/super/route.ts b/src/app/api/submission/super/route.ts index faa66cf..c950dfe 100644 --- a/src/app/api/submission/super/route.ts +++ b/src/app/api/submission/super/route.ts @@ -30,12 +30,11 @@ async function getSubmissionSuper(request: NextRequest): Promise { AND MCSA.DEL_YN = 'N'; CLOSE SYMMETRIC KEY SYMMETRICKEY; ` - const suitable: SuperPerson[] = await prisma.$queryRawUnsafe(query) - - return NextResponse.json({ message: 'Hello, world!' }) + const data: SuperPerson[] = await prisma.$queryRawUnsafe(query) + return NextResponse.json(data) } catch (error) { - console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) - return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) + console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error); + return NextResponse.json({ error: 'データの取得に失敗しました。' }, { status: 500 }); } } diff --git a/src/components/popup/SurveySaleSubmitPopup.tsx b/src/components/popup/SurveySaleSubmitPopup.tsx index 45bc7b1..ca8ced0 100644 --- a/src/components/popup/SurveySaleSubmitPopup.tsx +++ b/src/components/popup/SurveySaleSubmitPopup.tsx @@ -48,34 +48,43 @@ export default function SurveySaleSubmitPopup() { }) const [commCodeList, setCommCodeList] = useState([]) + /** 제출 타겟 데이터 조회 및 제출 폼 데이터 삽입 */ useEffect(() => { if (!session?.isLoggedIn || !surveyDetail?.id) return + const baseUpdate = { + sender: session?.email ?? '', + title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')', + } + if (session?.role === 'Admin') { getCommCode('SALES_OFFICE_CD').then((codes) => { setCommCodeList(codes) }) + setSubmitData((prev) => ({ + ...prev, + ...baseUpdate, + })) } else if (session?.role === 'Builder' || session?.role === 'Admin_Sub') { getSubmitTarget({ storeId: surveyDetail?.storeId ?? '', role: session?.role ?? '' }).then((data) => { - if (data) { - setSubmitData({ - ...submitData, - targetId: data[0].targetStoreId, - targetNm: data[0].targetStoreNm, - }) - data.length > 1 && - setSubmitData({ - ...submitData, - receiver: data.filter((item) => item.auth === 'S').map((item) => item.repUserEmail), - reference: data.filter((item) => item.auth === 'N').map((item) => item.repUserEmail), - }) + if (!data) return + if (data && data.length > 0) { + const updateData: Partial = { + sender: session?.email ?? '', + title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')', + targetId: data[0]?.targetStoreId ?? '', + targetNm: data[0]?.targetStoreNm ?? '', + receiver: data.filter((item) => item.auth === 'S').map((item) => item.repUserEmail), + reference: data.filter((item) => item.auth === 'N').map((item) => item.repUserEmail), + saleBase: null, + contents: '', + } + setSubmitData((prev) => ({ + ...prev, + ...updateData, + })) } }) } - setSubmitData({ - ...submitData, - sender: session?.email ?? '', - title: '[HANASYS現地調査] 調査物件が提出. (' + surveyDetail?.srlNo + ')', - }) }, [session, surveyDetail]) const FORM_FIELDS: FormField[] = [ @@ -118,6 +127,7 @@ export default function SurveySaleSubmitPopup() { setIsShow(true) sendEmail({ to: submitData.receiver, + cc: submitData.reference ?? '', subject: submitData.title, content: contentsRef.current?.innerHTML ?? '', })