Merge pull request 'feat: seporate save/submit/update process for reuse component' (#39) from feature/survey into dev

Reviewed-on: #39
This commit is contained in:
Dayoung 2025-05-20 13:57:22 +09:00
commit e2e5a484ca
14 changed files with 824 additions and 357 deletions

View File

@ -1,9 +1,10 @@
import { NextResponse } from 'next/server'
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
import { convertToSnakeCase } from '../route'
export async function GET(request: Request, context: { params: { id: string } }) {
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await context.params
const { id } = await params
// @ts-ignore
const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findUnique({
where: { ID: Number(id) },
@ -18,34 +19,34 @@ export async function GET(request: Request, context: { params: { id: string } })
}
}
export async function PUT(request: Request, context: { params: { id: string } }) {
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await context.params
const { id } = await params
const body = await request.json()
console.log('body:: ', body)
// DETAIL_INFO를 분리
const { DETAIL_INFO, ...basicInfo } = body
console.log('body:: ', body)
// @ts-ignore
const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
where: { ID: Number(id) },
data: {
...basicInfo,
...convertToSnakeCase(basicInfo),
UPT_DT: new Date(),
DETAIL_INFO: DETAIL_INFO
? {
upsert: {
create: DETAIL_INFO,
update: DETAIL_INFO,
},
DETAIL_INFO: DETAIL_INFO ? {
upsert: {
create: convertToSnakeCase(DETAIL_INFO),
update: convertToSnakeCase(DETAIL_INFO),
where: {
BASIC_INFO_ID: Number(id)
}
: undefined,
}
} : undefined
},
include: {
DETAIL_INFO: true,
},
DETAIL_INFO: true
}
})
console.log('survey:: ', survey)
return NextResponse.json(survey)
} catch (error) {
console.error('Error updating survey:', error)
@ -53,9 +54,9 @@ export async function PUT(request: Request, context: { params: { id: string } })
}
}
export async function DELETE(request: Request, context: { params: { id: string } }) {
export async function DELETE(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await context.params
const { id } = await params
await prisma.$transaction(async (tx) => {
// @ts-ignore
@ -86,9 +87,9 @@ export async function DELETE(request: Request, context: { params: { id: string }
}
}
export async function PATCH(request: Request, context: { params: { id: string } }) {
export async function PATCH(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try {
const { id } = await context.params
const { id } = await params
const body = await request.json()
if (body.submit) {
@ -98,40 +99,42 @@ export async function PATCH(request: Request, context: { params: { id: string }
data: {
SUBMISSION_STATUS: true,
SUBMISSION_DATE: new Date(),
UPT_DT: new Date(),
},
})
return NextResponse.json({ message: 'Survey confirmed successfully' })
} else {
// @ts-ignore
const hasDetails = await prisma.SD_SURVEY_SALES_DETAIL_INFO.findUnique({
where: { BASIC_INFO_ID: Number(id) },
})
if (hasDetails) {
//@ts-ignore
const result = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
where: { ID: Number(id) },
data: {
UPT_DT: new Date(),
DETAIL_INFO: {
update: body.DETAIL_INFO,
},
},
})
return NextResponse.json(result)
} else {
// @ts-ignore
const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
where: { ID: Number(id) },
data: {
DETAIL_INFO: {
create: body.DETAIL_INFO,
},
},
})
return NextResponse.json({ message: 'Survey detail created successfully' })
}
}
// } else {
// // @ts-ignore
// const hasDetails = await prisma.SD_SURVEY_SALES_DETAIL_INFO.findUnique({
// where: { BASIC_INFO_ID: Number(id) },
// })
// if (hasDetails) {
// //@ts-ignore
// const result = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
// where: { ID: Number(id) },
// data: {
// UPT_DT: new Date(),
// DETAIL_INFO: {
// update: convertToSnakeCase(body.DETAIL_INFO),
// },
// },
// })
// return NextResponse.json(result)
// } else {
// // @ts-ignore
// const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.update({
// where: { ID: Number(id) },
// data: {
// DETAIL_INFO: {
// create: convertToSnakeCase(body.DETAIL_INFO),
// },
// },
// })
// return NextResponse.json({ message: 'Survey detail created successfully' })
// }
// }
} catch (error) {
console.error('Error updating survey:', error)
return NextResponse.json({ error: 'Failed to update survey' }, { status: 500 })

View File

@ -219,19 +219,46 @@ export async function PUT(request: Request) {
}
}
export async function POST(request: Request) {
// 카멜케이스를 스네이크케이스로 변환하는 함수
export const toSnakeCase = (str: string) => {
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`);
}
// 객체의 키를 스네이크케이스로 변환하는 함수
export const convertToSnakeCase = (obj: any): Record<string, any> => {
if (obj === null || obj === undefined) return obj;
if (Array.isArray(obj)) {
return obj.map((item: any) => convertToSnakeCase(item))
}
if (typeof obj === 'object') {
return Object.keys(obj).reduce((acc, key) => {
const snakeKey = toSnakeCase(key).toUpperCase();
acc[snakeKey] = convertToSnakeCase(obj[key]);
return acc;
}, {} as Record<string, any>);
}
return obj;
}
export async function POST(request: Request) {
try {
const body = await request.json()
const { DETAIL_INFO, ...basicInfo } = body
console.log('body:: ', body)
const { detailInfo, ...basicInfo } = body
// 기본 정보 생성
//@ts-ignore
const result = await prisma.SD_SURVEY_SALES_BASIC_INFO.create({
data: {
...basicInfo,
...convertToSnakeCase(basicInfo),
DETAIL_INFO: {
create: DETAIL_INFO,
},
},
create: convertToSnakeCase(detailInfo)
}
}
})
return NextResponse.json(result)
} catch (error) {

View File

@ -4,7 +4,6 @@ export default function page() {
return (
<>
<DataTable />
{/* <DetailForm surveyInfo={surveyInfo} mode="READ" /> */}
</>
)
}

View File

@ -1,9 +1,9 @@
import RegistForm from '@/components/survey-sale/detail/RegistForm'
import DetailForm from "@/components/survey-sale/detail/DetailForm";
export default function RegistPage() {
return (
<>
<RegistForm/>
<DetailForm />
</>
)
}

View File

@ -2,18 +2,45 @@
import { useEffect, useState } from 'react'
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
import { SurveyBasicRequest } from '@/types/Survey'
import { Mode } from 'fs'
import type { SurveyBasicRequest } from '@/types/Survey'
import type { Mode } from 'fs'
import { useSessionStore } from '@/store/session'
import { usePopupController } from '@/store/popupController'
import { useAddressStore } from '@/store/addressStore'
export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBasicInfo: (basicInfo: SurveyBasicRequest) => void; mode: Mode }) {
const { basicInfo, setBasicInfo, mode } = props
const { setBasicInfoSelected } = useSurveySaleTabState()
const [isFlip, setIsFlip] = useState<boolean>(true)
const { session } = useSessionStore()
const { addressData } = useAddressStore()
useEffect(() => {
setBasicInfoSelected()
}, [])
useEffect(() => {
if (session?.isLoggedIn) {
setBasicInfo({
...basicInfo,
representative: session.userNm ?? '',
store: session.storeNm ?? null,
constructionPoint: session.builderNo ?? null,
})
}
if (addressData) {
setBasicInfo({
...basicInfo,
postCode: addressData.post_code,
address: addressData.address,
addressDetail: addressData.address_detail,
})
}
}, [session, addressData])
const popupController = usePopupController()
return (
<>
<div className={`sale-detail-toggle-bx ${isFlip ? 'act' : ''}`}>
@ -31,31 +58,35 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
<input
type="text"
className="input-frame"
readOnly={mode === 'READ'}
readOnly
value={basicInfo?.representative ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, representative: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
readOnly={mode === 'READ'}
value={basicInfo?.store ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, store: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
readOnly={mode === 'READ'}
value={basicInfo?.constructionPoint ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, constructionPoint: e.target.value })}
/>
</div>
{(session?.role === 'Builder' || session?.role?.includes('Admin')) && (
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
readOnly
value={basicInfo?.store ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, store: e.target.value })}
/>
</div>
)}
{(session?.role === 'Builder' || session?.role === 'Partner') && (
<div className="data-input-form-bx">
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
readOnly
value={basicInfo?.constructionPoint ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, constructionPoint: e.target.value })}
/>
</div>
)}
</div>
</div>
<div className="sale-frame">
@ -67,7 +98,13 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
<button className="date-btn">
<i className="date-icon"></i>
</button>
<input type="date" className="date-frame" readOnly defaultValue={basicInfo?.investigationDate?.toString()} />
<input
id="investigationDate"
type="date"
className="date-frame"
defaultValue={basicInfo?.investigationDate?.toString()}
onChange={(e) => setBasicInfo({ ...basicInfo, investigationDate: e.target.value })}
/>
</div>
) : (
<input type="date" className="input-frame" disabled defaultValue={basicInfo?.investigationDate?.toString()} />
@ -76,12 +113,24 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
<div className="data-input-form-bx">
{/* 건물명 */}
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" readOnly={mode === 'READ'} defaultValue={basicInfo?.buildingName ?? ''} />
<input
type="text"
className="input-frame"
readOnly={mode === 'READ'}
defaultValue={basicInfo?.buildingName ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, buildingName: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
{/* 고객명 */}
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" readOnly={mode === 'READ'} defaultValue={basicInfo?.customerName ?? ''} />
<div className="data-input-form-tit"></div>
<input
type="text"
className="input-frame"
readOnly={mode === 'READ'}
defaultValue={basicInfo?.customerName ?? ''}
onChange={(e) => setBasicInfo({ ...basicInfo, customerName: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
<div className="data-input-form-tit">便/</div>
@ -92,12 +141,12 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
</div>
{/* 도도부현 */}
<div className="form-bx">
<input type="text" className="input-frame" readOnly={mode === 'READ'} defaultValue={basicInfo?.address ?? ''} disabled />
<input type="text" className="input-frame" readOnly={mode === 'READ'} defaultValue={basicInfo?.address ?? ''} />
</div>
</div>
{/* 주소 */}
<div className="form-btn">
<button className="btn-frame n-blue icon">
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(true)}>
便<i className="btn-arr"></i>
</button>
</div>
@ -105,7 +154,7 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
<div className="data-input-form-bx">
<div className="data-input-form-tit">, </div>
<input type="text" className="input-frame" defaultValue={'浜松 浜松町'} readOnly={mode === 'READ'} />
<input type="text" className="input-frame" defaultValue={basicInfo?.addressDetail ?? ''} readOnly={mode === 'READ'} />
</div>
</div>
</div>

View File

@ -1,81 +1,267 @@
import { Mode, SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey'
'use client'
export default function ButtonForm(props: { mode: Mode; setMode: (mode: Mode) => void; data: { basic: SurveyBasicRequest; roof: SurveyDetailRequest } }) {
import type { Mode, SurveyBasicRequest, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
import { useSessionStore } from '@/store/session'
import { useEffect, useState } from 'react'
import { useParams, useRouter, useSearchParams } from 'next/navigation'
import { requiredFields, useServey } from '@/hooks/useSurvey'
export default function ButtonForm(props: {
mode: Mode
setMode: (mode: Mode) => void
data: { basic: SurveyBasicRequest; roof: SurveyDetailRequest }
}) {
// 라우터
const router = useRouter()
const { mode, setMode } = props
const { session } = useSessionStore()
const searchParams = useSearchParams()
const idParam = searchParams.get('id')
const params = useParams()
const routeId = params.id
const [isSubmitProcess, setIsSubmitProcess] = useState(false)
// ------------------------------------------------------------
// 권한
// 제출권한 ㅇ
const [isSubmiter, setIsSubmiter] = useState(false)
// 작성자
const [isWriter, setIsWriter] = useState(false)
const isSubmit = props.data.basic.submissionStatus
useEffect(() => {
if (session?.isLoggedIn) {
setIsSubmiter(session.storeNm === props.data.basic.store && session.builderNo === props.data.basic.constructionPoint)
setIsWriter(session.userNm === props.data.basic.representative)
}
}, [session, props.data])
// ------------------------------------------------------------
// 저장/임시저장/수정
const id = routeId ? Number(routeId) : Number(idParam)
const { deleteSurvey, submitSurvey, updateSurvey } = useServey(Number(id))
const { validateSurveyDetail, createSurvey } = useServey()
let saveData = {
...props.data.basic,
detailInfo: props.data.roof,
}
const handleSave = (isTemporary: boolean) => {
const emptyField = validateSurveyDetail(props.data.roof)
console.log('handleSave, emptyField:: ', emptyField)
if (isTemporary) {
tempSaveProcess()
} else {
saveProcess(emptyField)
}
}
const tempSaveProcess = async () => {
if (idParam) {
await updateSurvey(saveData)
router.push(`/survey-sale/detail?id=${idParam}&isTemporary=true`)
} else {
const id = await createSurvey(saveData)
router.push(`/survey-sale/detail?id=${id}&isTemporary=true`)
}
alert('一時保存されました。')
}
const focusInput = (field: keyof SurveyDetailInfo) => {
const input = document.getElementById(field)
if (input) {
input.focus()
}
}
const saveProcess = async (emptyField: string) => {
if (emptyField.trim() === '') {
if (idParam) {
// 수정 페이지에서 작성 후 제출
if (isSubmitProcess) {
saveData = {
...saveData,
submissionStatus: true,
submissionDate: new Date().toISOString(),
}
}
await updateSurvey(saveData)
router.push(`/survey-sale/${idParam}`)
} else {
const id = await createSurvey(saveData)
if (isSubmitProcess) {
submitProcess(id)
return
}
router.push(`/survey-sale/${id}`)
}
alert('保存されました。')
} else {
if (emptyField.includes('Unit')) {
alert('電気契約容量の単位を入力してください。')
focusInput(emptyField as keyof SurveyDetailInfo)
} else {
alert(requiredFields.find((field) => field.field === emptyField)?.name + ' 項目が空です。')
focusInput(emptyField as keyof SurveyDetailInfo)
}
}
}
// ------------------------------------------------------------
// 삭제/제출
const handleDelete = async () => {
if (routeId) {
window.neoConfirm('削除しますか?', async () => {
await deleteSurvey()
router.push('/survey-sale')
})
}
}
const handleSubmit = async () => {
window.neoConfirm('提出しますか?', async () => {
setIsSubmitProcess(true)
if (routeId) {
submitProcess()
} else {
handleSave(false)
}
})
}
const submitProcess = async (saveId?: number) => {
await submitSurvey(saveId)
alert('提出されました。')
router.push('/survey-sale')
}
// ------------------------------------------------------------
if (mode === 'READ' && isSubmit && isSubmiter) {
return (
<>
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
</div>
</div>
</>
)
}
return (
<>
{mode === 'CREATE' && (
{mode === 'READ' && (
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<div className="btn-bx">
{/* 임시저장 */}
<button className="btn-frame n-blue icon" onClick={() => setMode('TEMP')}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 저장 */}
<button className="btn-frame red icon">
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 목록 */}
<button className="btn-frame n-blue icon">
<i className="btn-arr"></i>
</button>
</div>
<ListButton />
<EditButton setMode={setMode} id={id.toString()} mode={mode} />
{(isWriter || !isSubmiter) && <DeleteButton handleDelete={handleDelete} />}
{!isSubmit && isSubmiter && <SubmitButton handleSubmit={handleSubmit} />}
</div>
</div>
)}
{mode === 'TEMP' && (
{(mode === 'CREATE' || mode === 'EDIT') && (
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<div className="btn-bx">
{/* 수정 */}
<button className="btn-frame n-blue icon" onClick={() => setMode('EDIT')}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 삭제 */}
<button className="btn-frame n-blue icon">
<i className="btn-arr"></i>
</button>
</div>
</div>
</div>
)}
{mode === 'EDIT' && (
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<div className="btn-bx">
{/* 목록 */}
<button className="btn-frame n-blue icon">
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 제출 */}
<button className="btn-frame red icon">
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 수정 */}
<button className="btn-frame n-blue icon" onClick={() => setMode('EDIT')}>
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
{/* 삭제 */}
<button className="btn-frame n-blue icon">
<i className="btn-arr"></i>
</button>
</div>
<ListButton />
<TempButton setMode={setMode} handleSave={handleSave} />
<SaveButton handleSave={handleSave} />
<SubmitButton handleSubmit={handleSubmit} />
</div>
</div>
)}
</>
)
}
// 목록 버튼
function ListButton() {
const router = useRouter()
return (
<div className="btn-bx">
{/* 목록 */}
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
<i className="btn-arr"></i>
</button>
</div>
)
}
function EditButton(props: { setMode: (mode: Mode) => void; id: string; mode: Mode }) {
const { setMode, id, mode } = props
const router = useRouter()
return (
<div className="btn-bx">
{/* 수정 */}
<button
className="btn-frame n-blue icon"
onClick={() => {
router.push(`/survey-sale/regist?id=${id}`)
setMode('EDIT')
}}
>
<i className="btn-arr"></i>
</button>
</div>
)
}
function SubmitButton(props: { handleSubmit: () => void }) {
const { handleSubmit } = props
return (
<div className="btn-bx">
{/* 제출 */}
<button className="btn-frame red icon" onClick={handleSubmit}>
<i className="btn-arr"></i>
</button>
</div>
)
}
function DeleteButton(props: { handleDelete: () => void }) {
const { handleDelete } = props
return (
<div className="btn-bx">
{/* 삭제 */}
<button className="btn-frame n-blue icon" onClick={handleDelete}>
<i className="btn-arr"></i>
</button>
</div>
)
}
function SaveButton(props: { handleSave: (isTemporary: boolean) => void }) {
const { handleSave } = props
return (
<div className="btn-bx">
{/* 저장 */}
<button className="btn-frame n-blue icon" onClick={() => handleSave(false)}>
<i className="btn-arr"></i>
</button>
</div>
)
}
function TempButton(props: { setMode: (mode: Mode) => void; handleSave: (isTemporary: boolean) => void }) {
const { setMode, handleSave } = props
const router = useRouter()
return (
<div className="btn-bx">
{/* 임시저장 */}
<button
className="btn-frame n-blue icon"
onClick={() => {
setMode('TEMP')
handleSave(true)
}}
>
<i className="btn-arr"></i>
</button>
</div>
)
}

View File

@ -4,7 +4,7 @@ import { useServey } from '@/hooks/useSurvey'
import { useParams, useSearchParams } from 'next/navigation'
import { useEffect, useState } from 'react'
import DetailForm from './DetailForm'
import { SurveyBasicInfo } from '@/types/Survey'
import type { SurveyBasicInfo } from '@/types/Survey'
export default function DataTable() {
const params = useParams()
@ -83,7 +83,7 @@ export default function DataTable() {
</tbody>
</table>
</div>
<DetailForm surveyInfo={surveyDetail as SurveyBasicInfo} mode="READ" />
<DetailForm />
</>
)
}

View File

@ -1,10 +1,13 @@
'use client'
import { Mode, SurveyBasicInfo, SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey'
import type { Mode, SurveyBasicInfo, SurveyBasicRequest, SurveyDetailRequest } from '@/types/Survey'
import { useEffect, useState } from 'react'
import ButtonForm from './ButtonForm'
import BasicForm from './BasicForm'
import RoofForm from './RoofForm'
import { useParams, useSearchParams } from 'next/navigation'
import { useServey } from '@/hooks/useSurvey'
const roofInfoForm: SurveyDetailRequest = {
contractCapacity: null,
retailCompany: null,
@ -57,21 +60,32 @@ const basicInfoForm: SurveyBasicRequest = {
submissionDate: null,
}
export default function DetailForm(props: { surveyInfo?: SurveyBasicInfo; mode?: Mode }) {
const [mode, setMode] = useState<Mode>(props.mode ?? 'CREATE')
export default function DetailForm() {
const idParam = useSearchParams().get('id')
const routeId = useParams().id
const id = idParam ?? routeId
const { surveyDetail } = useServey(Number(id))
const [mode, setMode] = useState<Mode>(idParam ? 'EDIT' : routeId ? 'READ' : 'CREATE')
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(basicInfoForm)
const [roofInfoData, setRoofInfoData] = useState<SurveyDetailRequest>(roofInfoForm)
useEffect(() => {
if (props.surveyInfo && (mode === 'EDIT' || mode === 'READ')) {
const { id, uptDt, regDt, detailInfo, ...rest } = props.surveyInfo
if (surveyDetail && (mode === 'EDIT' || mode === 'READ')) {
const { id, uptDt, regDt, detailInfo, ...rest } = surveyDetail
setBasicInfoData(rest)
if (detailInfo) {
const { id, uptDt, regDt, basicInfoId, ...rest } = detailInfo
setRoofInfoData(rest)
}
}
}, [props.surveyInfo, mode])
}, [surveyDetail, mode])
// console.log('mode:: ', mode)
// console.log('surveyDetail:: ', surveyDetail)
// console.log('roofInfoData:: ', roofInfoData)
const data = {
basic: basicInfoData,

View File

@ -1,29 +0,0 @@
import { Mode } from '@/types/Survey'
import { useSearchParams } from 'next/navigation'
import DetailForm from './DetailForm'
import { useServey } from '@/hooks/useSurvey'
import { useEffect, useState } from 'react'
import { SurveyBasicInfo } from '@/types/Survey'
import { useSessionStore } from '@/store/session'
export default function RegistForm() {
const searchParams = useSearchParams()
const id = searchParams.get('id')
const { surveyDetail } = useServey(Number(id))
const { session } = useSessionStore()
const [mode, setMode] = useState<Mode>('CREATE')
useEffect(() => {
if (id) {
setMode('EDIT')
}
}, [id])
return (
<>
<DetailForm mode={mode} surveyInfo={surveyDetail as SurveyBasicInfo} />
</>
)
}

View File

@ -1,5 +1,5 @@
import { useState } from 'react'
import { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
import type { Mode, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
type RadioEtcKeys = 'houseStructure' | 'rafterMaterial' | 'waterproofMaterial' | 'insulationPresence' | 'rafterDirection' | 'leakTrace'
type SelectBoxKeys =
@ -12,14 +12,14 @@ type SelectBoxKeys =
| 'structureOrder'
| 'installationAvailability'
export const supplementary_facilities = [
export const supplementaryFacilities = [
{ id: 1, name: 'エコキュート' }, //에코큐트
{ id: 2, name: 'エネパーム' }, //에네팜
{ id: 3, name: '蓄電池システム' }, //축전지시스템
{ id: 4, name: '太陽光発電' }, //태양광발전
]
export const roof_material = [
export const roofMaterial = [
{ id: 1, name: 'スレート' }, //슬레이트
{ id: 2, name: 'アスファルトシングル' }, //아스팔트 싱글
{ id: 3, name: '瓦' }, //기와
@ -200,19 +200,47 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
],
}
const makeNumArr = (value: string) => {
return value
.split(',')
.map((v) => v.trim())
.filter((v) => v.length > 0)
}
export default function RoofForm(props: {
roofInfo: SurveyDetailRequest | SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
mode: Mode
}) {
const makeNumArr = (value: string) => {
return value
.split(',')
.map((v) => v.trim())
.filter((v) => v.length > 0)
}
const { roofInfo, setRoofInfo, mode } = props
const [isFlip, setIsFlip] = useState<boolean>(true)
const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => {
if (key === 'roofSlope' || key === 'openFieldPlateThickness') {
const stringValue = value.toString()
if (stringValue.length > 5) {
alert('保存できるサイズを超えました。')
return
}
if (stringValue.includes('.')) {
const decimalPlaces = stringValue.split('.')[1].length
if (decimalPlaces > 1) {
alert('小数点以下1桁までしか許されません。')
return
}
}
}
setRoofInfo({ ...roofInfo, [key]: value.toString() })
}
const handleUnitInput = (value: string) => {
const numericValue = roofInfo.contractCapacity?.replace(/[^0-9.]/g, '') || ''
setRoofInfo({
...roofInfo,
contractCapacity: numericValue ? `${numericValue} ${value}` : value,
})
}
return (
<div className={`sale-detail-toggle-bx ${isFlip ? 'act' : ''}`}>
<div className="sale-detail-toggle-head" onClick={() => setIsFlip(!isFlip)}>
@ -229,78 +257,55 @@ export default function RoofForm(props: {
<div className="data-input-form-bx">
{/* 전기 계약 용량 */}
<div className="data-input-form-tit"></div>
<div className="data-input mb5">
<input type="text" className="input-frame" value={roofInfo?.contractCapacity ?? ''} readOnly={mode === 'READ'} />
</div>
{mode === 'READ' && <input type="text" className="input-frame" defaultValue={'10'} readOnly={mode === 'READ'} />}
{mode === 'READ' && <input type="text" className="input-frame" value={roofInfo?.contractCapacity ?? ''} disabled={mode === 'READ'} />}
{mode !== 'READ' && (
<div className="data-input">
<select className="select-form" name="" id="">
<option value="">kVA</option>
<option value="">kVA</option>
<option value="">kVA</option>
<option value="">kVA</option>
<option value="">kVA</option>
</select>
<div className="data-input mb5">
<input
type="text"
id="contractCapacity"
className="input-frame"
value={roofInfo?.contractCapacity?.split(' ')[0] ?? ''}
onChange={(e) => handleNumberInput('contractCapacity', e.target.value)}
/>
<div className="data-input">
<select
className="select-form"
name="contractCapacityUnit"
id="contractCapacityUnit"
value={roofInfo?.contractCapacity?.split(' ')[1] ?? ''}
onChange={(e) => handleUnitInput(e.target.value)}
>
<option value="" hidden>
</option>
<option value="A">A</option>
<option value="kVA">kVA</option>
</select>
</div>
</div>
)}
</div>
<div className="data-input-form-bx">
{/* 전기 소매 회사사 */}
<div className="data-input-form-tit"></div>
<input type="text" className="input-frame" value={roofInfo?.retailCompany ?? ''} readOnly={mode === 'READ'} />
<input
type="text"
className="input-frame"
value={roofInfo?.retailCompany ?? ''}
disabled={mode === 'READ'}
onChange={(e) => setRoofInfo({ ...roofInfo, retailCompany: e.target.value })}
/>
</div>
<div className="data-input-form-bx">
{/* 전기 부대 설비 */}
<div className="data-input-form-tit">
<span></span>
</div>
<div className="data-check-wrap">
{/* <div className="check-form-box">
<input type="checkbox" id="ch01" readOnly={mode === 'READ'} />
<label htmlFor="ch01"></label>
</div> */}
{supplementary_facilities.map((item) => (
<div className="check-form-box" key={item.id}>
<input
type="checkbox"
id={`${item.id}`}
checked={makeNumArr(roofInfo?.supplementaryFacilities ?? '').includes(String(item.id))}
readOnly={mode === 'READ'}
/>
<label htmlFor={`${item.id}`}>{item.name}</label>
</div>
))}
<div className="check-form-box">
<input type="checkbox" id={`supplementaryFacilitiesEtc`} checked={roofInfo?.supplementaryFacilitiesEtc !== null} readOnly />
<label htmlFor={`supplementaryFacilitiesEtc`}> ()</label>
</div>
</div>
<div className="data-input">
<input type="text" className="input-frame" placeholder="-" readOnly value={roofInfo?.supplementaryFacilitiesEtc ?? ''} />
</div>
<MultiCheck mode={mode} column="supplementaryFacilities" roofInfo={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 설치 희망 시스템 */}
{/* <div className="data-input-form-tit red-f"></div>
{mode === 'READ' && (
<div className="data-input">
<input type="text" className="input-frame" defaultValue={''} disabled />
</div>
)}
{mode !== 'READ' && (
<div className="data-input mb5">
<select className="select-form" name="" id="">
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
</select>
</div>
)} */}
<div className="data-input-form-tit"></div>
<SelectedBox column="installationSystem" detailInfoData={roofInfo as SurveyDetailInfo} />
<div className="data-input-form-tit red-f"></div>
<SelectedBox mode={mode} column="installationSystem" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
</div>
@ -312,108 +317,73 @@ export default function RoofForm(props: {
{/* 건축 연수 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
{/* {mode === 'READ' && <input type="text" className="input-frame" defaultValue={''} disabled />}
{mode !== 'READ' && (
<select className="select-form" name="" id="">
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
</select>
)} */}
<SelectedBox column="constructionYear" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="constructionYear" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
{/* <div className="data-input flex">
<input type="text" className="input-frame" defaultValue={''} disabled />
<span></span>
</div> */}
</div>
<div className="data-input-form-bx">
{/* 지붕재 */}
<div className="data-input-form-tit">
<span>2</span>
</div>
<div className="data-check-wrap">
{roof_material.map((item) => (
<div className="check-form-box" key={item.id}>
<input type="checkbox" id={`${item.id}`} checked={makeNumArr(roofInfo?.roofMaterial ?? '').includes(String(item.id))} readOnly />
<label htmlFor={`${item.id}`}>{item.name}</label>
</div>
))}
<div className="check-form-box">
<input type="checkbox" id={`roofMaterialEtc`} checked={roofInfo?.roofMaterialEtc !== null} readOnly />
<label htmlFor={`roofMaterialEtc`}> ()</label>
</div>
</div>
<div className="data-input">
<input type="text" className="input-frame" placeholder="-" readOnly value={roofInfo?.roofMaterialEtc ?? ''} />
</div>
<MultiCheck mode={mode} column="roofMaterial" roofInfo={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 모양 */}
<div className="data-input-form-tit"></div>
<div className="data-input-form-tit"></div>
<div className="data-input mb5">
{/* {mode === 'READ' && <input type="text" className="input-frame" defaultValue={''} disabled />}
{mode !== 'READ' && (
<select className="select-form" name="" id="">
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
<option value=""></option>
</select>
)} */}
<SelectedBox column="roofShape" detailInfoData={roofInfo as SurveyDetailInfo} />
</div>
<div className="data-input">
<input type="text" className="input-frame" defaultValue={''} disabled />
<SelectedBox mode={mode} column="roofShape" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 지붕 경사도도 */}
<div className="data-input-form-tit"></div>
<div className="data-input flex">
<input type="text" className="input-frame" value={roofInfo?.roofSlope ?? ''} readOnly={mode === 'READ'} />
<input
type="number"
className="input-frame"
value={roofInfo?.roofSlope ?? ''}
disabled={mode === 'READ'}
onChange={(e) => handleNumberInput('roofSlope', e.target.value)}
/>
<span></span>
</div>
</div>
<div className="data-input-form-bx">
{/* 주택구조조 */}
<div className="data-input-form-tit"></div>
<RadioSelected column="houseStructure" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="houseStructure" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 서까래 재질 */}
<div className="data-input-form-tit"></div>
<RadioSelected column="rafterMaterial" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="rafterMaterial" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 서까래 크기 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
<SelectedBox column="rafterSize" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="rafterSize" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 서까래 피치 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-input mb5">
<SelectedBox column="rafterPitch" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="rafterPitch" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 서까래 방향 */}
<div className="data-input-form-tit red-f"></div>
<div className="data-check-wrap mb0">
<RadioSelected column="rafterDirection" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="rafterDirection" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 노지판 종류류 */}
<div className="data-input-form-tit"></div>
<div className="data-input mb5">
<SelectedBox column="openFieldPlateKind" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="openFieldPlateKind" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
@ -422,7 +392,13 @@ export default function RoofForm(props: {
<span>, . </span>
</div>
<div className="data-input flex">
<input type="text" className="input-frame" value={roofInfo?.openFieldPlateThickness ?? ''} readOnly={mode === 'READ'} />
<input
type="text"
className="input-frame"
value={roofInfo?.openFieldPlateThickness ?? ''}
disabled={mode === 'READ'}
onChange={(e) => handleNumberInput('openFieldPlateThickness', e.target.value)}
/>
<span>mm</span>
</div>
</div>
@ -430,28 +406,28 @@ export default function RoofForm(props: {
{/* 누수 흔적 */}
<div className="data-input-form-tit "></div>
<div className="data-check-wrap mb0">
<RadioSelected column="leakTrace" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="leakTrace" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
</div>
<div className="data-input-form-bx">
{/* 방수재 종류 */}
<div className="data-input-form-tit red-f"></div>
<RadioSelected column="waterproofMaterial" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="waterproofMaterial" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 단열재 유무 */}
<div className="data-input-form-tit red-f"></div>
<RadioSelected column="insulationPresence" detailInfoData={roofInfo as SurveyDetailInfo} />
<RadioSelected mode={mode} column="insulationPresence" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 구조의 순서 */}
<div className="data-input-form-tit red-f"></div>
<SelectedBox column="structureOrder" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="structureOrder" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 지붕 제품명 설치 가능 여부 확인 */}
<div className="data-input-form-tit"> </div>
<SelectedBox column="installationAvailability" detailInfoData={roofInfo as SurveyDetailInfo} />
<SelectedBox mode={mode} column="installationAvailability" detailInfoData={roofInfo as SurveyDetailInfo} setRoofInfo={setRoofInfo} />
</div>
<div className="data-input-form-bx">
{/* 메모 */}
@ -459,11 +435,12 @@ export default function RoofForm(props: {
<div className="data-input">
<textarea
className="textarea-form"
name=""
id=""
name="memo"
id="memo"
placeholder="TextArea Filed"
value={roofInfo?.memo ?? ''}
readOnly={mode === 'READ'}
disabled={mode === 'READ'}
onChange={(e) => setRoofInfo({ ...roofInfo, memo: e.target.value })}
></textarea>
</div>
</div>
@ -474,23 +451,107 @@ export default function RoofForm(props: {
)
}
const SelectedBox = ({ column, detailInfoData }: { column: string; detailInfoData: SurveyDetailInfo }) => {
const SelectedBox = ({
mode,
column,
detailInfoData,
setRoofInfo,
}: {
mode: Mode
column: string
detailInfoData: SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
const selectedId = detailInfoData?.[column as keyof SurveyDetailInfo]
const etcValue = detailInfoData?.[`${column}Etc` as keyof SurveyDetailInfo]
const [isEtcSelected, setIsEtcSelected] = useState<boolean>(etcValue !== null && etcValue !== undefined && etcValue !== '')
const [etcVal, setEtcVal] = useState<string>(etcValue?.toString() ?? '')
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const value = e.target.value
const isSpecialCase = column === 'constructionYear' || column === 'installationAvailability'
const isEtc = value === 'etc'
const isSpecialEtc = isSpecialCase && value === '2'
const updatedData: typeof detailInfoData = {
...detailInfoData,
[column]: isEtc ? null : value,
[`${column}Etc`]: isEtc ? '' : null,
}
if (isSpecialEtc) {
updatedData[column] = value
}
setIsEtcSelected(isEtc || isSpecialEtc)
if (!isEtc) setEtcVal('')
setRoofInfo(updatedData)
}
const handleEtcInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setEtcVal(value)
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: value })
}
return (
<>
<select className="select-form mb10" name={column} id={column} disabled value={selectedId ? 'selected' : etcValue ? 'etc' : ''}>
<option value="">-</option>
<option value="etc"> ()</option>
<option value="selected">{selectBoxOptions[column as keyof typeof selectBoxOptions][Number(selectedId)]?.name}</option>
<select
className="select-form mb10"
name={column}
id={column}
disabled={mode === 'READ'}
value={selectedId ? Number(selectedId) : etcValue !== null ? 'etc' : ''}
onChange={handleSelectChange}
>
{selectBoxOptions[column as keyof typeof selectBoxOptions].map((item) => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
{column !== 'installationAvailability' && column !== 'constructionYear' && (
<option key="etc" value="etc">
()
</option>
)}
<option key="" value="" hidden>
</option>
</select>
{etcValue && <input type="text" className="input-frame" readOnly value={etcValue.toString()} />}
<div className="data-input">
<input
type="text"
className="input-frame"
placeholder="-"
value={etcVal}
onChange={handleEtcInputChange}
disabled={
mode === 'READ'
? true
: column === 'installationAvailability'
? false
: column === 'constructionYear'
? detailInfoData.constructionYear === '1' || detailInfoData.constructionYear === null
: !isEtcSelected
}
/>
</div>
</>
)
}
const RadioSelected = ({ column, detailInfoData }: { column: string; detailInfoData: SurveyDetailInfo | null }) => {
const RadioSelected = ({
mode,
column,
detailInfoData,
setRoofInfo,
}: {
mode: Mode
column: string
detailInfoData: SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
let selectedId = detailInfoData?.[column as keyof SurveyDetailInfo]
if (column === 'leakTrace') {
selectedId = Number(selectedId)
@ -501,28 +562,182 @@ const RadioSelected = ({ column, detailInfoData }: { column: string; detailInfoD
if (column !== 'rafterDirection') {
etcValue = detailInfoData?.[`${column}Etc` as keyof SurveyDetailInfo]
}
const etcChecked = etcValue !== null && etcValue !== undefined && etcValue !== ''
const [etcChecked, setEtcChecked] = useState<boolean>(etcValue !== null && etcValue !== undefined && etcValue !== '')
const [etcVal, setEtcVal] = useState<string>(etcValue?.toString() ?? '')
const handleRadioChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
if (column === 'leakTrace') {
handleBooleanRadioChange(value)
}
if (value === 'etc') {
setEtcChecked(true)
setRoofInfo({ ...detailInfoData, [column]: null, [`${column}Etc`]: '' })
} else {
if (column === 'insulationPresence' && value === '2') {
setEtcChecked(true)
} else {
setEtcChecked(false)
}
setRoofInfo({ ...detailInfoData, [column]: value, [`${column}Etc`]: null })
}
}
const handleBooleanRadioChange = (value: string) => {
if (value === '1') {
setRoofInfo({ ...detailInfoData, leakTrace: true })
} else {
setRoofInfo({ ...detailInfoData, leakTrace: false })
}
}
const handleEtcInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setEtcVal(value)
setRoofInfo({ ...detailInfoData, [`${column}Etc`]: value })
}
// console.log('column: selectedId', column, selectedId)
return (
<>
{radioEtcData[column as keyof typeof radioEtcData].map((item) => (
<div className="radio-form-box mb10" key={item.id}>
<input type="radio" name={column} id={`${item.id}`} disabled checked={Number(selectedId) === item.id} />
<label htmlFor={`${item.id}`}>{item.label}</label>
<input
type="radio"
name={column}
id={`${column}_${item.id}`}
disabled={mode === 'READ'}
checked={Number(selectedId) === item.id}
onChange={handleRadioChange}
value={item.id}
/>
<label htmlFor={`${column}_${item.id}`}>{item.label}</label>
</div>
))}
{column !== 'rafterDirection' && column !== 'leakTrace' && column !== 'insulationPresence' && (
<div className="radio-form-box mb10">
<input type="radio" name={column} id={`${column}Etc`} value="etc" disabled checked={etcChecked} />
<input
type="radio"
name={column}
id={`${column}Etc`}
value="etc"
disabled={mode === 'READ'}
checked={etcChecked}
onChange={handleRadioChange}
/>
<label htmlFor={`${column}Etc`}> ()</label>
</div>
)}
{etcChecked && (
{column !== 'leakTrace' && column !== 'rafterDirection' && (
<div className="data-input">
<input type="text" className="input-frame" placeholder="-" readOnly value={etcValue?.toString() ?? ''} />
<input
type="text"
className="input-frame"
placeholder="-"
value={etcVal}
onChange={handleEtcInputChange}
disabled={mode === 'READ' || !etcChecked}
/>
</div>
)}
</>
)
}
const MultiCheck = ({
mode,
column,
roofInfo,
setRoofInfo,
}: {
mode: Mode
column: string
roofInfo: SurveyDetailInfo
setRoofInfo: (roofInfo: SurveyDetailRequest) => void
}) => {
const multiCheckData = column === 'supplementaryFacilities' ? supplementaryFacilities : roofMaterial
const [isOtherCheck, setIsOtherCheck] = useState<boolean>(false)
const [otherValue, setOtherValue] = useState<string>(roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo]?.toString() ?? '')
const handleCheckbox = (id: number) => {
const value = makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? ''))
const isOtherSelected = roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo] !== null
let newValue: string[]
if (value.includes(String(id))) {
newValue = value.filter((v) => v !== String(id))
} else {
if (column === 'roofMaterial') {
const totalSelected = value.length + (isOtherSelected ? 1 : 0)
if (totalSelected >= 2) {
alert('屋根材は最大2個まで選択できます。')
return
}
}
newValue = [...value, String(id)]
}
setRoofInfo({ ...roofInfo, [column]: newValue.join(',') })
}
const handleOtherCheckbox = () => {
if (column === 'roofMaterial') {
const value = makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? ''))
const currentSelected = value.length
if (!isOtherCheck && currentSelected >= 2) {
alert('屋根材は最大2個まで選択できます。')
return
}
}
const newIsOtherCheck = !isOtherCheck
setIsOtherCheck(newIsOtherCheck)
setOtherValue('')
setRoofInfo({ ...roofInfo, [`${column}Etc`]: newIsOtherCheck ? '' : null })
}
const handleOtherInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
setOtherValue(value)
setRoofInfo({ ...roofInfo, [`${column}Etc`]: value })
}
return (
<>
<div className="data-check-wrap">
{multiCheckData.map((item) => (
<div className="check-form-box" key={item.id}>
<input
type="checkbox"
id={`${column}_${item.id}`}
checked={makeNumArr(String(roofInfo[column as keyof SurveyDetailInfo] ?? '')).includes(String(item.id))}
disabled={mode === 'READ'}
onChange={() => handleCheckbox(item.id)}
/>
<label htmlFor={`${column}_${item.id}`}>{item.name}</label>
</div>
))}
<div className="check-form-box">
<input
type="checkbox"
id={`${column}Etc`}
checked={roofInfo?.[`${column}Etc` as keyof SurveyDetailInfo] !== null}
disabled={mode === 'READ'}
onChange={handleOtherCheckbox}
/>
<label htmlFor={`${column}Etc`}> ()</label>
</div>
</div>
<div className="data-input">
<input
type="text"
className="input-frame"
placeholder="-"
value={otherValue}
onChange={handleOtherInputChange}
disabled={mode === 'READ' || !isOtherCheck}
/>
</div>
</>
)
}

View File

@ -7,7 +7,7 @@ import { useRouter } from 'next/navigation'
import SearchForm from './SearchForm'
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
import { useSessionStore } from '@/store/session'
import { SurveyBasicInfo } from '@/types/Survey'
import type { SurveyBasicInfo } from '@/types/Survey'
export default function ListTable() {
const router = useRouter()

View File

@ -17,7 +17,6 @@ export default function SearchForm({ memberRole, userId }: { memberRole: string;
}
setKeyword(searchKeyword)
setSearchOption(option)
// onItemsInit()
}
const searchOptions = memberRole === 'Partner' ? SEARCH_OPTIONS_PARTNERS : SEARCH_OPTIONS
@ -38,7 +37,6 @@ export default function SearchForm({ memberRole, userId }: { memberRole: string;
if (e.target.value === 'all') {
setKeyword('')
setSearchKeyword('')
// onItemsInit()
setSearchOption('all')
setOption('all')
} else {
@ -80,7 +78,6 @@ export default function SearchForm({ memberRole, userId }: { memberRole: string;
checked={isMySurvey === userId}
onChange={() => {
setIsMySurvey(isMySurvey === userId ? null : userId)
// onItemsInit()
}}
/>
<label htmlFor="ch01"></label>
@ -94,7 +91,6 @@ export default function SearchForm({ memberRole, userId }: { memberRole: string;
value={sort}
onChange={(e) => {
setSort(e.target.value as 'created' | 'updated')
// onItemsInit()
}}
>
<option value="created"></option>

View File

@ -7,7 +7,7 @@ import { useSessionStore } from '@/store/session'
import { useMemo } from 'react'
import { AxiosResponse } from 'axios'
const requiredFields = [
export const requiredFields = [
{
field: 'installationSystem',
name: '設置希望システム',
@ -67,7 +67,7 @@ export function useServey(id?: number): {
createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => void
updateSurvey: (survey: SurveyRegistRequest) => void
deleteSurvey: () => Promise<boolean>
submitSurvey: () => void
submitSurvey: (saveId?: number) => void
validateSurveyDetail: (surveyDetail: SurveyDetailRequest) => string
getZipCode: (zipCode: string) => Promise<ZipCode[] | null>
refetchSurveyList: () => void
@ -77,7 +77,7 @@ export function useServey(id?: number): {
const { session } = useSessionStore()
const {
data,
data: surveyListData,
isLoading: isLoadingSurveyList,
refetch: refetchSurveyList,
} = useQuery({
@ -100,18 +100,17 @@ export function useServey(id?: number): {
enabled: session?.isLoggedIn,
})
const surveyData = useMemo(() => {
if (!data) return {}
if (!surveyListData) return { count: 0, data: [] }
return {
data: data.data,
count: data.count,
...surveyListData,
}
}, [data])
}, [surveyListData])
const { data: surveyDetail, isLoading: isLoadingSurveyDetail } = useQuery({
queryKey: ['survey', id],
queryFn: async () => {
if (id === undefined) throw new Error('id is required')
if (id === null) return null
if (id === null || isNaN(id)) return null
const resp = await axiosInstance(null).get<SurveyBasicInfo>(`/api/survey-sales/${id}`)
return resp.data
},
@ -132,6 +131,7 @@ export function useServey(id?: number): {
const { mutate: updateSurvey, isPending: isUpdatingSurvey } = useMutation({
mutationFn: async (survey: SurveyRegistRequest) => {
console.log('updateSurvey, survey:: ', survey)
if (id === undefined) throw new Error('id is required')
const resp = await axiosInstance(null).put<SurveyRegistRequest>(`/api/survey-sales/${id}`, survey)
return resp.data
@ -166,9 +166,10 @@ export function useServey(id?: number): {
})
const { mutateAsync: submitSurvey } = useMutation({
mutationFn: async () => {
if (id === undefined) throw new Error('id is required')
const resp = await axiosInstance(null).patch<boolean>(`/api/survey-sales/${id}`, {
mutationFn: async (saveId?: number) => {
const submitId = saveId ?? id
if (!submitId) throw new Error('id is required')
const resp = await axiosInstance(null).patch<boolean>(`/api/survey-sales/${submitId}`, {
submit: true,
})
return resp.data
@ -180,12 +181,22 @@ export function useServey(id?: number): {
})
const validateSurveyDetail = (surveyDetail: SurveyDetailRequest) => {
const etcFields = ['installationSystem', 'constructionYear', 'rafterSize', 'rafterPitch', 'waterproofMaterial', 'structureOrder'] as const
const etcFields = [
'installationSystem',
'constructionYear',
'rafterSize',
'rafterPitch',
'waterproofMaterial',
'structureOrder',
'insulationPresence',
] as const
const emptyField = requiredFields.find((field) => {
if (etcFields.includes(field.field as (typeof etcFields)[number])) {
return (
surveyDetail[field.field as keyof SurveyDetailRequest] === null && surveyDetail[`${field.field}_ETC` as keyof SurveyDetailRequest] === ''
surveyDetail[field.field as keyof SurveyDetailRequest] === null &&
(surveyDetail[`${field.field}Etc` as keyof SurveyDetailRequest] === null ||
surveyDetail[`${field.field}Etc` as keyof SurveyDetailRequest]?.toString().trim() === '')
)
} else {
return surveyDetail[field.field as keyof SurveyDetailRequest] === null
@ -197,7 +208,7 @@ export function useServey(id?: number): {
return 'contractCapacityUnit'
}
return emptyField?.name || ''
return emptyField?.field || ''
}
const getZipCode = async (zipCode: string): Promise<ZipCode[] | null> => {
@ -213,7 +224,7 @@ export function useServey(id?: number): {
}
return {
surveyList: surveyData,
surveyList: surveyData.data,
surveyDetail: surveyDetail as SurveyBasicInfo | null,
isLoadingSurveyList,
isLoadingSurveyDetail,

View File

@ -84,10 +84,6 @@ const transformObjectKeys = (obj: any): any => {
return obj
}
export const camelToSnake = (str: string): string => {
return str.replace(/([A-Z])/g, (group) => `_${group.toLowerCase()}`)
}
const snakeToCamel = (str: string): string => {
return str.toLowerCase().replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''))
}