From 0521c88e79d42a34f274933623bb98facda80927 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 23 May 2025 10:52:11 +0900 Subject: [PATCH 01/11] fix: reslove survey list not initializing error when enter search keywords --- src/components/survey-sale/list/ListTable.tsx | 30 +++++++++---------- .../survey-sale/list/SearchForm.tsx | 3 +- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/components/survey-sale/list/ListTable.tsx b/src/components/survey-sale/list/ListTable.tsx index 0bfeafe..8ff5eaf 100644 --- a/src/components/survey-sale/list/ListTable.tsx +++ b/src/components/survey-sale/list/ListTable.tsx @@ -27,7 +27,7 @@ export default function ListTable() { }, [pathname]) useEffect(() => { - if (!session.isLoggedIn || !('data' in surveyList)) return + if (!session.isLoggedIn || isLoadingSurveyList) return if ('count' in surveyList && surveyList.count > 0) { if (offset > 0) { setHeldSurveyList((prev) => [...prev, ...surveyList.data]) @@ -45,14 +45,12 @@ export default function ListTable() { router.push(`/survey-sale/${id}`) } - // TODO: 로딩 처리 필요 - return ( <> -
- {heldSurveyList.length > 0 ? ( -
    +
    + {heldSurveyList.length > 0 ? ( +
      {heldSurveyList.map((survey) => (
    • handleDetailClick(survey.id)}>
      @@ -67,18 +65,18 @@ export default function ListTable() {
      {new Date(survey.uptDt).toLocaleString()}
    - - ))} -
- ) : ( -
- 作成された物件はありません。 -
- )} -
- setOffset(offset + 10)} /> + + ))} + + ) : ( +
+ 作成された物件はありません。
+ )} +
+ setOffset(offset + 10)} />
+
) } diff --git a/src/components/survey-sale/list/SearchForm.tsx b/src/components/survey-sale/list/SearchForm.tsx index e0de061..82147ba 100644 --- a/src/components/survey-sale/list/SearchForm.tsx +++ b/src/components/survey-sale/list/SearchForm.tsx @@ -6,7 +6,7 @@ import { useState } from 'react' export default function SearchForm({ memberRole, userNm }: { memberRole: string; userNm: string }) { const router = useRouter() - const { setSearchOption, setSort, setIsMySurvey, setKeyword, isMySurvey, keyword, searchOption, sort } = useSurveyFilterStore() + const { setSearchOption, setSort, setIsMySurvey, setKeyword, reset, isMySurvey, keyword, searchOption, sort } = useSurveyFilterStore() const [searchKeyword, setSearchKeyword] = useState(keyword) const [option, setOption] = useState(searchOption) @@ -15,6 +15,7 @@ export default function SearchForm({ memberRole, userNm }: { memberRole: string; alert('2文字以上入力してください') return } + reset() setKeyword(searchKeyword) setSearchOption(option) } From 431512f5bf11ef696e4c178df5c00f20bee29ca0 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 23 May 2025 11:02:50 +0900 Subject: [PATCH 02/11] fix: change search by srlNo instead of id when search option is registration number --- src/app/api/survey-sales/route.ts | 17 +---------------- src/store/surveyFilterStore.ts | 4 ++-- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index 7d51802..c17e96d 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -32,6 +32,7 @@ const SEARCH_OPTIONS = [ 'POST_CODE', // 우편번호 'ADDRESS', // 주소 'ADDRESS_DETAIL', // 상세주소 + 'SRL_NO', // 등록번호 ] as const // 페이지당 항목 수 @@ -50,13 +51,6 @@ const createKeywordSearchCondition = (keyword: string, searchOption: string): Wh // 모든 필드 검색 시 OR 조건 사용 where.OR = [] - // ID가 숫자인 경우 ID 검색 조건 추가 - if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) { - where.OR.push({ - ID: { equals: Number(keyword) }, - }) - } - where.OR.push( ...SEARCH_OPTIONS.map((field) => ({ [field]: { contains: keyword }, @@ -65,15 +59,6 @@ const createKeywordSearchCondition = (keyword: string, searchOption: string): Wh } else if (SEARCH_OPTIONS.includes(searchOption.toUpperCase() as any)) { // 특정 필드 검색 where[searchOption.toUpperCase()] = { contains: keyword } - } else if (searchOption === 'id') { - // ID 검색 (숫자 변환 필요) - const number = Number(keyword) - if (!isNaN(number)) { - where.ID = { equals: number } - } else { - // 유효하지 않은 ID 검색 시 빈 결과 반환 - where.ID = { equals: null } - } } return where } diff --git a/src/store/surveyFilterStore.ts b/src/store/surveyFilterStore.ts index 95514e8..a27f2fc 100644 --- a/src/store/surveyFilterStore.ts +++ b/src/store/surveyFilterStore.ts @@ -6,7 +6,7 @@ export const SEARCH_OPTIONS = [ label: '全体', }, { - id: 'id', + id: 'srl_no', label: '登録番号', }, { @@ -41,7 +41,7 @@ export const SEARCH_OPTIONS_PARTNERS = [ label: '全体', }, { - id: 'id', + id: 'srl_no', label: '登録番号', }, { From 149be24e9aca6aaac54bb54177e448b4da0b0fc1 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 23 May 2025 11:02:50 +0900 Subject: [PATCH 03/11] fix: change search by srlNo instead of id when search option is registration number --- src/app/api/survey-sales/route.ts | 18 +----------------- src/store/surveyFilterStore.ts | 4 ++-- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index 7d51802..a1d818a 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -1,7 +1,6 @@ import { NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' import { convertToSnakeCase } from '@/utils/common-utils' -import { equal } from 'assert' /** * 검색 파라미터 */ @@ -32,6 +31,7 @@ const SEARCH_OPTIONS = [ 'POST_CODE', // 우편번호 'ADDRESS', // 주소 'ADDRESS_DETAIL', // 상세주소 + 'SRL_NO', // 등록번호 ] as const // 페이지당 항목 수 @@ -50,13 +50,6 @@ const createKeywordSearchCondition = (keyword: string, searchOption: string): Wh // 모든 필드 검색 시 OR 조건 사용 where.OR = [] - // ID가 숫자인 경우 ID 검색 조건 추가 - if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) { - where.OR.push({ - ID: { equals: Number(keyword) }, - }) - } - where.OR.push( ...SEARCH_OPTIONS.map((field) => ({ [field]: { contains: keyword }, @@ -65,15 +58,6 @@ const createKeywordSearchCondition = (keyword: string, searchOption: string): Wh } else if (SEARCH_OPTIONS.includes(searchOption.toUpperCase() as any)) { // 특정 필드 검색 where[searchOption.toUpperCase()] = { contains: keyword } - } else if (searchOption === 'id') { - // ID 검색 (숫자 변환 필요) - const number = Number(keyword) - if (!isNaN(number)) { - where.ID = { equals: number } - } else { - // 유효하지 않은 ID 검색 시 빈 결과 반환 - where.ID = { equals: null } - } } return where } diff --git a/src/store/surveyFilterStore.ts b/src/store/surveyFilterStore.ts index 95514e8..a27f2fc 100644 --- a/src/store/surveyFilterStore.ts +++ b/src/store/surveyFilterStore.ts @@ -6,7 +6,7 @@ export const SEARCH_OPTIONS = [ label: '全体', }, { - id: 'id', + id: 'srl_no', label: '登録番号', }, { @@ -41,7 +41,7 @@ export const SEARCH_OPTIONS_PARTNERS = [ label: '全体', }, { - id: 'id', + id: 'srl_no', label: '登録番号', }, { From 173401b67ddd3fd68ab56b9e161012fea0baa795 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 23 May 2025 11:15:46 +0900 Subject: [PATCH 04/11] fix: delete console.log --- src/app/api/survey-sales/[id]/route.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index db5c9df..07ecd6a 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -21,7 +21,6 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ const getNewSrlNo = async (srlNo: string, storeId: string) => { let newSrlNo = srlNo - console.log('srlNo:: ', srlNo) if (srlNo.startsWith('一時保存')) { //@ts-ignore const lastSurvey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findFirst({ @@ -128,7 +127,6 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< SRL_NO: newSrlNo, }, }) - console.log(survey) return NextResponse.json({ message: 'Survey confirmed successfully' }) } } catch (error) { From b2aefe7ea804e51aa2e8762d13e904693edfb1de Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Fri, 23 May 2025 14:04:01 +0900 Subject: [PATCH 05/11] feat: add REPRESENTATIVE_ID STORE_ID column, implement set value of storeId, representativeId for filtering survey List by member role --- src/app/api/survey-sales/route.ts | 17 +++++++++-------- src/components/survey-sale/detail/BasicForm.tsx | 4 ++++ .../survey-sale/detail/DetailForm.tsx | 2 ++ src/components/survey-sale/list/ListTable.tsx | 4 ++-- src/components/survey-sale/list/SearchForm.tsx | 6 +++--- src/hooks/useSurvey.ts | 2 +- src/types/Survey.ts | 6 ++++++ 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index a1d818a..d3160b6 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -75,7 +75,7 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { where.OR = [ { // 같은 판매점에서 작성한 제출/제출되지 않은 매물 - AND: [{ STORE: { equals: params.store } }], + AND: [{ STORE_ID: { equals: params.store } }], }, { // MUSUBI (시공권한 X) 가 ORDER 에 제출한 매물 @@ -89,7 +89,7 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { { // MUSUBI (시공권한 X) 같은 판매점에서 작성한 제출/제출되지 않은 매물 AND: [ - { STORE: { equals: params.store } }, + { STORE_ID: { equals: params.store } }, { OR: [{ CONSTRUCTION_POINT: { equals: null } }, { CONSTRUCTION_POINT: { equals: '' } }], }, @@ -109,9 +109,10 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { case 'Builder': // MUSUBI (시공권한 O) case 'Partner': // PARTNER - // 같은 시공ID에서 작성된 매물 + // 시공점이 있고 STORE_ID가 시공ID와 같은 매물 where.AND?.push({ - CONSTRUCTION_POINT: { equals: params.builderNo }, + CONSTRUCTION_POINT: { not: null }, + STORE_ID: { equals: params.builderNo }, }) break @@ -125,7 +126,7 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => { }, }, { - STORE: { + STORE_ID: { equals: params.store, }, }, @@ -149,11 +150,11 @@ export async function GET(request: Request) { const params: SearchParams = { keyword: searchParams.get('keyword'), searchOption: searchParams.get('searchOption'), - isMySurvey: searchParams.get('isMySurvey'), + isMySurvey: searchParams.get('isMySurvey'), //representativeId sort: searchParams.get('sort'), offset: searchParams.get('offset'), role: searchParams.get('role'), - store: searchParams.get('store'), + store: searchParams.get('store'), //storeId builderNo: searchParams.get('builderNo'), } @@ -162,7 +163,7 @@ export async function GET(request: Request) { // 내가 작성한 매물 조건 적용 if (params.isMySurvey) { - where.AND.push({ REPRESENTATIVE: params.isMySurvey }) + where.AND.push({ REPRESENTATIVE_ID: params.isMySurvey }) } // 키워드 검색 조건 적용 diff --git a/src/components/survey-sale/detail/BasicForm.tsx b/src/components/survey-sale/detail/BasicForm.tsx index b984101..52a541f 100644 --- a/src/components/survey-sale/detail/BasicForm.tsx +++ b/src/components/survey-sale/detail/BasicForm.tsx @@ -20,12 +20,16 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas setBasicInfoSelected() }, []) + // 시공권한 user(Builder), Partner 계정은 조사매물 등록 할 때 STORE_ID에 시공점ID가 들어감 + // 권한 별 목록 필터링 시 시공권한 user(Builder), Partner는 시공점ID가 같은 것들만 조회 useEffect(() => { if (session?.isLoggedIn) { setBasicInfo({ ...basicInfo, representative: session.userNm ?? '', + representativeId: session.userId ?? null, store: session.role === 'Partner' ? null : session.storeNm ?? null, + storeId: session.role === 'Partner' || session.role === 'Builder' ? session.builderNo : session.storeId ?? null, constructionPoint: session.builderNo ?? null, }) } diff --git a/src/components/survey-sale/detail/DetailForm.tsx b/src/components/survey-sale/detail/DetailForm.tsx index 2827cf8..340b13d 100644 --- a/src/components/survey-sale/detail/DetailForm.tsx +++ b/src/components/survey-sale/detail/DetailForm.tsx @@ -48,7 +48,9 @@ const roofInfoForm: SurveyDetailRequest = { const basicInfoForm: SurveyBasicRequest = { representative: '', + representativeId: null, store: null, + storeId: null, constructionPoint: null, investigationDate: new Date().toLocaleDateString('en-CA'), buildingName: null, diff --git a/src/components/survey-sale/list/ListTable.tsx b/src/components/survey-sale/list/ListTable.tsx index 8ff5eaf..38e96a9 100644 --- a/src/components/survey-sale/list/ListTable.tsx +++ b/src/components/survey-sale/list/ListTable.tsx @@ -2,7 +2,7 @@ import LoadMoreButton from '@/components/LoadMoreButton' import { useServey } from '@/hooks/useSurvey' -import { useEffect, useState, useMemo, useRef } from 'react' +import { useEffect, useState } from 'react' import { useRouter, usePathname } from 'next/navigation' import SearchForm from './SearchForm' import { useSurveyFilterStore } from '@/store/surveyFilterStore' @@ -47,7 +47,7 @@ export default function ListTable() { return ( <> - +
{heldSurveyList.length > 0 ? (
    diff --git a/src/components/survey-sale/list/SearchForm.tsx b/src/components/survey-sale/list/SearchForm.tsx index 82147ba..f5bb9d8 100644 --- a/src/components/survey-sale/list/SearchForm.tsx +++ b/src/components/survey-sale/list/SearchForm.tsx @@ -4,7 +4,7 @@ import { SEARCH_OPTIONS, SEARCH_OPTIONS_ENUM, SEARCH_OPTIONS_PARTNERS, useSurvey import { useRouter } from 'next/navigation' import { useState } from 'react' -export default function SearchForm({ memberRole, userNm }: { memberRole: string; userNm: string }) { +export default function SearchForm({ memberRole, userId }: { memberRole: string; userId: string }) { const router = useRouter() const { setSearchOption, setSort, setIsMySurvey, setKeyword, reset, isMySurvey, keyword, searchOption, sort } = useSurveyFilterStore() const [searchKeyword, setSearchKeyword] = useState(keyword) @@ -76,9 +76,9 @@ export default function SearchForm({ memberRole, userNm }: { memberRole: string; { - setIsMySurvey(isMySurvey === userNm ? null : userNm) + setIsMySurvey(isMySurvey === userId ? null : userId) }} /> diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 6610cea..0f897ff 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -90,7 +90,7 @@ export function useServey(id?: number): { isMySurvey, sort, offset, - store: session?.storeNm, + store: session?.storeId, builderNo: session?.builderNo, role: session?.role, }, diff --git a/src/types/Survey.ts b/src/types/Survey.ts index 8066836..cd2b7d0 100644 --- a/src/types/Survey.ts +++ b/src/types/Survey.ts @@ -1,7 +1,9 @@ export type SurveyBasicInfo = { id: number representative: string + representativeId: string | null store: string | null + storeId: string | null constructionPoint: string | null investigationDate: string | null buildingName: string | null @@ -62,7 +64,9 @@ export type SurveyDetailInfo = { export type SurveyBasicRequest = { representative: string + representativeId: string | null store: string | null + storeId: string | null constructionPoint: string | null investigationDate: string | null buildingName: string | null @@ -120,7 +124,9 @@ export type SurveyDetailCoverRequest = { export type SurveyRegistRequest = { representative: string + representativeId: string | null store: string | null + storeId: string | null constructionPoint: string | null investigationDate: string | null buildingName: string | null From 0fbb8025f203fb645a4802bb56e51e4c411b3355 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Mon, 26 May 2025 10:58:25 +0900 Subject: [PATCH 06/11] fix: change SRL_NO save rules --- src/app/api/survey-sales/[id]/route.ts | 12 ++++++---- src/app/api/survey-sales/route.ts | 18 +++++++++++---- .../survey-sale/detail/ButtonForm.tsx | 6 ++--- .../survey-sale/detail/DetailForm.tsx | 4 ++-- src/hooks/useSurvey.ts | 23 +++++++------------ 5 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index 07ecd6a..7c03530 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -19,14 +19,16 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{ } } -const getNewSrlNo = async (srlNo: string, storeId: string) => { +const getNewSrlNo = async (srlNo: string, storeId: string, role: string) => { + const srlRole = role === 'T01' || role === 'Admin' ? 'HO' : role === 'Admin_Sub' || role === 'Builder' ? 'HM' : '' + let newSrlNo = srlNo if (srlNo.startsWith('一時保存')) { //@ts-ignore const lastSurvey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findFirst({ where: { SRL_NO: { - startsWith: storeId, + startsWith: srlRole + storeId, }, }, orderBy: { @@ -34,7 +36,9 @@ const getNewSrlNo = async (srlNo: string, storeId: string) => { }, }) const lastNo = lastSurvey ? parseInt(lastSurvey.SRL_NO.slice(-3)) : 0 + newSrlNo = + srlRole + storeId + new Date().getFullYear().toString().slice(-2) + (new Date().getMonth() + 1).toString().padStart(2, '0') + @@ -51,7 +55,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{ const { detailInfo, ...basicInfo } = body.survey // PUT 요청 시 임시저장 여부 확인 후 임시저장 시 기존 SRL_NO 사용, 기본 저장 시 새로운 SRL_NO 생성 - const newSrlNo = body.isTemporary ? body.survey.srlNo : await getNewSrlNo(body.survey.srlNo, body.storeId) + const newSrlNo = body.isTemporary ? body.survey.srlNo : await getNewSrlNo(body.survey.srlNo, body.storeId, body.role) // @ts-ignore const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({ where: { ID: Number(id) }, @@ -113,7 +117,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< const body = await request.json() // 제출 시 기존 SRL_NO 확인 후 '임시저장'으로 시작하면 새로운 SRL_NO 생성 - const newSrlNo = await getNewSrlNo(body.srlNo, body.storeId) + const newSrlNo = await getNewSrlNo(body.srlNo, body.storeId, body.role) if (body.targetId) { // @ts-ignore diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index d3160b6..a5d60c8 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -227,11 +227,21 @@ export async function POST(request: Request) { try { const body = await request.json() - // 임시 저장 시 임시저장 + 000 으로 저장 - // 기본 저장 시 판매점ID + yyMMdd + 000 으로 저장 + const role = + body.role === 'T01' || body.role === 'Admin' + ? 'HO' + : body.role === 'Admin_Sub' || body.role === 'Builder' + ? 'HM' + : body.role === 'Partner' + ? '' + : null + + // 임시 저장 시 임시저장으로 저장 + // 기본 저장 시 (HO/HM) + 판매점ID + yyMMdd + 000 으로 저장 const baseSrlNo = body.survey.srlNo ?? - body.storeId + + role + + body.storeId + new Date().getFullYear().toString().slice(-2) + (new Date().getMonth() + 1).toString().padStart(2, '0') + new Date().getDate().toString().padStart(2, '0') @@ -240,7 +250,7 @@ export async function POST(request: Request) { const lastSurvey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findFirst({ where: { SRL_NO: { - startsWith: body.storeId, + startsWith: role + body.storeId, }, }, orderBy: { diff --git a/src/components/survey-sale/detail/ButtonForm.tsx b/src/components/survey-sale/detail/ButtonForm.tsx index 8f8e3dd..fd3e339 100644 --- a/src/components/survey-sale/detail/ButtonForm.tsx +++ b/src/components/survey-sale/detail/ButtonForm.tsx @@ -29,7 +29,7 @@ export default function ButtonForm(props: { }) // !!!!!!!!!! - const [tempTargetId, setTempTargetId] = useState('') + const [tempTargetId, setTempTargetId] = useState('TEST') // -------------------------------------------------------------- // 권한 @@ -158,6 +158,7 @@ export default function ButtonForm(props: { if (routeId) { window.neoConfirm('削除しますか?', async () => { await deleteSurvey() + alert('削除されました。') router.push('/survey-sale') }) } @@ -269,9 +270,6 @@ function SubmitButton(props: { handleSubmit: () => void; setTempTargetId: (targe 提出
-
- setTempTargetId(e.target.value)} /> -
) } diff --git a/src/components/survey-sale/detail/DetailForm.tsx b/src/components/survey-sale/detail/DetailForm.tsx index 340b13d..7204135 100644 --- a/src/components/survey-sale/detail/DetailForm.tsx +++ b/src/components/survey-sale/detail/DetailForm.tsx @@ -1,11 +1,11 @@ 'use client' -import type { Mode, SurveyBasicInfo, SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey' +import type { Mode, SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey' import { useEffect, useState } from 'react' import ButtonForm from './ButtonForm' import BasicForm from './BasicForm' import RoofForm from './RoofForm' -import { useParams, useRouter, useSearchParams } from 'next/navigation' +import { useParams, useSearchParams } from 'next/navigation' import { useServey } from '@/hooks/useSurvey' const roofInfoForm: SurveyDetailRequest = { diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index 0f897ff..7149828 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -1,4 +1,4 @@ -import type { SurveyBasicInfo, SurveyDetailInfo, SurveyDetailRequest, SurveyDetailCoverRequest, SurveyRegistRequest } from '@/types/Survey' +import type { SurveyBasicInfo, SurveyDetailRequest, SurveyRegistRequest } from '@/types/Survey' import { useMemo } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useSurveyFilterStore } from '@/store/surveyFilterStore' @@ -63,7 +63,6 @@ export function useServey(id?: number): { isUpdatingSurvey: boolean isDeletingSurvey: boolean createSurvey: (survey: SurveyRegistRequest) => Promise - createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => void updateSurvey: ({ survey, isTemporary, storeId }: { survey: SurveyRegistRequest; isTemporary: boolean; storeId?: string }) => void deleteSurvey: () => Promise submitSurvey: (params: { saveId?: number; targetId?: string; storeId?: string; srlNo?: string }) => void @@ -119,7 +118,11 @@ export function useServey(id?: number): { const { mutateAsync: createSurvey, isPending: isCreatingSurvey } = useMutation({ mutationFn: async (survey: SurveyRegistRequest) => { - const resp = await axiosInstance(null).post('/api/survey-sales', { survey: survey, storeId: session?.storeId ?? null }) + const resp = await axiosInstance(null).post('/api/survey-sales', { + survey: survey, + storeId: session?.storeId ?? null, + role: session?.role ?? null, + }) return resp.data.id ?? 0 }, onSuccess: (data) => { @@ -137,6 +140,7 @@ export function useServey(id?: number): { survey: survey, isTemporary: isTemporary, storeId: storeId, + role: session?.role ?? null, }) return resp.data }, @@ -158,17 +162,6 @@ export function useServey(id?: number): { }, }) - const { mutateAsync: createSurveyDetail } = useMutation({ - mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => { - const resp = await axiosInstance(null).patch(`/api/survey-sales/${surveyId}`, surveyDetail) - return resp.data - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['survey', 'list'] }) - queryClient.invalidateQueries({ queryKey: ['survey', id] }) - }, - }) - const { mutateAsync: submitSurvey } = useMutation({ mutationFn: async ({ saveId, targetId, storeId, srlNo }: { saveId?: number; targetId?: string; storeId?: string; srlNo?: string }) => { const submitId = saveId ?? id @@ -177,6 +170,7 @@ export function useServey(id?: number): { targetId, storeId, srlNo, + role: session?.role ?? null, }) return resp.data }, @@ -240,7 +234,6 @@ export function useServey(id?: number): { createSurvey, updateSurvey, deleteSurvey, - createSurveyDetail, submitSurvey, validateSurveyDetail, getZipCode, From 35b1002908fb7d12632b44e65f2d0d4d77381850 Mon Sep 17 00:00:00 2001 From: keyy1315 Date: Tue, 27 May 2025 09:08:45 +0900 Subject: [PATCH 07/11] feat: add submit popup page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 제출 팝업 페이지 추가 - 조사매물 작성 시 숫자 입력 항목 모바일에서 숫자 키패드만 나오도록 설정 - 제출 필수값 validation 구현 --- .env.development | 2 +- .env.localhost | 2 +- src/app/api/survey-sales/[id]/route.ts | 6 +- .../popup/SurveySaleSubmitPopup.tsx | 151 ++++++++++++++++++ src/components/popup/ZipCodePopup.tsx | 1 - .../survey-sale/detail/BasicForm.tsx | 14 +- .../survey-sale/detail/ButtonForm.tsx | 84 ++++------ .../survey-sale/detail/DataTable.tsx | 5 +- .../survey-sale/detail/RoofForm.tsx | 24 +-- src/components/ui/PopupController.tsx | 4 +- src/hooks/useSurvey.ts | 12 +- src/store/popupController.ts | 5 + 12 files changed, 225 insertions(+), 85 deletions(-) create mode 100644 src/components/popup/SurveySaleSubmitPopup.tsx diff --git a/.env.development b/.env.development index 47cfa78..f9cbda5 100644 --- a/.env.development +++ b/.env.development @@ -2,7 +2,7 @@ NEXT_PUBLIC_RUN_MODE=development # 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경 # 다시 로컬에서 개발할때는 localhost로 변경 #route handler -NEXT_PUBLIC_API_URL=http://localhost:3000 +NEXT_PUBLIC_API_URL=http://172.30.1.65:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 diff --git a/.env.localhost b/.env.localhost index 966e366..944ee6f 100644 --- a/.env.localhost +++ b/.env.localhost @@ -2,7 +2,7 @@ NEXT_PUBLIC_RUN_MODE=local # 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경 # 다시 로컬에서 개발할때는 localhost로 변경 #route handler -NEXT_PUBLIC_API_URL=http://localhost:3000 +NEXT_PUBLIC_API_URL=http://172.30.1.65:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 diff --git a/src/app/api/survey-sales/[id]/route.ts b/src/app/api/survey-sales/[id]/route.ts index 7c03530..eb718f4 100644 --- a/src/app/api/survey-sales/[id]/route.ts +++ b/src/app/api/survey-sales/[id]/route.ts @@ -116,9 +116,6 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< const { id } = await params const body = await request.json() - // 제출 시 기존 SRL_NO 확인 후 '임시저장'으로 시작하면 새로운 SRL_NO 생성 - const newSrlNo = await getNewSrlNo(body.srlNo, body.storeId, body.role) - if (body.targetId) { // @ts-ignore const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({ @@ -128,10 +125,9 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise< SUBMISSION_DATE: new Date(), SUBMISSION_TARGET_ID: body.targetId, UPT_DT: new Date(), - SRL_NO: newSrlNo, }, }) - return NextResponse.json({ message: 'Survey confirmed successfully' }) + return NextResponse.json({ message: 'Survey confirmed successfully', data: survey }) } } catch (error) { console.error('Error updating survey:', error) diff --git a/src/components/popup/SurveySaleSubmitPopup.tsx b/src/components/popup/SurveySaleSubmitPopup.tsx new file mode 100644 index 0000000..1d61da1 --- /dev/null +++ b/src/components/popup/SurveySaleSubmitPopup.tsx @@ -0,0 +1,151 @@ +import Image from 'next/image' +import { usePopupController } from '@/store/popupController' +import { useParams } from 'next/navigation' +import { useServey } from '@/hooks/useSurvey' +import { useState } from 'react' +import { useSessionStore } from '@/store/session' + +interface SubmitFormData { + store: string + sender: string + receiver: string + reference: string + title: string + contents: string +} + +interface FormField { + id: keyof SubmitFormData + name: string + required: boolean +} + +const FORM_FIELDS: FormField[] = [ + { id: 'store', name: '提出販売店', required: true }, + { id: 'sender', name: '発送者', required: true }, + { id: 'receiver', name: '受信者', required: true }, + { id: 'reference', name: '参考', required: false }, + { id: 'title', name: 'タイトル', required: true }, + { id: 'contents', name: '内容', required: true }, +] + +export default function SurveySaleSubmitPopup() { + const popupController = usePopupController() + const { session } = useSessionStore() + const params = useParams() + const routeId = params.id + + const [submitData, setSubmitData] = useState({ + store: '', + sender: session?.email ?? '', + receiver: '', + reference: '', + title: '[HANASYS現地調査] 調査物件が提出.', + contents: '', + }) + + const { submitSurvey, isSubmittingSurvey } = useServey(Number(routeId)) + + const handleInputChange = (field: keyof SubmitFormData, value: string) => { + setSubmitData((prev) => ({ ...prev, [field]: value })) + } + + const validateData = (data: SubmitFormData): boolean => { + const requiredFields = FORM_FIELDS.filter((field) => field.required) + + for (const field of requiredFields) { + if (!data[field.id].trim()) { + const element = document.getElementById(field.id) + if (element) { + element.focus() + } + alert(`${field.name}は必須入力項目です。`) + return false + } + } + + return true + } + + const handleSubmit = () => { + if (validateData(submitData)) { + window.neoConfirm('送信しますか? 送信後は変更・修正することはできません。', () => { + submitSurvey({ targetId: submitData.store }) + if (!isSubmittingSurvey) { + popupController.setSurveySaleSubmitPopup(false) + } + }) + } + } + + const handleClose = () => { + popupController.setSurveySaleSubmitPopup(false) + } + + const renderFormField = (field: FormField) => { + // const isReadOnly = (field.id === 'store' && session?.role !== 'Partner') || (field.id === 'receiver' && session?.role !== 'Partner') + const isReadOnly = false + + return ( +
+
+ {field.name} {field.required && *} +
+
+ {field.id === 'contents' ? ( +