feat: add survey roof-info validation when Create, Update
- 조사 매물 생성, 수정 시 각 컬럼 필수값 validation 추가 - 조사 매물 수정 페이지에서 기타 옵션 선택 시 값 초기화 되도록 구현
This commit is contained in:
parent
dfd5ba419b
commit
2397b7f144
@ -1,20 +1,20 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
const body = await request.json()
|
||||
const { id } = await context.params
|
||||
// export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
// const body = await request.json()
|
||||
// const { id } = await context.params
|
||||
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
// // @ts-ignore
|
||||
// const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
// where: { id: Number(id) },
|
||||
// data: {
|
||||
// detail_info: {
|
||||
// create: body,
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
// }
|
||||
|
||||
export async function GET(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
@ -80,12 +80,45 @@ export async function DELETE(request: Request, context: { params: { id: string }
|
||||
|
||||
export async function PATCH(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
const body = await request.json()
|
||||
if (body.submit) {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
submission_date: new Date(),
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const hasDetails = await prisma.SD_SERVEY_SALES_DETAIL_INFO.findUnique({
|
||||
where: { basic_info_id: Number(id) },
|
||||
})
|
||||
if (hasDetails) {
|
||||
//@ts-ignore
|
||||
const result = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
updated_at: new Date(),
|
||||
detail_info: {
|
||||
update: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json(result)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,15 +30,19 @@ export default function DetailForm() {
|
||||
}
|
||||
}, [tab, setBasicInfoSelected, setRoofInfoSelected, surveyDetail])
|
||||
|
||||
console.log('isTemporary', isTemporary)
|
||||
console.log('surveyDetail', surveyDetail)
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isTemporary) {
|
||||
alert('SAVE FIRST')
|
||||
return
|
||||
}
|
||||
if (confirm('submit?')) {
|
||||
if (surveyDetail?.id) {
|
||||
// TODO: 제출 페이지 추가
|
||||
alert('SUBMIT POPUP')
|
||||
await submitSurvey()
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ const defaultBasicInfoForm: SurveyBasicRequest = {
|
||||
representative: '',
|
||||
store: null,
|
||||
construction_point: null,
|
||||
investigation_date: null,
|
||||
investigation_date: new Date().toLocaleDateString('en-CA'),
|
||||
building_name: null,
|
||||
customer_name: null,
|
||||
post_code: null,
|
||||
@ -138,7 +138,7 @@ export default function BasicForm() {
|
||||
type="date"
|
||||
className="date-frame"
|
||||
id="investigation_date"
|
||||
value={basicInfoData.investigation_date ?? new Date().toISOString().split('T')[0]}
|
||||
value={basicInfoData.investigation_date ?? ''}
|
||||
onChange={(e) => handleChange('investigation_date', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { SurveyDetailRequest } from '@/types/Survey'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const supplementary_facilities = [
|
||||
{ id: 1, name: 'エコキュート' }, //에코큐트
|
||||
@ -29,11 +29,22 @@ export default function MultiCheckbox({
|
||||
const [isOtherChecked, setIsOtherChecked] = useState(false)
|
||||
const [otherValue, setOtherValue] = useState('')
|
||||
|
||||
const handleCheckbox = (dataIndex: number) => {
|
||||
const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
const makeNumArr = (value: string) => {
|
||||
return value
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsOtherChecked(true)
|
||||
setOtherValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
|
||||
const handleCheckbox = (dataIndex: number) => {
|
||||
const value = makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? ''))
|
||||
|
||||
let newValue: string[]
|
||||
if (value.includes(String(dataIndex))) {
|
||||
@ -63,11 +74,7 @@ export default function MultiCheckbox({
|
||||
|
||||
const handleOtherCheckbox = () => {
|
||||
if (column === 'roof_material') {
|
||||
const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0)
|
||||
|
||||
const value = makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? ''))
|
||||
// 현재 선택된 항목 수
|
||||
const currentSelected = value.length
|
||||
|
||||
@ -119,10 +126,7 @@ export default function MultiCheckbox({
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`${column}_ch${item.id}`}
|
||||
checked={String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.includes(String(item.id))}
|
||||
checked={makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')).includes(String(item.id))}
|
||||
onChange={() => handleCheckbox(item.id)}
|
||||
/>
|
||||
<label htmlFor={`${column}_ch${item.id}`}>{item.name}</label>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { SurveyDetailRequest } from '@/types/Survey'
|
||||
|
||||
type RadioEtcKeys = 'house_structure' | 'rafter_material' | 'waterproof_material' | 'insulation_presence'
|
||||
@ -58,6 +58,13 @@ export default function RadioEtc({
|
||||
const [isEtcSelected, setIsEtcSelected] = useState(false)
|
||||
const [etcValue, setEtcValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsEtcSelected(true)
|
||||
setEtcValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
|
||||
const handleRadioChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
if (column === 'insulation_presence') {
|
||||
@ -110,7 +117,7 @@ export default function RadioEtc({
|
||||
))}
|
||||
{column !== 'insulation_presence' && (
|
||||
<div className="radio-form-box mb10">
|
||||
<input type="radio" name={column} id={`${column}`} value="etc" onChange={handleRadioChange} />
|
||||
<input type="radio" name={column} id={`${column}_etc`} value="etc" onChange={handleRadioChange} checked={isEtcSelected} />
|
||||
<label htmlFor={`${column}_etc`}>その他 (直接入力)</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -65,15 +65,26 @@ export default function RoofInfoForm() {
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyDetail?.detail_info) {
|
||||
const { id, updated_at, created_at, ...rest } = surveyDetail.detail_info
|
||||
const { id, updated_at, created_at, basic_info_id, ...rest } = surveyDetail.detail_info
|
||||
setDetailInfoData(rest)
|
||||
}
|
||||
}, [surveyDetail])
|
||||
|
||||
const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => {
|
||||
if (typeof value === 'string') {
|
||||
const numberValue = value === '' ? null : Number(value)
|
||||
setDetailInfoData({ ...detailInfoData, [key]: numberValue })
|
||||
if (key === 'roof_slope' || key === 'open_field_plate_thickness') {
|
||||
const stringValue = value.toString()
|
||||
if (stringValue.length > 4) {
|
||||
alert('over db size')
|
||||
return
|
||||
}
|
||||
if (stringValue.includes('.')) {
|
||||
const decimalPlaces = stringValue.split('.')[1].length
|
||||
if (decimalPlaces > 1) {
|
||||
alert('小数点以下1桁までしか許されません。')
|
||||
return
|
||||
}
|
||||
}
|
||||
setDetailInfoData({ ...detailInfoData, [key]: value.toString() })
|
||||
} else {
|
||||
setDetailInfoData({ ...detailInfoData, [key]: value })
|
||||
}
|
||||
@ -95,12 +106,22 @@ export default function RoofInfoForm() {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: 조사매물 저장 요구사항 정립 이후 수정 필요
|
||||
const handleSave = async () => {
|
||||
if (id) {
|
||||
const emptyField = validateSurveyDetail(detailInfoData)
|
||||
if (emptyField.trim() === '') {
|
||||
createSurveyDetail({ surveyId: Number(id), surveyDetail: detailInfoData })
|
||||
const updatedBasicInfoData = {
|
||||
detail_info: detailInfoData,
|
||||
}
|
||||
try {
|
||||
createSurveyDetail({
|
||||
surveyId: Number(id),
|
||||
surveyDetail: updatedBasicInfoData,
|
||||
})
|
||||
} catch (error) {
|
||||
throw new Error('failed to create survey detail: ' + error)
|
||||
}
|
||||
alert('created successfully')
|
||||
router.push(`/survey-sale`)
|
||||
} else {
|
||||
alert(emptyField + ' is required')
|
||||
@ -126,10 +147,11 @@ export default function RoofInfoForm() {
|
||||
<div className="data-input-form-tit">電気契約容量</div>
|
||||
<div className="data-input mb5">
|
||||
<input
|
||||
type="text"
|
||||
type="number"
|
||||
inputMode="decimal"
|
||||
className="input-frame"
|
||||
value={detailInfoData.contract_capacity?.split(' ')[0] ?? ''}
|
||||
onChange={(e) => handleTextInput('contract_capacity', e.target.value)}
|
||||
onChange={(e) => handleNumberInput('contract_capacity', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="data-input">
|
||||
@ -138,7 +160,11 @@ export default function RoofInfoForm() {
|
||||
name="contract_capacity_unit"
|
||||
id="contract_capacity_unit"
|
||||
onChange={(e) => handleUnitInput(e.target.value)}
|
||||
value={detailInfoData.contract_capacity?.split(' ')[1] ?? ''}
|
||||
>
|
||||
<option value="" hidden>
|
||||
選択してください
|
||||
</option>
|
||||
<option value="kVA">kVA</option>
|
||||
<option value="A">A</option>
|
||||
</select>
|
||||
@ -179,10 +205,12 @@ export default function RoofInfoForm() {
|
||||
<div className="data-input-form-tit">屋根の斜面</div>
|
||||
<div className="data-input flex">
|
||||
<input
|
||||
type="text"
|
||||
type="number"
|
||||
step={0.1}
|
||||
inputMode="decimal"
|
||||
className="input-frame"
|
||||
value={detailInfoData.roof_slope ?? ''}
|
||||
onChange={(e) => handleTextInput('roof_slope', e.target.value)}
|
||||
onChange={(e) => handleNumberInput('roof_slope', e.target.value)}
|
||||
/>
|
||||
<span>寸</span>
|
||||
</div>
|
||||
@ -205,7 +233,7 @@ export default function RoofInfoForm() {
|
||||
name="rafter_direction"
|
||||
id="rafter_direction_1"
|
||||
value={1}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', e.target.value)}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', Number(e.target.value))}
|
||||
checked={detailInfoData.rafter_direction === 1}
|
||||
/>
|
||||
<label htmlFor="rafter_direction_1">垂直垂木</label>
|
||||
@ -216,7 +244,7 @@ export default function RoofInfoForm() {
|
||||
name="rafter_direction"
|
||||
id="rafter_direction_2"
|
||||
value={2}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', e.target.value)}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', Number(e.target.value))}
|
||||
checked={detailInfoData.rafter_direction === 2}
|
||||
/>
|
||||
<label htmlFor="rafter_direction_2">水平垂木</label>
|
||||
@ -232,10 +260,12 @@ export default function RoofInfoForm() {
|
||||
</div>
|
||||
<div className="data-input flex">
|
||||
<input
|
||||
type="text"
|
||||
type="number"
|
||||
step={0.1}
|
||||
inputMode="decimal"
|
||||
className="input-frame"
|
||||
value={detailInfoData.open_field_plate_thickness ?? ''}
|
||||
onChange={(e) => handleTextInput('open_field_plate_thickness', e.target.value)}
|
||||
onChange={(e) => handleNumberInput('open_field_plate_thickness', e.target.value)}
|
||||
/>
|
||||
<span>mm</span>
|
||||
</div>
|
||||
|
||||
@ -165,25 +165,28 @@ export default function SelectBoxForm({
|
||||
const [etcValue, setEtcValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
setEtcValue(detailInfoData[`${column}_etc`] ?? '')
|
||||
}, [detailInfoData[`${column}_etc`]])
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsEtcSelected(true)
|
||||
setEtcValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
|
||||
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = e.target.value
|
||||
if (column === 'installation_availability' || column === 'construction_year') {
|
||||
setIsEtcSelected(value === '2') // 既築(2) 또는 未確認(2) 선택 시 input 활성화
|
||||
setIsEtcSelected(value === '2') // 건축 연수 & 설치 가능 여부 컬럼 2번 선택 시 input 활성화
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
[column]: Number(value),
|
||||
})
|
||||
} else if (value === 'etc') {
|
||||
setIsEtcSelected(true)
|
||||
setIsEtcSelected(true) // 기타 선택 시 input 활성화
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
[column]: null,
|
||||
})
|
||||
} else {
|
||||
setIsEtcSelected(false)
|
||||
setIsEtcSelected(false) // 기타 선택 해제 시 input 비활성화
|
||||
setEtcValue('')
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
@ -207,7 +210,19 @@ export default function SelectBoxForm({
|
||||
<div className="data-input-form-bx">
|
||||
<div className={font[column]}>{translateJapanese[column]}</div>
|
||||
<div className="data-input mb5">
|
||||
<select className="select-form" name={column} id={column} onChange={handleSelectChange} value={detailInfoData[column] ?? ''}>
|
||||
<select
|
||||
className="select-form"
|
||||
name={column}
|
||||
id={column}
|
||||
onChange={handleSelectChange}
|
||||
value={
|
||||
detailInfoData[column]
|
||||
? detailInfoData[column]
|
||||
: detailInfoData[`${column}_etc`]
|
||||
? 'etc'
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<option value="" hidden>
|
||||
선택해주세요
|
||||
</option>
|
||||
@ -225,7 +240,13 @@ export default function SelectBoxForm({
|
||||
className="input-frame"
|
||||
value={etcValue ?? ''}
|
||||
onChange={handleEtcInputChange}
|
||||
disabled={column === 'installation_availability' || column === 'construction_year' ? !Boolean(detailInfoData[column]) : !isEtcSelected}
|
||||
disabled={
|
||||
column === 'installation_availability'
|
||||
? !Boolean(detailInfoData[column])
|
||||
: column === 'construction_year'
|
||||
? detailInfoData[column] !== 2 // 既築(2)가 아닐 때 비활성화
|
||||
: !isEtcSelected
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { SEARCH_OPTIONS_ENUM, SEARCH_OPTIONS_PARTNERS_ENUM, SORT_OPTIONS_ENUM } from "@/store/surveyFilterStore"
|
||||
|
||||
export type SurveyBasicInfo = {
|
||||
id: number
|
||||
representative: string
|
||||
@ -18,6 +20,7 @@ export type SurveyBasicInfo = {
|
||||
|
||||
export type SurveyDetailInfo = {
|
||||
id: number
|
||||
basic_info_id: number
|
||||
contract_capacity: string | null
|
||||
retail_company: string | null
|
||||
supplementary_facilities: string | null // number 배열
|
||||
@ -108,3 +111,7 @@ export type SurveyDetailRequest = {
|
||||
installation_availability_etc: string | null
|
||||
memo: string | null
|
||||
}
|
||||
|
||||
export type SurveyDetailCoverRequest = {
|
||||
detail_info: SurveyDetailRequest
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user