fix: fix rendering issue

- 매물 수정/작성 시 담당자명, 판매점명, 시공점명 랜더링 안되는 문제 해결
- T01 계정 수정 버튼 랜더링 안되는 문제 해결
This commit is contained in:
Dayoung 2025-06-04 13:28:42 +09:00
parent 0c27c19341
commit 50617c7b7f
4 changed files with 196 additions and 198 deletions

View File

@ -1,7 +1,6 @@
import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma'
import { convertToSnakeCase } from '@/utils/common-utils'
import { SessionData } from '@/types/Auth'
interface Survey {
SRL_NO: string

View File

@ -8,45 +8,34 @@ import { usePopupController } from '@/store/popupController'
import { useAddressStore } from '@/store/addressStore'
import { SessionData } from '@/types/Auth'
export default function BasicForm(props: {
interface BasicFormProps {
basicInfo: SurveyBasicRequest
setBasicInfo: (basicInfo: SurveyBasicRequest) => void
mode: Mode
session: SessionData
}) {
const { basicInfo, setBasicInfo, mode, session } = props
}
export default function BasicForm({ basicInfo, setBasicInfo, mode, session }: BasicFormProps) {
const { setBasicInfoSelected } = useSurveySaleTabState()
const [isFlip, setIsFlip] = useState<boolean>(true)
const { addressData } = useAddressStore()
const popupController = usePopupController()
useEffect(() => {
setBasicInfoSelected()
}, [])
// 주소 데이터가 변경될 때만 업데이트
useEffect(() => {
if (session?.isLoggedIn) {
setBasicInfo({
...basicInfo,
representative: session.userNm ?? '',
representativeId: session.userId ?? null,
store: session.storeNm ?? null,
storeId: session.storeId ?? null,
constructionPoint: session.builderNm ?? null,
constructionPointId: session.builderNo ?? null,
})
}
if (addressData) {
setBasicInfo({
...basicInfo,
postCode: addressData.post_code,
address: addressData.address,
addressDetail: addressData.address_detail,
})
}
}, [session, addressData])
if (!addressData) return
const popupController = usePopupController()
setBasicInfo({
...basicInfo,
postCode: addressData.post_code,
address: addressData.address,
addressDetail: addressData.address_detail,
})
}, [addressData])
return (
<>

View File

@ -7,79 +7,88 @@ import { useParams, useRouter, useSearchParams } from 'next/navigation'
import { requiredFields, useSurvey } from '@/hooks/useSurvey'
import { usePopupController } from '@/store/popupController'
export default function ButtonForm(props: {
interface ButtonFormProps {
mode: Mode
setMode: (mode: Mode) => void
data: { basic: SurveyBasicRequest; roof: SurveyDetailRequest }
}) {
// 라우터
const router = useRouter()
const { mode, setMode } = props
const { session } = useSessionStore()
data: {
basic: SurveyBasicRequest
roof: SurveyDetailRequest
}
}
interface PermissionState {
isSubmiter: boolean
isWriter: boolean
isReceiver: boolean
}
interface SaveData extends SurveyBasicRequest {
detailInfo: SurveyDetailRequest
}
export default function ButtonForm({ mode, setMode, data }: ButtonFormProps) {
const router = useRouter()
const { session } = useSessionStore()
const searchParams = useSearchParams()
const idParam = searchParams.get('id')
const params = useParams()
const routeId = params.id
const popupController = usePopupController()
// ------------------------------------------------------------
const [saveData, setSaveData] = useState({
...props.data.basic,
detailInfo: props.data.roof,
const [saveData, setSaveData] = useState<SaveData>({
...data.basic,
detailInfo: data.roof,
})
// --------------------------------------------------------------
// 권한
// 제출권한 있는 회원
const [isSubmiter, setIsSubmiter] = useState(false)
// 작성자
const [isWriter, setIsWriter] = useState(false)
// 제출받은 대상자
const [isReceiver, setIsReceiver] = useState(false)
const isSubmit = props.data.basic.submissionStatus
const [permissions, setPermissions] = useState<PermissionState>({
isSubmiter: false,
isWriter: false,
isReceiver: false,
})
useEffect(() => {
if (session?.isLoggedIn) {
switch (session?.role) {
// T01 제출권한 없음
case 'T01':
setIsSubmiter(false)
setIsReceiver(!props.data.basic.submissionTargetId && !props.data.basic.submissionTargetNm)
break
// 1차 판매점(Order) + 2차 판매점(Musubi) => 같은 판매점 제출권한
case 'Admin':
case 'Admin_Sub':
setIsSubmiter(session.storeNm === props.data.basic.store && session.builderNo === props.data.basic.constructionPointId)
setIsReceiver(session?.storeId === props.data.basic.submissionTargetId)
break
// 시공권한 User(Musubi) + Partner => 같은 시공ID 제출권한
case 'Builder':
case 'Partner':
setIsSubmiter(session.builderNo === props.data.basic.constructionPointId)
break
default:
setIsSubmiter(false)
break
}
setIsWriter(session.userNm === props.data.basic.representative)
}
setSaveData({
...props.data.basic,
detailInfo: props.data.roof,
})
}, [session, props.data])
// ------------------------------------------------------------
// 저장/임시저장/수정
const isSubmit = data.basic.submissionStatus
const id = Number(routeId) ? Number(routeId) : Number(idParam)
const { deleteSurvey, updateSurvey, isDeletingSurvey, isUpdatingSurvey } = useSurvey(Number(id))
const { deleteSurvey, updateSurvey, isDeletingSurvey, isUpdatingSurvey } = useSurvey(id)
const { validateSurveyDetail, createSurvey, isCreatingSurvey } = useSurvey()
useEffect(() => {
if (!session?.isLoggedIn) return
const newPermissions = calculatePermissions(session, data.basic)
setPermissions(newPermissions)
setSaveData({
...data.basic,
detailInfo: data.roof,
})
}, [session, data])
const calculatePermissions = (session: any, basicData: SurveyBasicRequest): PermissionState => {
const isSubmiter = calculateSubmitPermission(session, basicData)
const isWriter = session.userNm === basicData.representative
const isReceiver = session?.storeId === basicData.submissionTargetId
return { isSubmiter, isWriter, isReceiver }
}
const calculateSubmitPermission = (session: any, basicData: SurveyBasicRequest): boolean => {
switch (session?.role) {
case 'T01':
return false
case 'Admin':
case 'Admin_Sub':
return session.storeNm === basicData.store && session.builderNo === basicData.constructionPointId
case 'Builder':
case 'Partner':
return session.builderNo === basicData.constructionPointId
default:
return false
}
}
const handleSave = (isTemporary: boolean, isSubmitProcess: boolean) => {
const emptyField = validateSurveyDetail(props.data.roof)
const emptyField = validateSurveyDetail(data.roof)
const hasEmptyField = emptyField?.trim() !== ''
if (isTemporary) {
@ -110,43 +119,47 @@ export default function ButtonForm(props: {
const focusInput = (field: keyof SurveyDetailInfo) => {
const input = document.getElementById(field)
if (input) {
input.focus()
}
input?.focus()
}
const saveProcess = async (emptyField: string | null, isSubmitProcess?: boolean) => {
if (emptyField?.trim() === '') {
if (idParam) {
await updateSurvey({ survey: saveData, isTemporary: false, storeId: session.storeId ?? '' })
if (!isUpdatingSurvey) {
router.push(`/survey-sale/${idParam}`)
}
} else {
const id = await createSurvey(saveData)
if (!isCreatingSurvey) {
router.push(`/survey-sale/${id}`)
}
}
if (isSubmitProcess) {
if (!isCreatingSurvey && !isUpdatingSurvey) {
await popupController.setSurveySaleSubmitPopup(true)
}
} else {
alert('保存されました。')
}
await handleSuccessfulSave(isSubmitProcess)
} 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)
}
handleFailedSave(emptyField)
}
}
// ------------------------------------------------------------
// 삭제/제출
const handleSuccessfulSave = async (isSubmitProcess?: boolean) => {
if (idParam) {
await updateSurvey({ survey: saveData, isTemporary: false, storeId: session.storeId ?? '' })
if (!isUpdatingSurvey) {
router.push(`/survey-sale/${idParam}`)
}
} else {
const id = await createSurvey(saveData)
if (!isCreatingSurvey) {
router.push(`/survey-sale/${id}`)
}
}
if (isSubmitProcess) {
if (!isCreatingSurvey && !isUpdatingSurvey) {
await popupController.setSurveySaleSubmitPopup(true)
}
} else {
alert('保存されました。')
}
}
const handleFailedSave = (emptyField: string | null) => {
if (emptyField?.includes('Unit')) {
alert('電気契約容量の単位を入力してください。')
} else {
alert(requiredFields.find((field) => field.field === emptyField)?.name + ' 項目が空です。')
}
focusInput(emptyField as keyof SurveyDetailInfo)
}
const handleDelete = async () => {
if (routeId) {
@ -161,10 +174,11 @@ export default function ButtonForm(props: {
}
const handleSubmit = async () => {
if (props.data.basic.srlNo?.startsWith('一時保存') && Number(routeId)) {
if (data.basic.srlNo?.startsWith('一時保存') && Number(routeId)) {
alert('一時保存されたデータは提出できません。')
return
}
if (Number(routeId)) {
window.neoConfirm('提出しますか?', async () => {
popupController.setSurveySaleSubmitPopup(true)
@ -176,21 +190,15 @@ export default function ButtonForm(props: {
}
}
// ------------------------------------------------------------
if (!session?.isLoggedIn) return null
if (!session?.isLoggedIn) {
return <></>
}
if (mode === 'READ' && isSubmit && isSubmiter) {
if (mode === 'READ' && isSubmit && permissions.isSubmiter) {
return (
<>
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
</div>
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
</div>
</>
</div>
)
}
@ -200,9 +208,11 @@ export default function ButtonForm(props: {
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
{(isSubmiter || (isReceiver && isSubmit)) && <EditButton setMode={setMode} id={id.toString()} mode={mode} />}
{(isWriter || (isReceiver && isSubmit)) && <DeleteButton handleDelete={handleDelete} />}
{!isSubmit && isSubmiter && <SubmitButton handleSubmit={handleSubmit} />}
{(permissions.isWriter || permissions.isSubmiter || (permissions.isReceiver && isSubmit)) && (
<EditButton setMode={setMode} id={id.toString()} />
)}
{(permissions.isWriter || (permissions.isReceiver && isSubmit)) && <DeleteButton handleDelete={handleDelete} />}
{!isSubmit && permissions.isSubmiter && <SubmitButton handleSubmit={handleSubmit} />}
</div>
</div>
)}
@ -211,9 +221,9 @@ export default function ButtonForm(props: {
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
<TempButton setMode={setMode} handleSave={() => handleSave(true, false)} />
<TempButton handleSave={() => handleSave(true, false)} />
<SaveButton handleSave={() => handleSave(false, false)} />
{session?.role === 'T01' || isSubmit ? <></> : <SubmitButton handleSubmit={handleSubmit} />}
{session?.role === 'T01' || isSubmit ? null : <SubmitButton handleSubmit={handleSubmit} />}
</div>
</div>
)}
@ -221,12 +231,11 @@ export default function ButtonForm(props: {
)
}
// 목록 버튼
function ListButton() {
// Button Components
const 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>
@ -234,12 +243,10 @@ function ListButton() {
)
}
function EditButton(props: { setMode: (mode: Mode) => void; id: string; mode: Mode }) {
const { setMode, id, mode } = props
const EditButton = ({ setMode, id }: { setMode: (mode: Mode) => void; id: string }) => {
const router = useRouter()
return (
<div className="btn-bx">
{/* 수정 */}
<button
className="btn-frame n-blue icon"
onClick={() => {
@ -253,59 +260,34 @@ function EditButton(props: { setMode: (mode: Mode) => void; id: string; mode: Mo
)
}
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>
</>
)
}
const SubmitButton = ({ handleSubmit }: { handleSubmit: () => void }) => (
<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>
)
}
const DeleteButton = ({ handleDelete }: { handleDelete: () => void }) => (
<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>
)
}
const SaveButton = ({ handleSave }: { handleSave: () => void }) => (
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleSave}>
<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={() => {
handleSave(true)
}}
>
<i className="btn-arr"></i>
</button>
</div>
)
}
const TempButton = ({ handleSave }: { handleSave: () => void }) => (
<div className="btn-bx">
<button className="btn-frame n-blue icon" onClick={handleSave}>
<i className="btn-arr"></i>
</button>
</div>
)

View File

@ -70,7 +70,6 @@ const basicInfoForm: SurveyBasicRequest = {
export default function DetailForm() {
const idParam = useSearchParams().get('id')
const routeId = useParams().id
const router = useRouter()
const modeset = Number(routeId) ? 'READ' : idParam ? 'EDIT' : 'CREATE'
@ -80,26 +79,57 @@ export default function DetailForm() {
const { session } = useSessionStore()
const [mode, setMode] = useState<Mode>(modeset)
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(basicInfoForm)
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(() => ({
...basicInfoForm,
representative: session?.userNm ?? '',
representativeId: session?.userId ?? null,
store: session?.storeNm ?? null,
storeId: session?.storeId ?? null,
constructionPoint: session?.builderNm ?? null,
constructionPointId: session?.builderNo ?? null,
}))
const [roofInfoData, setRoofInfoData] = useState<SurveyDetailRequest>(roofInfoForm)
// 세션 데이터가 변경될 때 기본 정보 업데이트
useEffect(() => {
if (isLoadingSurveyDetail) return
if (surveyDetail === null) {
if (!session?.isLoggedIn) return
setBasicInfoData((prev) => ({
...prev,
representative: session.userNm ?? '',
representativeId: session.userId ?? null,
store: session.storeNm ?? null,
storeId: session.storeId ?? null,
constructionPoint: session.builderNm ?? null,
constructionPointId: session.builderNo ?? null,
}))
}, [session])
// 설문 데이터 로딩 및 업데이트
useEffect(() => {
if (isLoadingSurveyDetail || !session?.isLoggedIn) return
if (surveyDetail === null && mode !== 'CREATE') {
alert('権限がありません。')
router.replace('/survey-sale')
return
}
if (surveyDetail && (mode === 'EDIT' || mode === 'READ')) {
const { id, uptDt, regDt, detailInfo, ...rest } = surveyDetail
setBasicInfoData(rest)
setBasicInfoData((prev) => ({
...prev,
...rest,
}))
if (detailInfo) {
const { id, uptDt, regDt, basicInfoId, ...rest } = detailInfo
setRoofInfoData(rest)
if (validateSurveyDetail(rest).trim() !== '') {
// validation logic here if needed
}
}
}
}, [surveyDetail, id, mode, session])
}, [surveyDetail, mode, session?.isLoggedIn, isLoadingSurveyDetail])
const data = {
basic: basicInfoData,
@ -111,9 +141,7 @@ export default function DetailForm() {
return (
<>
<div className="sale-detail-toggle-wrap">
{/* 기본정보 */}
<BasicForm basicInfo={basicInfoData} setBasicInfo={setBasicInfoData} mode={mode} session={session} />
{/* 전기/지붕정보 */}
<RoofForm roofInfo={roofInfoData} setRoofInfo={setRoofInfoData} mode={mode} />
<ButtonForm {...buttonFormProps} />
</div>