diff --git a/.env.development b/.env.development index d4d4309..2034c56 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://172.30.1.65:3000 +NEXT_PUBLIC_API_URL=http://172.30.1.23:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 @@ -20,7 +20,7 @@ DB_PORT=3306 SMTP_HOST=autodiscover.qcells.com SMTP_PORT=25 -SMTP_SECURE=true +SMTP_SECURE=false SMTP_USER=hss404.u021@cleverse.dev SMTP_PASSWORD=0000 SMTP_FROM=qsalesplatform@qcells.com \ No newline at end of file diff --git a/.env.localhost b/.env.localhost index 3f760ab..844bc76 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://172.30.1.65:3000 +NEXT_PUBLIC_API_URL=http://172.30.1.23:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 @@ -20,7 +20,7 @@ DB_PORT=3306 SMTP_HOST=autodiscover.qcells.com SMTP_PORT=25 -SMTP_SECURE=true +SMTP_SECURE=false SMTP_USER=hss404.u021@cleverse.dev SMTP_PASSWORD=0000 SMTP_FROM=qsalesplatform@qcells.com diff --git a/src/app/api/comm-code/route.ts b/src/app/api/comm-code/route.ts index 6b4852c..d000836 100644 --- a/src/app/api/comm-code/route.ts +++ b/src/app/api/comm-code/route.ts @@ -20,7 +20,6 @@ export async function GET(request: NextRequest) { if (!headCd) { return NextResponse.json({ error: `${headCode}를 찾을 수 없습니다` }, { status: 404 }) } - // @ts-ignore const roofMaterials: CommCode[] = await prisma.BC_COMM_L.findMany({ where: { @@ -35,10 +34,29 @@ export async function GET(request: NextRequest) { CODE: 'asc', }, }) - + if (headCode === 'SALES_OFFICE_CD') { + return getSaleOffice(headCd.HEAD_CD) + } return NextResponse.json(roofMaterials) } catch (error) { console.error('❌ 데이터 조회 중 오류가 발생했습니다:', error) return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) } } + +const getSaleOffice = async (headCode: string) => { + // @ts-ignore + const commCodeSaleOffice: CommCode[] = await prisma.BC_COMM_L.findMany({ + where: { + HEAD_CD: headCode, + REF_NUM1: 1, + }, + select: { + CODE: true, + CODE_JP: true, + REF_CHR1: true, + REF_NUM1: true, + }, + }) + return NextResponse.json(commCodeSaleOffice) +} diff --git a/src/app/pdf/survey-sale/page.tsx b/src/app/pdf/survey-sale/[id]/page.tsx similarity index 100% rename from src/app/pdf/survey-sale/page.tsx rename to src/app/pdf/survey-sale/[id]/page.tsx diff --git a/src/components/pdf/SurveySaleDownloadPdf.tsx b/src/components/pdf/SurveySaleDownloadPdf.tsx index 313f18d..02ba732 100644 --- a/src/components/pdf/SurveySaleDownloadPdf.tsx +++ b/src/components/pdf/SurveySaleDownloadPdf.tsx @@ -1,10 +1,30 @@ 'use client' -import { useRef } from 'react' +import { useEffect, useRef } from 'react' import generatePDF, { Margin, Resolution } from 'react-to-pdf' +import { useParams, useRouter } from 'next/navigation' +import { useSurvey } from '@/hooks/useSurvey' +import { radioEtcData, roofMaterial, selectBoxOptions, supplementaryFacilities } from '../survey-sale/detail/RoofForm' +import { useSpinnerStore } from '@/store/spinnerStore' export default function SurveySaleDownloadPdf() { + const params = useParams() + const id = params.id + const router = useRouter() + + const { surveyDetail, isLoadingSurveyDetail } = useSurvey(Number(id)) + const { setIsShow } = useSpinnerStore() + const targetRef = useRef(null) + const isGeneratedRef = useRef(false) + + useEffect(() => { + setIsShow(true) + if (isLoadingSurveyDetail || !surveyDetail || isGeneratedRef.current) return + isGeneratedRef.current = true + handleDownPdf() + }, [surveyDetail?.id, isLoadingSurveyDetail]) + const handleDownPdf = () => { const options = { method: 'open' as const, @@ -28,14 +48,31 @@ export default function SurveySaleDownloadPdf() { }, } - generatePDF(targetRef, options) - // generatePDF(targetRef, { filename: 'page.pdf' }) + generatePDF(targetRef, options).then(() => { + setIsShow(false) + router.push(`/survey-sale/${id}`) + }) } + const supplementList = supplementaryFacilities + .filter((facility) => surveyDetail?.detailInfo?.supplementaryFacilities?.includes(facility.id.toString())) + .map((facility) => facility.name) + return ( <> - -
-
+
+
HWJ 現地調査シート1/2 @@ -46,12 +83,14 @@ export default function SurveySaleDownloadPdf() { 現地明登施工店名

- Sheet2 No.4Sheet2 + {surveyDetail?.store ?? '-'}

現地阴買日

-

2025.05.09

+

+ {surveyDetail?.investigationDate ?? '-'} +

@@ -86,7 +125,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - Sheet2No.1 + {surveyDetail?.customerName ?? '-'} @@ -115,7 +154,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - Sheet2No.2 + {surveyDetail?.postCode ? `(${surveyDetail?.postCode}) ${surveyDetail?.address} ${surveyDetail?.addressDetail}` : '-'} @@ -153,7 +192,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - Sheet2No.1 + {surveyDetail?.detailInfo?.contractCapacity ?? '-'} - Sheet2No.1 + {surveyDetail?.detailInfo?.retailCompany ?? '-'} @@ -210,7 +249,11 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - Sheet2No.7 選式回答表示/自由入力回答表示 + {supplementList === null && surveyDetail?.detailInfo?.supplementaryFacilitiesEtc === null + ? '-' + : surveyDetail?.detailInfo?.supplementaryFacilitiesEtc + ? `${supplementList.join(', ')}, ${surveyDetail?.detailInfo?.supplementaryFacilitiesEtc}` + : supplementList.join(', ')} @@ -240,7 +283,16 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - Sheet2No.8 選択式回表示/自由入力回表 + {/* {selectBoxOptions.installationSystem.find ((system) => system.id.toString() === surveyDetail?.detailInfo?.installationSystem) + ?.name ?? surveyDetail?.detailInfo?.installationSystemEtc !== null + ? `${surveyDetail?.detailInfo?.installationSystemEtc}` + : '-'} */} + {surveyDetail?.detailInfo?.installationSystem === null && surveyDetail?.detailInfo?.installationSystemEtc === null + ? '-' + : surveyDetail?.detailInfo?.installationSystemEtc + ? `${surveyDetail?.detailInfo?.installationSystemEtc}` + : selectBoxOptions.installationSystem.find((system) => system.id.toString() === surveyDetail?.detailInfo?.installationSystem) + ?.name} @@ -278,7 +330,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.9 + {surveyDetail?.detailInfo?.constructionYear === '1' ? '新築' : `既築 (${surveyDetail?.detailInfo?.constructionYear}年)`} - No.10 + {surveyDetail?.detailInfo?.roofMaterial === null && surveyDetail?.detailInfo?.roofMaterialEtc === null + ? '-' + : roofMaterial + .filter((material) => surveyDetail?.detailInfo?.roofMaterial?.includes(material.id.toString())) + .map((material) => material.name) + .join(', ')} + {surveyDetail?.detailInfo?.roofMaterialEtc ? `, ${surveyDetail?.detailInfo?.roofMaterialEtc}` : ''} - No.11 + {selectBoxOptions.roofShape.find((shape) => shape.id.toString() === surveyDetail?.detailInfo?.roofShape)?.name ?? + (surveyDetail?.detailInfo?.roofShapeEtc ? ` ${surveyDetail?.detailInfo?.roofShapeEtc}` : '-')} @@ -361,7 +420,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.12 + {surveyDetail?.detailInfo?.roofSlope ? `${surveyDetail?.detailInfo?.roofSlope} 寸` : '-'} - No.13 + {radioEtcData.houseStructure.find((structure) => structure.id.toString() === surveyDetail?.detailInfo?.houseStructure)?.label ?? + (surveyDetail?.detailInfo?.houseStructureEtc ? ` ${surveyDetail?.detailInfo?.houseStructureEtc}` : '-')} @@ -418,7 +478,12 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.14 + {/* {surveyDetail?.detailInfo?.rafterMaterial === null && surveyDetail?.detailInfo?.rafterMaterialEtc === null + ? '-' + : radioEtcData.rafterMaterial.find((material) => material.id.toString() === surveyDetail?.detailInfo?.rafterMaterial)?.label ?? + surveyDetail?.detailInfo?.rafterMaterialEtc} */} + {radioEtcData.rafterMaterial.find((material) => material.id.toString() === surveyDetail?.detailInfo?.rafterMaterial)?.label ?? + (surveyDetail?.detailInfo?.rafterMaterialEtc ? ` ${surveyDetail?.detailInfo?.rafterMaterialEtc}` : '-')} - No.15 + {selectBoxOptions.rafterSize.find((size) => size.id.toString() === surveyDetail?.detailInfo?.rafterSize)?.name ?? + (surveyDetail?.detailInfo?.rafterSizeEtc ? ` ${surveyDetail?.detailInfo?.rafterSizeEtc}` : '-')} @@ -463,7 +529,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - 垂木ビッチ + 垂木ピッチ - No.16 + {selectBoxOptions.rafterPitch.find((pitch) => pitch.id.toString() === surveyDetail?.detailInfo?.rafterPitch)?.name ?? + (surveyDetail?.detailInfo?.rafterPitchEtc ? ` ${surveyDetail?.detailInfo?.rafterPitchEtc}` : '-')} - No.17 + {radioEtcData.rafterDirection.find((direction) => direction.id.toString() === surveyDetail?.detailInfo?.rafterDirection)?.label ?? + '-'} @@ -532,7 +600,8 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.18 + {selectBoxOptions.openFieldPlateKind.find((kind) => kind.id.toString() === surveyDetail?.detailInfo?.openFieldPlateKind)?.name ?? + (surveyDetail?.detailInfo?.openFieldPlateKindEtc ? `${surveyDetail?.detailInfo?.openFieldPlateKindEtc}` : '-')} - No.19 + {surveyDetail?.detailInfo?.openFieldPlateThickness ? `${surveyDetail?.detailInfo?.openFieldPlateThickness}mm` : '-'} @@ -590,7 +659,7 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.20 + {surveyDetail?.detailInfo?.leakTrace ? 'あり' : 'なし'} @@ -620,7 +689,8 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.21 + {radioEtcData.waterproofMaterial.find((material) => material.id.toString() === surveyDetail?.detailInfo?.waterproofMaterial) + ?.label ?? (surveyDetail?.detailInfo?.waterproofMaterialEtc ? ` ${surveyDetail?.detailInfo?.waterproofMaterialEtc}` : '-')} @@ -650,7 +720,11 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.22 + { + radioEtcData.insulationPresence.find((presence) => presence.id.toString() === surveyDetail?.detailInfo?.insulationPresence) + ?.label + } + {surveyDetail?.detailInfo?.insulationPresenceEtc ? `, ${surveyDetail?.detailInfo?.insulationPresenceEtc}` : ''} @@ -680,7 +754,8 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.23 + {radioEtcData.structureOrder.find((order) => order.id.toString() === surveyDetail?.detailInfo?.structureOrder)?.label ?? + (surveyDetail?.detailInfo?.structureOrderEtc ? `${surveyDetail?.detailInfo?.structureOrderEtc}` : '-')} @@ -718,7 +793,12 @@ export default function SurveySaleDownloadPdf() { boxSizing: 'border-box', }} > - No.23 + {surveyDetail?.detailInfo?.installationAvailability === null && surveyDetail.detailInfo?.installationAvailabilityEtc === null + ? '-' + : selectBoxOptions.installationAvailability.find( + (availability) => availability.id.toString() === surveyDetail?.detailInfo?.installationAvailability, + )?.name} + {surveyDetail?.detailInfo?.installationAvailabilityEtc ? `, ${surveyDetail?.detailInfo?.installationAvailabilityEtc}` : ''} @@ -738,7 +818,7 @@ export default function SurveySaleDownloadPdf() { height: '150px', }} > - No.25 + {surveyDetail?.detailInfo?.memo ?? '-'}
diff --git a/src/components/popup/SurveySaleSubmitPopup.tsx b/src/components/popup/SurveySaleSubmitPopup.tsx index 1d61da1..7a50406 100644 --- a/src/components/popup/SurveySaleSubmitPopup.tsx +++ b/src/components/popup/SurveySaleSubmitPopup.tsx @@ -1,15 +1,20 @@ 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 { useSurvey } from '@/hooks/useSurvey' +import { useEffect, useState } from 'react' import { useSessionStore } from '@/store/session' +import { useCommCode } from '@/hooks/useCommCode' +import { CommCode } from '@/types/CommCode' +import { sendEmail } from '@/libs/mailer' +import { useSpinnerStore } from '@/store/spinnerStore' interface SubmitFormData { + saleBase: string | null store: string sender: string - receiver: string - reference: string + receiver: string[] | string + reference: string | null title: string contents: string } @@ -20,31 +25,46 @@ interface FormField { 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 { setIsShow } = useSpinnerStore() + const [commCodeList, setCommCodeList] = useState([]) + + const { getCommCode } = useCommCode() + + useEffect(() => { + if (session?.isLoggedIn && session?.role === 'Admin') { + getCommCode('SALES_OFFICE_CD').then((codes) => { + setCommCodeList(codes) + }) + } + }, [session]) + + const FORM_FIELDS: FormField[] = [ + { id: 'saleBase', name: '提出地点選択', required: session?.role === 'Admin' }, + { 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 }, + ] + const [submitData, setSubmitData] = useState({ + saleBase: null, store: '', sender: session?.email ?? '', - receiver: '', - reference: '', + receiver: [], + reference: null, title: '[HANASYS現地調査] 調査物件が提出.', contents: '', }) - const { submitSurvey, isSubmittingSurvey } = useServey(Number(routeId)) + const { submitSurvey, isSubmittingSurvey } = useSurvey(Number(routeId)) const handleInputChange = (field: keyof SubmitFormData, value: string) => { setSubmitData((prev) => ({ ...prev, [field]: value })) @@ -54,26 +74,40 @@ export default function SurveySaleSubmitPopup() { const requiredFields = FORM_FIELDS.filter((field) => field.required) for (const field of requiredFields) { - if (!data[field.id].trim()) { + if (data[field.id]?.length === 0) { + alert(`${field.name}は必須入力項目です。`) const element = document.getElementById(field.id) if (element) { element.focus() } - alert(`${field.name}は必須入力項目です。`) return false } } - return true } const handleSubmit = () => { if (validateData(submitData)) { window.neoConfirm('送信しますか? 送信後は変更・修正することはできません。', () => { + setIsShow(true) submitSurvey({ targetId: submitData.store }) + sendEmail({ + to: submitData.receiver, + subject: submitData.title, + content: submitData.contents, + }) + .then(() => { if (!isSubmittingSurvey) { popupController.setSurveySaleSubmitPopup(false) } + }) + .catch((error) => { + console.error('Error sending email:', error) + alert('メール送信に失敗しました。') + }) + .finally(() => { + setIsShow(false) + }) }) } } @@ -83,9 +117,12 @@ export default function SurveySaleSubmitPopup() { } const renderFormField = (field: FormField) => { - // const isReadOnly = (field.id === 'store' && session?.role !== 'Partner') || (field.id === 'receiver' && session?.role !== 'Partner') const isReadOnly = false + if (field.id === 'saleBase' && session?.role !== 'Admin') { + return null + } + return (
@@ -96,18 +133,43 @@ export default function SurveySaleSubmitPopup() {