diff --git a/apps/as/src/entities/elapr/model/elapr-prgrs-stts-interface.ts b/apps/as/src/entities/elapr/model/elapr-prgrs-stts-interface.ts index 7c24ff8eb..61d0f2cdf 100644 --- a/apps/as/src/entities/elapr/model/elapr-prgrs-stts-interface.ts +++ b/apps/as/src/entities/elapr/model/elapr-prgrs-stts-interface.ts @@ -51,7 +51,6 @@ export interface ElaprPrgrsSttsMyDocSearchParams extends ElaprPrgrsSttsSearchDto export interface ElaprProcessRequestDto { atrzPrgrsSttsComCd: string; rjctRsn?: string; - finalApprovalYn?: boolean; } /** 백엔드 ElaprApproverChangeRequestDto */ diff --git a/apps/as/src/entities/sewer-bsc-plan/api/queries.ts b/apps/as/src/entities/sewer-bsc-plan/api/queries.ts index 063e1bd00..a4488a3ed 100644 --- a/apps/as/src/entities/sewer-bsc-plan/api/queries.ts +++ b/apps/as/src/entities/sewer-bsc-plan/api/queries.ts @@ -5,6 +5,8 @@ import { ENDPOINT } from "@/shared/config"; import type { SewerBscPlanApplySaveRequestDto, SewerBscPlanSaveRequestDto, +} from "../model/sewer-bsc-plan-apply-payload-schema"; +import type { SewerBscPlanSearchRequestDto, SewerBscPlanUpdateRequestDto, } from "../model/sewer-bsc-plan-interface"; diff --git a/apps/as/src/entities/sewer-bsc-plan/api/sewer-bsc-plan-service.ts b/apps/as/src/entities/sewer-bsc-plan/api/sewer-bsc-plan-service.ts index 49c770d85..62aced93b 100644 --- a/apps/as/src/entities/sewer-bsc-plan/api/sewer-bsc-plan-service.ts +++ b/apps/as/src/entities/sewer-bsc-plan/api/sewer-bsc-plan-service.ts @@ -4,10 +4,12 @@ import type { PageResponse } from "@/shared/model"; import type { SewerBscPlanApplySaveRequestDto, + SewerBscPlanSaveRequestDto, +} from "../model/sewer-bsc-plan-apply-payload-schema"; +import type { SewerBscPlanApplySaveResponseDto, SewerBscPlanDetResponseDto, SewerBscPlanResponseDto, - SewerBscPlanSaveRequestDto, SewerBscPlanSearchRequestDto, SewerBscPlanService, SewerBscPlanUpdateRequestDto, diff --git a/apps/as/src/entities/sewer-bsc-plan/index.ts b/apps/as/src/entities/sewer-bsc-plan/index.ts index eecf354cd..8d560a60c 100644 --- a/apps/as/src/entities/sewer-bsc-plan/index.ts +++ b/apps/as/src/entities/sewer-bsc-plan/index.ts @@ -13,35 +13,52 @@ export { } from "./api/queries"; export { sewerBscPlanService } from "./api/sewer-bsc-plan-service"; export type { - AtflMngClientResponseDto, CklstResultItem, - CklstRvwRsltTreeDto, EsntlAtchFileItem, - EsntlAtchFileMngTreeResponseDto, SewerBscPlanApplySaveRequestDto, - SewerBscPlanApplySaveResponseDto, SewerBscPlanBizCstItem, + SewerBscPlanDtlSaveRequestDto, + SewerBscPlanFnrsCstItem, + SewerBscPlanOtlnItem, + SewerBscPlanRvwOpnnItem, + SewerBscPlanSaveRequestDto, + SewerBscPlanSplmntDmndItem, + SewerBscPlanTmplntDtlItem, + SewerBscPlanTmplntItem, + SewerBscPlanTmplntTmprStrgItem, +} from "./model/sewer-bsc-plan-apply-payload-schema"; +export { + sewerBscPlanApplySaveSchema, + sewerBscPlanBizCstItemSchema, + sewerBscPlanCklstResultItemSchema, + sewerBscPlanDtlSaveSchema, + sewerBscPlanEsntlAtchFileItemSchema, + sewerBscPlanFnrsCstItemSchema, + sewerBscPlanOtlnItemSchema, + sewerBscPlanRvwOpnnItemSchema, + sewerBscPlanSaveSchema, + sewerBscPlanSplmntDmndItemSchema, + sewerBscPlanTmplntDtlItemSchema, + sewerBscPlanTmplntItemSchema, + sewerBscPlanTmplntTmprStrgItemSchema, +} from "./model/sewer-bsc-plan-apply-payload-schema"; +export type { + AtflMngClientResponseDto, + CklstRvwRsltTreeDto, + EsntlAtchFileMngTreeResponseDto, + SewerBscPlanApplySaveResponseDto, SewerBscPlanBizCstResponseDto, SewerBscPlanDetResponseDto, SewerBscPlanDtlResponseDto, - SewerBscPlanDtlSaveRequestDto, - SewerBscPlanFnrsCstItem, SewerBscPlanFnrsCstResponseDto, - SewerBscPlanOtlnItem, SewerBscPlanOtlnResponseDto, SewerBscPlanResponseDto, - SewerBscPlanRvwOpnnItem, SewerBscPlanRvwOpnnResponseDto, - SewerBscPlanSaveRequestDto, SewerBscPlanSearchRequestDto, SewerBscPlanService, - SewerBscPlanSplmntDmndItem, SewerBscPlanSplmntDmndResponseDto, - SewerBscPlanTmplntDtlItem, SewerBscPlanTmplntDtlResponseDto, - SewerBscPlanTmplntItem, SewerBscPlanTmplntResponseDto, - SewerBscPlanTmplntTmprStrgItem, SewerBscPlanTmplntTmprStrgResponseDto, SewerBscPlanUpdateRequestDto, } from "./model/sewer-bsc-plan-interface"; diff --git a/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-apply-payload-schema.ts b/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-apply-payload-schema.ts new file mode 100644 index 000000000..0060467b7 --- /dev/null +++ b/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-apply-payload-schema.ts @@ -0,0 +1,173 @@ +import { z } from "zod"; + +/** 백엔드 SewerBscPlanSaveRequestDto */ +export const sewerBscPlanSaveSchema = z.object({ + sewerBscPlanDocNo: z.string().optional(), + admdstCd: z.string().optional(), + envoSeComCd: z.string().optional(), + sewerBscPlanSeComCd: z.string().optional(), + sewerBscPlanNm: z.string().optional(), + taskSeComCd: z.string().optional(), + prgrsSttsComCd: z.string().optional(), +}); + +/** 백엔드 SewerBscPlanDtlSaveRequestDto */ +export const sewerBscPlanDtlSaveSchema = z.object({ + sewerBscPlanDocNo: z.string().optional(), + chgCycl: z.number().optional(), + aplcntNm: z.string().optional(), + aplcntMblTelno: z.string().optional(), + aplcntOgdpNm: z.string().optional(), + aplcntEmlAddr: z.string().optional(), + dsgnrNm: z.string().optional(), + dsgnrTelno: z.string().optional(), + dsgnrOgdpNm: z.string().optional(), + dsgnrEmlAddr: z.string().optional(), + planYr: z.string().optional(), + crtrYr: z.string().optional(), + bizOtlnCn: z.string().optional(), + frlvlGoalYr: z.string().optional(), + sclvlGoalYr: z.string().optional(), + thlvlGoalYr: z.string().optional(), + folvlGoalYr: z.string().optional(), + atchFileGroupId: z.string().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanOtlnItem */ +export const sewerBscPlanOtlnItemSchema = z.object({ + planStpNo: z.number().optional(), + sewerPlanPpltnCnt: z.number().optional(), + sewerPrcsPpltnCnt: z.number().optional(), + sewerPsplyRt: z.number().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.CklstResultItem */ +export const sewerBscPlanCklstResultItemSchema = z.object({ + cklstRvwSn: z.number().optional(), + cklstArtclSn: z.number().optional(), + cklstSn: z.number().optional(), + rvwStpComCd: z.string(), + selected: z.string().optional(), + picRvwCn: z.string().nullish(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanFnrsCstItem */ +export const sewerBscPlanFnrsCstItemSchema = z.object({ + planStpNo: z.number().optional(), + gvexpAmt: z.number().optional(), + lcexpAmt: z.number().optional(), + orgprBdchgAmt: z.number().optional(), + etcAmt: z.number().optional(), + sumAmt: z.number().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanSplmntDmndItem */ +export const sewerBscPlanSplmntDmndItemSchema = z.object({ + splmntDmndSn: z.number().nullish(), + splmntArtclComCd: z.string().optional(), + splmntDmndCn: z.string().optional(), + splmntDmndDt: z.string().optional(), + splmntDmndAtchFileGroupId: z.string().optional(), + splmntCn: z.string().optional(), + splmntPicNm: z.string().optional(), + splmntCmptnDt: z.string().optional(), + splmntAtchFileGroupId: z.string().optional(), + cmptnYn: z.string().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanRvwOpnnItem */ +export const sewerBscPlanRvwOpnnItemSchema = z.object({ + rvwOpnnSn: z.number().optional(), + opnrSeComCd: z.string().optional(), + opnrNm: z.string().optional(), + rvwRsltCn: z.string().optional(), + rvwRsltAnmlyYn: z.string().optional(), + atchFileGroupId: z.string().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.EsntlAtchFileItem */ +export const sewerBscPlanEsntlAtchFileItemSchema = z.object({ + esntlAtchFileId: z.string().optional(), + atchFileGroupId: z.string().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanBizCstItem */ +export const sewerBscPlanBizCstItemSchema = z.object({ + planStpNo: z.number().optional(), + acntgSeCd: z.string().optional(), + cstrnAmt: z.number().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntDtlItem */ +export const sewerBscPlanTmplntDtlItemSchema = z.object({ + planStpNo: z.number().optional(), + swtrmtFcltCd: z.string().optional(), + prcsZoneArea: z.number().optional(), + swtrmtPpltnCnt: z.number().optional(), + planSwqty: z.number().optional(), + fcltCpct: z.number().optional(), + sprsysDrtwPplLen: z.number().optional(), + sprsysExlnPplLen: z.number().optional(), + cbsysPplLen: z.number().optional(), + icptgPplLen: z.number().optional(), + drgCnt: z.number().optional(), + drtwRlyPumpCnt: z.number().optional(), + drtwPumpCnt: z.number().optional(), + exlnPumpCnt: z.number().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntItem */ +export const sewerBscPlanTmplntItemSchema = z.object({ + swtrmtFcltCd: z.string().optional(), + atchFileGroupId: z.string().optional(), + sewerBscPlanTmplntDtlList: z.array(sewerBscPlanTmplntDtlItemSchema).optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntTmprStrgItem */ +export const sewerBscPlanTmplntTmprStrgItemSchema = z.object({ + dtStrgDt: z.string().optional(), + swtrmtFcltCd: z.string().optional(), + prcsZoneArea: z.number().optional(), + swtrmtPpltnCnt: z.number().optional(), + planSwqty: z.number().optional(), + fcltCpct: z.number().optional(), + sprsysDrtwPplLen: z.number().optional(), + sprsysExlnPplLen: z.number().optional(), + cbsysPplLen: z.number().optional(), + icptgPplLen: z.number().optional(), + drgCnt: z.number().optional(), + drtwRlyPumpCnt: z.number().optional(), + drtwPumpCnt: z.number().optional(), + exlnPumpCnt: z.number().optional(), +}); + +/** 백엔드 SewerBscPlanApplySaveRequestDto */ +export const sewerBscPlanApplySaveSchema = z.object({ + techReViewSaveCd: z.string().optional(), + rvwStpCd: z.string().optional(), + sewerBscPlan: sewerBscPlanSaveSchema, + sewerBscPlanDtl: sewerBscPlanDtlSaveSchema, + sewerBscPlanOtlnList: z.array(sewerBscPlanOtlnItemSchema).optional(), + cklstResultList: z.array(sewerBscPlanCklstResultItemSchema).optional(), + fnrsCstList: z.array(sewerBscPlanFnrsCstItemSchema).optional(), + splmntDmndList: z.array(sewerBscPlanSplmntDmndItemSchema).optional(), + rvwOpnnList: z.array(sewerBscPlanRvwOpnnItemSchema).optional(), + esntlAtchFileList: z.array(sewerBscPlanEsntlAtchFileItemSchema).optional(), + bizCstList: z.array(sewerBscPlanBizCstItemSchema).optional(), + sewerBscPlanTmplntList: z.array(sewerBscPlanTmplntItemSchema).optional(), + tmplntTmprStrgList: z.array(sewerBscPlanTmplntTmprStrgItemSchema).optional(), +}); + +export type SewerBscPlanSaveRequestDto = z.infer; +export type SewerBscPlanDtlSaveRequestDto = z.infer; +export type SewerBscPlanOtlnItem = z.infer; +export type CklstResultItem = z.infer; +export type SewerBscPlanFnrsCstItem = z.infer; +export type SewerBscPlanSplmntDmndItem = z.infer; +export type SewerBscPlanRvwOpnnItem = z.infer; +export type EsntlAtchFileItem = z.infer; +export type SewerBscPlanBizCstItem = z.infer; +export type SewerBscPlanTmplntDtlItem = z.infer; +export type SewerBscPlanTmplntItem = z.infer; +export type SewerBscPlanTmplntTmprStrgItem = z.infer; +export type SewerBscPlanApplySaveRequestDto = z.infer; diff --git a/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-interface.ts b/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-interface.ts index 6c7b9360c..0e8025278 100644 --- a/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-interface.ts +++ b/apps/as/src/entities/sewer-bsc-plan/model/sewer-bsc-plan-interface.ts @@ -1,35 +1,26 @@ import type { AtflMngClientResponseDto, - CklstResultSaveBase, CklstRvwRsltTreeDto, EsntlAtchFileMngTreeResponseDto, - EsntlAtchFileSaveBase, PageableParams, PageResponse, - PbadmsApplySaveHeader, RvwOpnnResponseExtension, RvwOpnnSaveBase, SplmntDmndResponseExtension, SplmntDmndSaveBase, } from "@/shared/model"; +import type { + SewerBscPlanApplySaveRequestDto, + SewerBscPlanSaveRequestDto, +} from "./sewer-bsc-plan-apply-payload-schema"; + export type { AtflMngClientResponseDto, CklstRvwRsltTreeDto, EsntlAtchFileMngTreeResponseDto, } from "@/shared/model"; -/** 백엔드 SewerBscPlanSaveRequestDto */ -export interface SewerBscPlanSaveRequestDto { - sewerBscPlanDocNo?: string; - admdstCd?: string; - envoSeComCd?: string; - sewerBscPlanSeComCd?: string; - sewerBscPlanNm?: string; - taskSeComCd?: string; - prgrsSttsComCd?: string; -} - /** 백엔드 SewerBscPlanUpdateRequestDto */ export interface SewerBscPlanUpdateRequestDto { admdstCd?: string; @@ -72,28 +63,6 @@ export interface SewerBscPlanResponseDto { mdfcnDt?: string; } -/** 백엔드 SewerBscPlanDtlSaveRequestDto */ -export interface SewerBscPlanDtlSaveRequestDto { - sewerBscPlanDocNo?: string; - chgCycl?: number; - aplcntNm?: string; - aplcntMblTelno?: string; - aplcntOgdpNm?: string; - aplcntEmlAddr?: string; - dsgnrNm?: string; - dsgnrTelno?: string; - dsgnrOgdpNm?: string; - dsgnrEmlAddr?: string; - planYr?: string; - crtrYr?: string; - bizOtlnCn?: string; - frlvlGoalYr?: string; - sclvlGoalYr?: string; - thlvlGoalYr?: string; - folvlGoalYr?: string; - atchFileGroupId?: string; -} - /** 백엔드 SewerBscPlanDtlResponseDto */ export interface SewerBscPlanDtlResponseDto { sewerBscPlanDocNo?: string; @@ -210,103 +179,6 @@ export interface SewerBscPlanTmplntTmprStrgResponseDto { exlnPumpCnt?: number; } -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanOtlnItem */ -export interface SewerBscPlanOtlnItem { - planStpNo?: number; - sewerPlanPpltnCnt?: number; - sewerPrcsPpltnCnt?: number; - sewerPsplyRt?: number; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.CklstResultItem */ -export interface CklstResultItem extends CklstResultSaveBase { - selected?: string; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanFnrsCstItem */ -export interface SewerBscPlanFnrsCstItem { - planStpNo?: number; - gvexpAmt?: number; - lcexpAmt?: number; - orgprBdchgAmt?: number; - etcAmt?: number; - sumAmt?: number; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanSplmntDmndItem */ -export type SewerBscPlanSplmntDmndItem = SplmntDmndSaveBase; - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanRvwOpnnItem */ -export type SewerBscPlanRvwOpnnItem = RvwOpnnSaveBase; - -/** 백엔드 SewerBscPlanApplySaveRequestDto.EsntlAtchFileItem */ -export type EsntlAtchFileItem = EsntlAtchFileSaveBase; - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanBizCstItem */ -export interface SewerBscPlanBizCstItem { - planStpNo?: number; - acntgSeCd?: string; - cstrnAmt?: number; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntDtlItem */ -export interface SewerBscPlanTmplntDtlItem { - planStpNo?: number; - swtrmtFcltCd?: string; - prcsZoneArea?: number; - swtrmtPpltnCnt?: number; - planSwqty?: number; - fcltCpct?: number; - sprsysDrtwPplLen?: number; - sprsysExlnPplLen?: number; - cbsysPplLen?: number; - icptgPplLen?: number; - drgCnt?: number; - drtwRlyPumpCnt?: number; - drtwPumpCnt?: number; - exlnPumpCnt?: number; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntItem */ -export interface SewerBscPlanTmplntItem { - swtrmtFcltCd?: string; - atchFileGroupId?: string; - sewerBscPlanTmplntDtlList?: SewerBscPlanTmplntDtlItem[]; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto.SewerBscPlanTmplntTmprStrgItem */ -export interface SewerBscPlanTmplntTmprStrgItem { - dtStrgDt?: string; - swtrmtFcltCd?: string; - prcsZoneArea?: number; - swtrmtPpltnCnt?: number; - planSwqty?: number; - fcltCpct?: number; - sprsysDrtwPplLen?: number; - sprsysExlnPplLen?: number; - cbsysPplLen?: number; - icptgPplLen?: number; - drgCnt?: number; - drtwRlyPumpCnt?: number; - drtwPumpCnt?: number; - exlnPumpCnt?: number; -} - -/** 백엔드 SewerBscPlanApplySaveRequestDto */ -export interface SewerBscPlanApplySaveRequestDto extends PbadmsApplySaveHeader { - sewerBscPlan: SewerBscPlanSaveRequestDto; - sewerBscPlanDtl: SewerBscPlanDtlSaveRequestDto; - sewerBscPlanOtlnList?: SewerBscPlanOtlnItem[]; - cklstResultList?: CklstResultItem[]; - fnrsCstList?: SewerBscPlanFnrsCstItem[]; - splmntDmndList?: SewerBscPlanSplmntDmndItem[]; - rvwOpnnList?: SewerBscPlanRvwOpnnItem[]; - esntlAtchFileList?: EsntlAtchFileItem[]; - bizCstList?: SewerBscPlanBizCstItem[]; - sewerBscPlanTmplntList?: SewerBscPlanTmplntItem[]; - tmplntTmprStrgList?: SewerBscPlanTmplntTmprStrgItem[]; -} - /** 백엔드 SewerBscPlanApplySaveResponseDto */ export interface SewerBscPlanApplySaveResponseDto { sewerBscPlanDocNo: string; diff --git a/apps/as/src/pages/design-cnstn/ui/design-form-page.tsx b/apps/as/src/pages/design-cnstn/ui/design-form-page.tsx index 63d4eda75..b9bc230d9 100644 --- a/apps/as/src/pages/design-cnstn/ui/design-form-page.tsx +++ b/apps/as/src/pages/design-cnstn/ui/design-form-page.tsx @@ -15,10 +15,12 @@ import { type DesignCnstnMngDetResponseDto, designCnstnMngService, } from "@/entities/design-cnstn"; +import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { AttachmentsFormSection, BusinessScaleFormSection, ContactInfoFormSection, + isFormEsntlAtchComplete, PrivacyConsentSection, } from "@/widgets/pbadms-sprt"; import { Button } from "@common/components"; @@ -53,6 +55,8 @@ function DesignFormPage({ id, mode, initialData, createDefaults }: DesignFormPag const [isSubmitting, setIsSubmitting] = useState(false); const isEditMode = mode === "edit"; + const { data: essentialFiles = [] } = useEsntlAtchFileMngTree({ docSeComCd: "CNFM" }); + const form = useForm({ resolver: zodResolver(designCnstnRegistSchema), mode: "onChange", @@ -113,6 +117,11 @@ function DesignFormPage({ id, mode, initialData, createDefaults }: DesignFormPag async function onApply(data: DesignCnstnRegistFormData) { if (isSubmitting) return; + if (!isFormEsntlAtchComplete(essentialFiles, data.esntlAtchFileList)) { + alert("필수 첨부파일을 모두 등록해야 신청이 가능합니다."); + return; + } + try { setIsSubmitting(true); const designCnstnDocNo = await submitForm(data, "CT"); diff --git a/apps/as/src/pages/design-cnstn/ui/sections/attachments-section.tsx b/apps/as/src/pages/design-cnstn/ui/sections/attachments-section.tsx index 082881231..d176631be 100644 --- a/apps/as/src/pages/design-cnstn/ui/sections/attachments-section.tsx +++ b/apps/as/src/pages/design-cnstn/ui/sections/attachments-section.tsx @@ -62,49 +62,51 @@ function AttachmentsSection({ esntlFileList, etcFileList }: AttachmentsSectionPr )} - -
- - - - 파일명 - 크기 - - - - {etcFileList.length === 0 ? ( + {etcFileList.length > 0 && ( + +
+
+ - - 등록된 기타첨부파일이 없습니다. - + 파일명 + 크기 - ) : ( - etcFileList.map((file) => ( - - - {file.atflCd ? ( - downloadAttachment(file.atflCd)} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || file.physFileNm || file.atflId} - - ) : ( - {file.orgnlFileNm || file.physFileNm || file.atflId} - )} - - - {formatFileSize(file.atflFlieSz)} + + + {etcFileList.length === 0 ? ( + + + 등록된 기타첨부파일이 없습니다. - )) - )} - -
-
-
+ ) : ( + etcFileList.map((file) => ( + + + {file.atflCd ? ( + downloadAttachment(file.atflCd)} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || file.physFileNm || file.atflId} + + ) : ( + {file.orgnlFileNm || file.physFileNm || file.atflId} + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + + + + )} ); } diff --git a/apps/as/src/pages/design-cnstn/ui/sections/tech-review-opinion-section.tsx b/apps/as/src/pages/design-cnstn/ui/sections/tech-review-opinion-section.tsx index 9c6985ffb..f53e1bc72 100644 --- a/apps/as/src/pages/design-cnstn/ui/sections/tech-review-opinion-section.tsx +++ b/apps/as/src/pages/design-cnstn/ui/sections/tech-review-opinion-section.tsx @@ -2,6 +2,8 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { Paperclip } from "lucide-react"; + import { PROXY_HOST } from "@/shared/config"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { Field, FieldLabel, FieldSet } from "@/shared/ui/field"; @@ -71,12 +73,10 @@ function hasFormContent(formData: ReviewOpinionFormData) { const TechReviewOpinionSection = forwardRef< TechReviewOpinionSectionHandle, TechReviewOpinionSectionProps ->(function TechReviewOpinionSection({ - rvwList, - editableReviewStep, - atrzSeComCdList = [], - isAtrzLoading = false, -}, ref) { +>(function TechReviewOpinionSection( + { rvwList, editableReviewStep, atrzSeComCdList = [], isAtrzLoading = false }, + ref, +) { const canWriteOpinion = !isAtrzLoading && atrzSeComCdList.includes("ATRZ02"); const [formData, setFormData] = useState({ ...EMPTY_FORM_DATA, @@ -317,11 +317,12 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) { {item.atchFileInfo?.atflCd ? ( + {item.atchFileInfo.orgnlFileNm || "다운로드"} ) : ( diff --git a/apps/as/src/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema.ts b/apps/as/src/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema.ts index 8bfc0c317..e925ab321 100644 --- a/apps/as/src/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema.ts +++ b/apps/as/src/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema.ts @@ -85,7 +85,7 @@ export const fnrsCnsltnRegistSchema = z.object({ ), splmntDmndList: z.array( z.object({ - splmntDmndSn: z.number().optional(), + splmntDmndSn: z.number().nullish(), splmntArtclComCd: z.string().optional(), splmntArtclCdNm: z.string().nullish(), splmntDmndCn: z.string().optional(), diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx index e85efd562..5a4222553 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-form-page.tsx @@ -10,6 +10,7 @@ import { useQueryClient } from "@tanstack/react-query"; import { useAlertStore, useUserStore } from "@/shared/lib"; import { useAcntgSeMngTree } from "@/entities/acntg"; +import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { type FnrsCnsltnApplySaveRequestDto, fnrsCnsltnMngBaseKey, @@ -27,6 +28,7 @@ import { ContactInfoFormSection, FnrsSharFormSection, isFormChecklistComplete, + isFormEsntlAtchComplete, PrivacyConsentSection, recalculateFnrsSharTotals, SplmntDmndFormSection, @@ -67,6 +69,7 @@ function FnrsFormPage({ id, mode, initialData, createDefaults }: FnrsFormPagePro const { data: masterSharTree = [] } = useFnrsSharSeMngTree(); const { data: acntgTree = [] } = useAcntgSeMngTree(); + const { data: essentialFiles = [] } = useEsntlAtchFileMngTree({ docSeComCd: "FNRS" }); const sharTree = isEditMode && initialData?.fnrsSharList?.length ? initialData.fnrsSharList : masterSharTree; const sharItems = useMemo(() => buildFnrsSharItems(sharTree), [sharTree]); @@ -210,6 +213,11 @@ function FnrsFormPage({ id, mode, initialData, createDefaults }: FnrsFormPagePro return; } + if (!isFormEsntlAtchComplete(essentialFiles, data.esntlAtchFileList)) { + alert("필수 첨부파일을 모두 등록해야 신청이 가능합니다."); + return; + } + try { setIsSubmitting(true); const fnrsCnsltnDocNo = await submitForm(data, "ST"); diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/attachments-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/attachments-section.tsx index 3dbbdf1fd..0ac529c18 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/attachments-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/attachments-section.tsx @@ -19,98 +19,104 @@ function AttachmentsSection({ esntlFileList, etcFileList }: AttachmentsSectionPr return ( <> - -
- - - - 필수 자료명 - 파일명 - 크기 - - - - {esntlFileList.length === 0 ? ( + {esntlFileList.length > 0 && ( + +
+
+ - - 조회된 필수 첨부파일 항목이 없습니다. - + 필수 자료명 + 파일명 + 크기 - ) : ( - esntlFileList.map((file) => ( - - - {file.esntlAtchFileNm} - {file.esntlAtchYn === "Y" ? ( - * - ) : null} + + + {esntlFileList.length === 0 ? ( + + + 조회된 필수 첨부파일 항목이 없습니다. - - {file.atflCd ? ( - downloadAttachment(file.atflCd ?? "")} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || "다운로드"} - - ) : ( - 첨부없음 - )} - - {formatFileSize(file.atflFlieSz)} - )) - )} - -
-
-
+ ) : ( + esntlFileList.map((file) => ( + + + {file.esntlAtchFileNm} + {file.esntlAtchYn === "Y" ? ( + * + ) : null} + + + {file.atflCd ? ( + downloadAttachment(file.atflCd ?? "")} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || "다운로드"} + + ) : ( + 첨부없음 + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + + + + )} - -
- - - - 파일명 - 크기 - - - - {etcFileList.length === 0 ? ( + {etcFileList.length > 0 && ( + +
+
+ - - 등록된 기타첨부파일이 없습니다. - + 파일명 + 크기 - ) : ( - etcFileList.map((file) => ( - - - {file.atflCd ? ( - downloadAttachment(file.atflCd)} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || file.physFileNm || file.atflId} - - ) : ( - {file.orgnlFileNm || file.physFileNm || file.atflId} - )} - - - {formatFileSize(file.atflFlieSz)} + + + {etcFileList.length === 0 ? ( + + + 등록된 기타첨부파일이 없습니다. - )) - )} - -
-
-
+ ) : ( + etcFileList.map((file) => ( + + + {file.atflCd ? ( + downloadAttachment(file.atflCd)} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || file.physFileNm || file.atflId} + + ) : ( + {file.orgnlFileNm || file.physFileNm || file.atflId} + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + + + + )} ); } diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/checklist-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/checklist-section.tsx index 80671876a..09aa48052 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/checklist-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/checklist-section.tsx @@ -71,6 +71,10 @@ function ChecklistSection({ const { total, completed, percent } = progress; + if (cklstResultList.length === 0) { + return null; + } + return (
diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx index bc7a51af6..21ea2c7a6 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/tech-review-opinion-section.tsx @@ -2,6 +2,8 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { Paperclip } from "lucide-react"; + import { PROXY_HOST } from "@/shared/config"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { Field, FieldLabel, FieldSet } from "@/shared/ui/field"; @@ -71,12 +73,10 @@ function hasFormContent(formData: ReviewOpinionFormData) { const TechReviewOpinionSection = forwardRef< TechReviewOpinionSectionHandle, TechReviewOpinionSectionProps ->(function TechReviewOpinionSection({ - rvwList, - editableReviewStep, - atrzSeComCdList = [], - isAtrzLoading = false, -}, ref) { +>(function TechReviewOpinionSection( + { rvwList, editableReviewStep, atrzSeComCdList = [], isAtrzLoading = false }, + ref, +) { const canWriteKecoOpinion = !isAtrzLoading && atrzSeComCdList.includes("ATRZ02"); const [formData, setFormData] = useState({ ...EMPTY_FORM_DATA, @@ -336,11 +336,12 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) { {item.atchFileInfo?.atflCd ? ( + {item.atchFileInfo.orgnlFileNm || "다운로드"} ) : ( diff --git a/apps/as/src/pages/instl-sncn/lib/instl-sncn-regist-schema.ts b/apps/as/src/pages/instl-sncn/lib/instl-sncn-regist-schema.ts index 5cc52d296..7fe74a337 100644 --- a/apps/as/src/pages/instl-sncn/lib/instl-sncn-regist-schema.ts +++ b/apps/as/src/pages/instl-sncn/lib/instl-sncn-regist-schema.ts @@ -77,7 +77,7 @@ export const instlSncnRegistSchema = z.object({ ), splmntDmndList: z.array( z.object({ - splmntDmndSn: z.number().optional(), + splmntDmndSn: z.number().nullish(), splmntArtclComCd: z.string().optional(), splmntArtclCdNm: z.string().nullish(), splmntDmndCn: z.string().optional(), diff --git a/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx b/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx index 766712e35..bc248e3b2 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx @@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useQueryClient } from "@tanstack/react-query"; import { useAlertStore, useUserStore } from "@/shared/lib"; +import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { type InstlSncnApplySaveRequestDto, instlSncnKeys, @@ -22,6 +23,7 @@ import { ChecklistFormSection, ContactInfoFormSection, isFormChecklistComplete, + isFormEsntlAtchComplete, PrivacyConsentSection, SplmntDmndFormSection, } from "@/widgets/pbadms-sprt"; @@ -57,6 +59,8 @@ function InstlFormPage({ id, mode, initialData, createDefaults }: InstlFormPageP const isEditMode = mode === "edit"; const isSplmntPrgrsStts = initialData?.detDto.prgrsSttsComCd === "SL"; + const { data: essentialFiles = [] } = useEsntlAtchFileMngTree({ docSeComCd: "INSTL" }); + const form = useForm({ resolver: zodResolver(instlSncnRegistSchema), mode: "onChange", @@ -172,6 +176,11 @@ function InstlFormPage({ id, mode, initialData, createDefaults }: InstlFormPageP return; } + if (!isFormEsntlAtchComplete(essentialFiles, data.esntlAtchFileList)) { + alert("필수 첨부파일을 모두 등록해야 신청이 가능합니다."); + return; + } + try { setIsSubmitting(true); const instlSncnDocNo = await submitForm(data, "ST"); diff --git a/apps/as/src/pages/instl-sncn/ui/sections/attachments-section.tsx b/apps/as/src/pages/instl-sncn/ui/sections/attachments-section.tsx index d518e80dc..5cd7fec89 100644 --- a/apps/as/src/pages/instl-sncn/ui/sections/attachments-section.tsx +++ b/apps/as/src/pages/instl-sncn/ui/sections/attachments-section.tsx @@ -19,98 +19,104 @@ function AttachmentsSection({ esntlFileList, etcFileList }: AttachmentsSectionPr return ( <> - -
- - - - 필수 자료명 - 파일명 - 크기 - - - - {esntlFileList.length === 0 ? ( + {esntlFileList.length > 0 && ( + +
+
+ - - 조회된 필수 첨부파일 항목이 없습니다. - + 필수 자료명 + 파일명 + 크기 - ) : ( - esntlFileList.map((file) => ( - - - {file.esntlAtchFileNm} - {file.esntlAtchYn === "Y" ? ( - * - ) : null} + + + {esntlFileList.length === 0 ? ( + + + 조회된 필수 첨부파일 항목이 없습니다. - - {file.atflCd ? ( - downloadAttachment(file.atflCd ?? "")} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || "다운로드"} - - ) : ( - 첨부없음 - )} - - {formatFileSize(file.atflFlieSz)} - )) - )} - -
-
-
+ ) : ( + esntlFileList.map((file) => ( + + + {file.esntlAtchFileNm} + {file.esntlAtchYn === "Y" ? ( + * + ) : null} + + + {file.atflCd ? ( + downloadAttachment(file.atflCd ?? "")} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || "다운로드"} + + ) : ( + 첨부없음 + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + +
+
+ )} - -
- - - - 파일명 - 크기 - - - - {etcFileList.length === 0 ? ( + {etcFileList.length > 0 && ( + +
+
+ - - 등록된 기타첨부파일이 없습니다. - + 파일명 + 크기 - ) : ( - etcFileList.map((file) => ( - - - {file.atflCd ? ( - downloadAttachment(file.atflCd)} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || file.physFileNm || file.atflId} - - ) : ( - {file.orgnlFileNm || file.physFileNm || file.atflId} - )} - - - {formatFileSize(file.atflFlieSz)} + + + {etcFileList.length === 0 ? ( + + + 등록된 기타첨부파일이 없습니다. - )) - )} - -
-
-
+ ) : ( + etcFileList.map((file) => ( + + + {file.atflCd ? ( + downloadAttachment(file.atflCd)} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || file.physFileNm || file.atflId} + + ) : ( + {file.orgnlFileNm || file.physFileNm || file.atflId} + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + + + + )} ); } diff --git a/apps/as/src/pages/instl-sncn/ui/sections/checklist-section.tsx b/apps/as/src/pages/instl-sncn/ui/sections/checklist-section.tsx index 0846d2b3e..2f370a5d7 100644 --- a/apps/as/src/pages/instl-sncn/ui/sections/checklist-section.tsx +++ b/apps/as/src/pages/instl-sncn/ui/sections/checklist-section.tsx @@ -70,6 +70,10 @@ function ChecklistSection({ const { total, completed, percent } = progress; + if (cklstResultList.length === 0) { + return null; + } + return (
diff --git a/apps/as/src/pages/instl-sncn/ui/sections/tech-review-opinion-section.tsx b/apps/as/src/pages/instl-sncn/ui/sections/tech-review-opinion-section.tsx index 2227492ed..eb5530c78 100644 --- a/apps/as/src/pages/instl-sncn/ui/sections/tech-review-opinion-section.tsx +++ b/apps/as/src/pages/instl-sncn/ui/sections/tech-review-opinion-section.tsx @@ -2,6 +2,8 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { Paperclip } from "lucide-react"; + import { PROXY_HOST } from "@/shared/config"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { Field, FieldLabel, FieldSet } from "@/shared/ui/field"; @@ -67,12 +69,10 @@ function hasFormContent(formData: ReviewOpinionFormData) { const TechReviewOpinionSection = forwardRef< TechReviewOpinionSectionHandle, TechReviewOpinionSectionProps ->(function TechReviewOpinionSection({ - rvwList, - editableReviewStep, - atrzSeComCdList = [], - isAtrzLoading = false, -}, ref) { +>(function TechReviewOpinionSection( + { rvwList, editableReviewStep, atrzSeComCdList = [], isAtrzLoading = false }, + ref, +) { const canWriteKecoOpinion = !isAtrzLoading && atrzSeComCdList.includes("ATRZ02"); const [formData, setFormData] = useState({ ...EMPTY_FORM_DATA, @@ -329,11 +329,12 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) { {item.atchFileInfo?.atflCd ? ( + {item.atchFileInfo.orgnlFileNm || "다운로드"} ) : ( diff --git a/apps/as/src/pages/sewer-bsc-plan/lib/form-data-mapper.ts b/apps/as/src/pages/sewer-bsc-plan/lib/form-data-mapper.ts index 25a39aab0..6f84154d5 100644 --- a/apps/as/src/pages/sewer-bsc-plan/lib/form-data-mapper.ts +++ b/apps/as/src/pages/sewer-bsc-plan/lib/form-data-mapper.ts @@ -1,9 +1,9 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import type { IUserInfo } from "@/shared/store"; import type { SewerBscPlanApplySaveRequestDto, SewerBscPlanDetResponseDto, } from "@/entities/sewer-bsc-plan"; +import { sewerBscPlanApplySaveSchema } from "@/entities/sewer-bsc-plan"; import type { BizCstMatrixItem } from "./biz-cst-matrix-utils"; import { recalcFnrsCstSumAmt } from "./fnrs-cst-form-utils"; @@ -245,47 +245,33 @@ export function buildApplyPayload( prgrsSttsComCd: "SV" | "LE" | "TE", options?: { isEditMode?: boolean; docNo?: string }, ): SewerBscPlanApplySaveRequestDto { - const { taskSeComCd, ...sewerBscPlanRest } = data.sewerBscPlan; + const docNo = options?.isEditMode ? options.docNo : undefined; - return { + return sewerBscPlanApplySaveSchema.parse({ sewerBscPlan: { - ...sewerBscPlanRest, - taskSeComCd: taskSeComCd ?? "", - sewerBscPlanDocNo: options?.isEditMode ? options.docNo : undefined, + ...data.sewerBscPlan, + sewerBscPlanDocNo: docNo, + taskSeComCd: data.sewerBscPlan.taskSeComCd ?? "", prgrsSttsComCd, }, sewerBscPlanDtl: { ...data.sewerBscPlanDtl, - sewerBscPlanDocNo: options?.isEditMode ? options.docNo : undefined, + sewerBscPlanDocNo: docNo, }, sewerBscPlanOtlnList: data.sewerBscPlanOtlnList, bizCstList: data.bizCstList, fnrsCstList: data.fnrsCstList.map((item) => recalcFnrsCstSumAmt(item)), - sewerBscPlanTmplntList: data.sewerBscPlanTmplntList.map( - ({ atchFileNm, swtrmtFcltNm, ...item }) => ({ - swtrmtFcltCd: item.swtrmtFcltCd, - atchFileGroupId: item.atchFileGroupId, - sewerBscPlanTmplntDtlList: item.sewerBscPlanTmplntDtlList, - }), - ), + sewerBscPlanTmplntList: data.sewerBscPlanTmplntList, cklstResultList: data.cklstResultList.map((item) => ({ ...item, selected: item.selected ?? undefined, })), esntlAtchFileList: data.esntlAtchFileList, - splmntDmndList: data.splmntDmndList.map( - ({ - splmntArtclCdNm, - splmntAtchFileNm, - splmntAtchFileAtflId, - splmntDmndAtchFileNm, - ...item - }) => ({ - ...item, - ...(prgrsSttsComCd === "TE" ? { cmptnYn: "Y" } : null), - }), - ), - }; + splmntDmndList: data.splmntDmndList.map((item) => ({ + ...item, + cmptnYn: prgrsSttsComCd === "TE" ? "Y" : item.cmptnYn, + })), + }); } export { createEmptyTmplntDtlList }; diff --git a/apps/as/src/pages/sewer-bsc-plan/lib/save-payload-utils.ts b/apps/as/src/pages/sewer-bsc-plan/lib/payload-utils.ts similarity index 94% rename from apps/as/src/pages/sewer-bsc-plan/lib/save-payload-utils.ts rename to apps/as/src/pages/sewer-bsc-plan/lib/payload-utils.ts index e2fc31f0a..320044a14 100644 --- a/apps/as/src/pages/sewer-bsc-plan/lib/save-payload-utils.ts +++ b/apps/as/src/pages/sewer-bsc-plan/lib/payload-utils.ts @@ -146,10 +146,8 @@ export function buildSupplementReqPayload( toSplmntDmndSaveItem(item), ); - const nextSn = existingItems.reduce((max, item) => Math.max(max, item.splmntDmndSn ?? 0), 0) + 1; - - const newItems: SewerBscPlanSplmntDmndItem[] = payload.items.map((item, index) => ({ - splmntDmndSn: nextSn + index, + const newItems: SewerBscPlanSplmntDmndItem[] = payload.items.map((item) => ({ + splmntDmndSn: null, splmntArtclComCd: item.splmntArtclComCd, splmntDmndCn: item.splmntDmndCn, splmntDmndAtchFileGroupId: item.splmntDmndAtchFileGroupId, diff --git a/apps/as/src/pages/sewer-bsc-plan/lib/sewer-bsc-plan-regist-schema.ts b/apps/as/src/pages/sewer-bsc-plan/lib/sewer-bsc-plan-regist-schema.ts index 19d0bdf90..416b1f401 100644 --- a/apps/as/src/pages/sewer-bsc-plan/lib/sewer-bsc-plan-regist-schema.ts +++ b/apps/as/src/pages/sewer-bsc-plan/lib/sewer-bsc-plan-regist-schema.ts @@ -1,38 +1,27 @@ import { z } from "zod"; -const tmplntDtlSchema = z.object({ - planStpNo: z.number(), - swtrmtFcltCd: z.string(), - prcsZoneArea: z.number().optional(), - swtrmtPpltnCnt: z.number().optional(), - planSwqty: z.number().optional(), - fcltCpct: z.number().optional(), - sprsysDrtwPplLen: z.number().optional(), - sprsysExlnPplLen: z.number().optional(), - cbsysPplLen: z.number().optional(), - icptgPplLen: z.number().optional(), - drgCnt: z.number().optional(), - drtwRlyPumpCnt: z.number().optional(), - drtwPumpCnt: z.number().optional(), - exlnPumpCnt: z.number().optional(), -}); +import { + sewerBscPlanBizCstItemSchema, + sewerBscPlanCklstResultItemSchema, + sewerBscPlanDtlSaveSchema, + sewerBscPlanEsntlAtchFileItemSchema, + sewerBscPlanFnrsCstItemSchema, + sewerBscPlanOtlnItemSchema, + sewerBscPlanSaveSchema, + sewerBscPlanSplmntDmndItemSchema, + sewerBscPlanTmplntDtlItemSchema, + sewerBscPlanTmplntItemSchema, +} from "@/entities/sewer-bsc-plan"; export const sewerBscPlanRegistSchema = z.object({ - sewerBscPlan: z.object({ - sewerBscPlanDocNo: z.string().optional(), + sewerBscPlan: sewerBscPlanSaveSchema.extend({ sewerBscPlanNm: z.string().min(1, "기본계획명은 필수입니다"), - sewerBscPlanSeComCd: z.string().optional(), sewerBscPlanSeCdNm: z.string().optional(), - taskSeComCd: z.string().optional(), taskSeCdNm: z.string().optional(), - admdstCd: z.string().optional(), - envoSeComCd: z.string().optional(), - prgrsSttsComCd: z.string().optional(), sido: z.string().optional(), - gugun: z.string().optional(), + gugun: z.string().nullish(), }), - sewerBscPlanDtl: z.object({ - chgCycl: z.number().optional(), + sewerBscPlanDtl: sewerBscPlanDtlSaveSchema.extend({ planYr: z.string().min(1, "계획년도는 필수입니다"), crtrYr: z.string().min(1, "기준년도는 필수입니다"), frlvlGoalYr: z.string().min(1, "1단계 목표연도는 필수입니다"), @@ -40,10 +29,6 @@ export const sewerBscPlanRegistSchema = z.object({ thlvlGoalYr: z.string().min(1, "3단계 목표연도는 필수입니다"), folvlGoalYr: z.string().min(1, "4단계 목표연도는 필수입니다"), bizOtlnCn: z.string().min(1, "계획개요는 필수입니다"), - aplcntNm: z.string().optional(), - aplcntMblTelno: z.string().optional(), - aplcntOgdpNm: z.string().optional(), - aplcntEmlAddr: z.string().optional(), dsgnrNm: z.string().min(1, "설계자 이름은 필수입니다"), dsgnrTelno: z .string() @@ -54,74 +39,60 @@ export const sewerBscPlanRegistSchema = z.object({ .string() .min(1, "설계자 이메일은 필수입니다") .email("올바른 이메일 형식이 아닙니다"), - atchFileGroupId: z.string().optional(), }), sewerBscPlanOtlnList: z.array( - z.object({ + sewerBscPlanOtlnItemSchema.extend({ planStpNo: z.number(), - sewerPlanPpltnCnt: z.number().optional(), - sewerPrcsPpltnCnt: z.number().optional(), - sewerPsplyRt: z.number().optional(), }), ), bizCstList: z.array( - z.object({ + sewerBscPlanBizCstItemSchema.extend({ planStpNo: z.number(), acntgSeCd: z.string(), cstrnAmt: z.number(), }), ), fnrsCstList: z.array( - z.object({ + sewerBscPlanFnrsCstItemSchema.extend({ planStpNo: z.number(), - gvexpAmt: z.number().optional(), - lcexpAmt: z.number().optional(), - orgprBdchgAmt: z.number().optional(), - etcAmt: z.number().optional(), - sumAmt: z.number().optional(), }), ), sewerBscPlanTmplntList: z.array( - z.object({ + sewerBscPlanTmplntItemSchema.extend({ swtrmtFcltCd: z.string(), swtrmtFcltNm: z.string().nullish(), - atchFileGroupId: z.string().optional(), atchFileNm: z.string().optional(), - sewerBscPlanTmplntDtlList: z.array(tmplntDtlSchema).optional(), + sewerBscPlanTmplntDtlList: z + .array( + sewerBscPlanTmplntDtlItemSchema.extend({ + planStpNo: z.number(), + swtrmtFcltCd: z.string(), + }), + ) + .optional(), }), ), cklstResultList: z.array( - z.object({ + sewerBscPlanCklstResultItemSchema.extend({ cklstSn: z.number(), cklstArtclSn: z.number(), cklstRvwSn: z.number(), - rvwStpComCd: z.string(), selected: z.enum(["Y", "N"]).nullable(), picRvwCn: z.string().nullable().optional(), }), ), esntlAtchFileList: z.array( - z.object({ + sewerBscPlanEsntlAtchFileItemSchema.extend({ esntlAtchFileId: z.string(), atchFileGroupId: z.string(), }), ), splmntDmndList: z.array( - z.object({ - splmntDmndSn: z.number().optional(), - splmntArtclComCd: z.string().optional(), + sewerBscPlanSplmntDmndItemSchema.extend({ splmntArtclCdNm: z.string().nullish(), - splmntDmndCn: z.string().optional(), - splmntDmndDt: z.string().optional(), - splmntDmndAtchFileGroupId: z.string().optional(), splmntDmndAtchFileNm: z.string().optional(), - splmntCn: z.string().optional(), - splmntPicNm: z.string().optional(), - splmntCmptnDt: z.string().optional(), - splmntAtchFileGroupId: z.string().optional(), splmntAtchFileNm: z.string().optional(), splmntAtchFileAtflId: z.string().optional(), - cmptnYn: z.string().optional(), }), ), privacyConsent: z.enum(["Y", "N"], { @@ -130,6 +101,3 @@ export const sewerBscPlanRegistSchema = z.object({ }); export type SewerBscPlanRegistFormData = z.infer; - -/** 체크리스트·필수첨부 docSeComCd — CMS 합의 전 임시값 */ -export const SEWER_BSC_PLAN_DOC_SE_COM_CD = "SEWER"; diff --git a/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx b/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx index dfe5aebd3..ec387c8cb 100644 --- a/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx @@ -46,8 +46,9 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro return [ { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, - { label: MAIN_STEP_LABELS.reply, state: "action" }, + { label: MAIN_STEP_LABELS.envoReview, state: "action" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } @@ -56,8 +57,8 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.supplement, state: "action" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } @@ -71,16 +72,6 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro ]; } - if (prgrsSttsComCd === "EP") { - return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, - { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "completed" }, - { label: replyLabel, state: "action" }, - ]; - } - if (prgrsSttsComCd === "TA") { return [ { label: MAIN_STEP_LABELS.save, state: "completed" }, @@ -91,6 +82,16 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro ]; } + if (prgrsSttsComCd === "EP") { + return [ + { label: MAIN_STEP_LABELS.save, state: "completed" }, + { label: MAIN_STEP_LABELS.apply, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { label: MAIN_STEP_LABELS.techReview, state: "completed" }, + { label: replyLabel, state: "completed" }, + ]; + } + return [{ label: prgrsSttsCdNm || prgrsSttsComCd || "-", state: "current" }]; } diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/components/floating-action-buttons.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/components/floating-action-buttons.tsx index d185180cc..b2e5fec40 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/components/floating-action-buttons.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/components/floating-action-buttons.tsx @@ -41,7 +41,7 @@ import { buildSupplementReqPayload, buildTechReviewReplyPayload, toSplmntDmndSaveItem, -} from "../../lib/save-payload-utils"; +} from "../../lib/payload-utils"; import { type ChecklistFormState } from "../../model/view-types"; import type { TechReviewOpinionSectionHandle } from "../sections/tech-review-opinion-section"; diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sections/attachments-section.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sections/attachments-section.tsx index b47c27580..f35cab5ff 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sections/attachments-section.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sections/attachments-section.tsx @@ -1,5 +1,7 @@ "use client"; +import { Paperclip } from "lucide-react"; + import { formatFileSize } from "@/shared/lib/file-utils"; import { useDownloadAttachmentByAtflCd } from "@/entities/attach"; import type { @@ -19,98 +21,105 @@ function AttachmentsSection({ esntlFileList, etcFileList }: AttachmentsSectionPr return ( <> - -
- - - - 필수 자료명 - 파일명 - 크기 - - - - {esntlFileList.length === 0 ? ( + {esntlFileList.length > 0 && ( + +
+
+ - - 조회된 필수 첨부파일 항목이 없습니다. - + 필수 자료명 + 파일명 + 크기 - ) : ( - esntlFileList.map((file) => ( - - - {file.esntlAtchFileNm} - {file.esntlAtchYn === "Y" ? ( - * - ) : null} + + + {esntlFileList.length === 0 ? ( + + + 조회된 필수 첨부파일 항목이 없습니다. - - {file.atflCd ? ( - downloadAttachment(file.atflCd ?? "")} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || "다운로드"} - - ) : ( - 첨부없음 - )} - - {formatFileSize(file.atflFlieSz)} - )) - )} - -
-
-
+ ) : ( + esntlFileList.map((file) => ( + + + {file.esntlAtchFileNm} + {file.esntlAtchYn === "Y" ? ( + * + ) : null} + + + {file.atflCd ? ( + downloadAttachment(file.atflCd ?? "")} + target="_blank" + rel="noreferrer" + > + + {file.orgnlFileNm || "다운로드"} + + ) : ( + 첨부없음 + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + +
+
+ )} - -
- - - - 파일명 - 크기 - - - - {etcFileList.length === 0 ? ( + {etcFileList.length > 0 && ( + +
+
+ - - 등록된 기타첨부파일이 없습니다. - + 파일명 + 크기 - ) : ( - etcFileList.map((file) => ( - - - {file.atflCd ? ( - downloadAttachment(file.atflCd)} - target="_blank" - rel="noreferrer" - > - {file.orgnlFileNm || file.physFileNm || file.atflId} - - ) : ( - {file.orgnlFileNm || file.physFileNm || file.atflId} - )} - - - {formatFileSize(file.atflFlieSz)} + + + {etcFileList.length === 0 ? ( + + + 등록된 기타첨부파일이 없습니다. - )) - )} - -
-
-
+ ) : ( + etcFileList.map((file) => ( + + + {file.atflCd ? ( + downloadAttachment(file.atflCd)} + target="_blank" + rel="noreferrer" + > + {file.orgnlFileNm || file.physFileNm || file.atflId} + + ) : ( + {file.orgnlFileNm || file.physFileNm || file.atflId} + )} + + + {formatFileSize(file.atflFlieSz)} + + + )) + )} + + + + + )} ); } diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sections/basic-info-form-section.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sections/basic-info-form-section.tsx index c935b9185..1af5a02b1 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sections/basic-info-form-section.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sections/basic-info-form-section.tsx @@ -2,6 +2,7 @@ import { Controller, useFormContext } from "react-hook-form"; +import { useUserStore } from "@/shared/lib"; import { Field, FieldError } from "@/shared/ui/field"; import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { @@ -23,6 +24,7 @@ import type { SewerBscPlanRegistFormData } from "../../lib/sewer-bsc-plan-regist import { getGoalYearOptions, getLinkedGoalYears, getPlanYearOptions } from "../../lib/year-options"; function BasicInfoFormSection() { + const userInfo = useUserStore((state) => state.userInfo); const { control, setValue, watch } = useFormContext(); const planYearOptions = getPlanYearOptions(); const goalYearOptions = getGoalYearOptions(); @@ -265,7 +267,7 @@ function BasicInfoFormSection() { 구분 {sewerBscPlanSeLabel} 신청 지자체 - {localGovLabel} + {localGovLabel || userInfo?.laeSeNm} diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sections/checklist-section.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sections/checklist-section.tsx index 379bb806a..9bb85272b 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sections/checklist-section.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sections/checklist-section.tsx @@ -71,6 +71,10 @@ function ChecklistSection({ const { total, completed, percent } = progress; + if (cklstResultList.length === 0) { + return null; + } + return (
diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sections/tech-review-opinion-section.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sections/tech-review-opinion-section.tsx index f8e29462c..506704cdc 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sections/tech-review-opinion-section.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sections/tech-review-opinion-section.tsx @@ -2,6 +2,8 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; +import { Paperclip } from "lucide-react"; + import { PROXY_HOST } from "@/shared/config"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { Field, FieldLabel, FieldSet } from "@/shared/ui/field"; @@ -334,11 +336,12 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) { {item.atchFileInfo?.atflCd ? ( + {item.atchFileInfo.orgnlFileNm || "다운로드"} ) : ( diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sections/tmplnt-form-section.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sections/tmplnt-form-section.tsx index d600d8701..ca5192a66 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sections/tmplnt-form-section.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sections/tmplnt-form-section.tsx @@ -4,11 +4,10 @@ import { useId, useMemo, useState } from "react"; import { Controller, useFieldArray, useFormContext, useWatch } from "react-hook-form"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; -import { Paperclip } from "lucide-react"; import { getDefaultFileKey, useTus } from "@/shared/lib"; import { Field } from "@/shared/ui/field"; -import { attachService, useDownloadAttachmentByAtflCd } from "@/entities/attach"; +import { attachService } from "@/entities/attach"; import { useFacilityList } from "@/entities/bns-if"; import { useNowDmgMtncGnsvy } from "@/entities/dc"; import { SectionCollapsible } from "@/widgets/pbadms-sprt"; @@ -226,7 +225,6 @@ function TmplntFacilityAttachments({ }: TmplntFacilityAttachmentsProps) { const formId = useId(); const { setValue } = useFormContext(); - const { mutate: downloadAttachment } = useDownloadAttachmentByAtflCd(); const [uploadedFile, setUploadedFile] = useState(() => atchFileGroupId && atchFileNm ? { @@ -254,7 +252,6 @@ function TmplntFacilityAttachments({ const fileInputId = `${formId}-tmplnt-atch-${facilityIndex}`; const uploading = uploadedFile ? (isUploading[uploadedFile.fileKey] ?? false) : false; const displayFileName = uploadedFile?.fileName ?? atchFileNm ?? ""; - const downloadAtflCd = uploadedFile?.atflCd || atchFileGroupId; const hasFile = Boolean(uploadedFile || atchFileNm); async function handleFileUpload(files: FileList | null) { @@ -315,22 +312,14 @@ function TmplntFacilityAttachments({ - {facilityName} 계획평면도 + + {facilityName} 계획평면도 + {TMPLNT_ATCH_FORMAT_LABEL} {hasFile ? (
- {downloadAtflCd ? ( - downloadAttachment(downloadAtflCd)} - > - - {displayFileName} - - ) : ( - {displayFileName} - )} + {displayFileName}