From d71f2043def5960ddedd9fd5024633794c1dda24 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 11:38:38 +0900 Subject: [PATCH 01/13] =?UTF-8?q?[as]=20feat:=20=ED=95=84=EC=88=98=20?= =?UTF-8?q?=EC=B2=A8=EB=B6=80=20=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B0=80=EB=8A=A5=ED=95=9C=20=ED=99=95=EC=9E=A5?= =?UTF-8?q?=EC=9E=90=20=EC=A0=9C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/pbadms-sprt/lib/attachments-form-utils.ts | 11 +++++++++++ .../pbadms-sprt/ui/attachments-form-section.tsx | 7 ++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/as/src/widgets/pbadms-sprt/lib/attachments-form-utils.ts b/apps/as/src/widgets/pbadms-sprt/lib/attachments-form-utils.ts index ff9c16b9d..59981f8d1 100644 --- a/apps/as/src/widgets/pbadms-sprt/lib/attachments-form-utils.ts +++ b/apps/as/src/widgets/pbadms-sprt/lib/attachments-form-utils.ts @@ -19,3 +19,14 @@ export function createEtcUploadKey(file: File): string { export function isEtcFileKey(fileKey: string): boolean { return fileKey.startsWith(ETC_FILE_KEY_PREFIX); } + +export function toFileAccept(rmrk: string | null | undefined): string { + if (!rmrk?.trim()) return ""; + + return rmrk + .split(",") + .map((ext) => ext.trim().toLowerCase()) + .filter(Boolean) + .map((ext) => (ext.startsWith(".") ? ext : `.${ext}`)) + .join(","); +} diff --git a/apps/as/src/widgets/pbadms-sprt/ui/attachments-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/attachments-form-section.tsx index b9d132cf8..ffc2b6341 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/attachments-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/attachments-form-section.tsx @@ -27,6 +27,7 @@ import { createUploadKey, type FieldPrefix, isEtcFileKey, + toFileAccept, } from "../lib/attachments-form-utils"; import { SectionCollapsible } from "./section-collapsible"; @@ -373,7 +374,8 @@ function AttachmentsFormSection({

필수 첨부파일

- + + @@ -381,6 +383,7 @@ function AttachmentsFormSection({ 파일명 + 파일 확장자 업로드된 파일 @@ -401,6 +404,7 @@ function AttachmentsFormSection({ > {file.esntlAtchFileNm} + {file.esntlAtchFileRmrk} {uploadedFiles[file.esntlAtchFileId]?.length > 0 ? (
@@ -432,6 +436,7 @@ function AttachmentsFormSection({ { void handleEssentialFileUpload( file.esntlAtchFileId, From b41814ed0c61689e5d355db8305fbdf09bf8c984 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 12:01:17 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[as]=20chore:=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A0=95=EB=A0=AC=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20(=EC=84=9C=EB=B2=84=EC=97=90?= =?UTF-8?q?=20=EC=9C=84=EC=9E=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/pbadms-sprt/lib/checklist-form-utils.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts b/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts index f49782d84..2c43aca43 100644 --- a/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts +++ b/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts @@ -26,16 +26,6 @@ interface CklstResultListItem { picRvwCn?: string | null; } -function compareCklstSiblings(a: CklstTreeNode, b: CklstTreeNode): number { - if (a.cklstArtclSn !== b.cklstArtclSn) { - return a.cklstArtclSn - b.cklstArtclSn; - } - if (a.sortSeq !== b.sortSeq) { - return b.sortSeq - a.sortSeq; - } - return a.cklstRvwSn - b.cklstRvwSn; -} - export function orderCklstTreeNodes(rows: CklstTreeNode[]): CklstTreeNode[] { const childMap = new Map(); @@ -45,8 +35,6 @@ export function orderCklstTreeNodes(rows: CklstTreeNode[]): CklstTreeNode[] { childMap.get(parent)!.push(row); }); - childMap.forEach((siblings) => siblings.sort(compareCklstSiblings)); - const ordered: CklstTreeNode[] = []; function walk(parent: number | null) { From e8c8db73837499bdab37da504a744e3e70dd1955 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 14:05:25 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[as]=20chore:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/as/src/widgets/pbadms-sprt/index.ts | 1 - .../pbadms-sprt/lib/checklist-form-utils.ts | 27 ++----------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/apps/as/src/widgets/pbadms-sprt/index.ts b/apps/as/src/widgets/pbadms-sprt/index.ts index 858019e55..aeee59a58 100644 --- a/apps/as/src/widgets/pbadms-sprt/index.ts +++ b/apps/as/src/widgets/pbadms-sprt/index.ts @@ -9,7 +9,6 @@ export { buildCnstpScaleItems, recalculateBzScaleTotals } from "./lib/business-s export { buildChecklistTabs, isFormChecklistComplete, - orderCklstTreeNodes, syncCklstResultListFromTree, } from "./lib/checklist-form-utils"; export type { FnrsSharItemDef, FnrsSharListItem } from "./lib/fnrs-shar-form-utils"; diff --git a/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts b/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts index 2c43aca43..07f015648 100644 --- a/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts +++ b/apps/as/src/widgets/pbadms-sprt/lib/checklist-form-utils.ts @@ -26,29 +26,6 @@ interface CklstResultListItem { picRvwCn?: string | null; } -export function orderCklstTreeNodes(rows: CklstTreeNode[]): CklstTreeNode[] { - const childMap = new Map(); - - rows.forEach((row) => { - const parent = row.upCklstRvwSn ?? null; - if (!childMap.has(parent)) childMap.set(parent, []); - childMap.get(parent)!.push(row); - }); - - const ordered: CklstTreeNode[] = []; - - function walk(parent: number | null) { - (childMap.get(parent) ?? []).forEach((child) => { - ordered.push(child); - walk(child.cklstRvwSn); - }); - } - - walk(null); - - return ordered; -} - function groupCklstTreeByTaskSeComCd(cklstTree: CklstTreeNode[]): Map { const grouped = new Map(); @@ -69,7 +46,7 @@ function getOrderedSelectableNodes( const grouped = groupCklstTreeByTaskSeComCd(cklstTree); return taskSeComCdArray.flatMap((code) => - orderCklstTreeNodes(grouped.get(code) ?? []).filter((node) => node.bchckUseYn === "Y"), + (grouped.get(code) ?? []).filter((node) => node.bchckUseYn === "Y"), ); } @@ -86,7 +63,7 @@ export function buildChecklistTabs( return orderedCodes.map((code) => ({ code, label: taskCodeLabels[code] || code, - items: orderCklstTreeNodes(grouped.get(code) ?? []).map((node) => ({ + items: (grouped.get(code) ?? []).map((node) => ({ cklstSn: node.cklstSn, cklstArtclSn: node.cklstArtclSn, cklstRvwSn: node.cklstRvwSn, From 9d13809d0169f2f1686007eb175ac8cd228e2078 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 14:28:10 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[as]=20feat:=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=95=AD=EB=AA=A9=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=97=90=20'=EC=A0=95=EB=A0=AC=20=EC=88=9C=EC=84=9C'?= =?UTF-8?q?=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/build-rvw-display-rows.ts | 16 +++++----------- .../checklist-management/ui/cklst-rvw-grid.tsx | 12 +++++++----- .../checklist-management/ui/cklst-rvw-row.tsx | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/apps/as/src/pages/checklist-management/model/build-rvw-display-rows.ts b/apps/as/src/pages/checklist-management/model/build-rvw-display-rows.ts index f82fc9a96..55e0250bc 100644 --- a/apps/as/src/pages/checklist-management/model/build-rvw-display-rows.ts +++ b/apps/as/src/pages/checklist-management/model/build-rvw-display-rows.ts @@ -49,21 +49,15 @@ export function formatTreeBranchPrefix(depth: number): string { return ""; } -function compareSortSeq(a: CklstRvwRow, b: CklstRvwRow): number { - return (a.sortSeq ?? 0) - (b.sortSeq ?? 0); -} - export function buildRvwDisplayRows(rows: CklstRvwRow[]): CklstRvwDisplayRow[] { const result: CklstRvwDisplayRow[] = []; - const roots = rows - .filter((row) => (row.rvwCnLvNo ?? 1) === 1 && row.upCklstRvwSn == null) - .sort(compareSortSeq); + const roots = rows.filter((row) => (row.rvwCnLvNo ?? 1) === 1 && row.upCklstRvwSn == null); function appendSubtree(parent: CklstRvwRow, depth: number) { - const children = rows - .filter((row) => row.upCklstRvwSn != null && row.upCklstRvwSn === parent.cklstRvwSn) - .sort(compareSortSeq); + const children = rows.filter( + (row) => row.upCklstRvwSn != null && row.upCklstRvwSn === parent.cklstRvwSn, + ); for (const child of children) { result.push({ row: child, depth }); @@ -82,7 +76,7 @@ export function buildRvwDisplayRows(rows: CklstRvwRow[]): CklstRvwDisplayRow[] { (row) => !result.some((display) => display.row.rowKey === row.rowKey), ); - for (const orphan of orphans.sort(compareSortSeq)) { + for (const orphan of orphans) { result.push({ row: orphan, depth: Math.max(0, (orphan.rvwCnLvNo ?? 1) - 1) }); } diff --git a/apps/as/src/pages/checklist-management/ui/cklst-rvw-grid.tsx b/apps/as/src/pages/checklist-management/ui/cklst-rvw-grid.tsx index 7b44ba2b5..bf9884324 100644 --- a/apps/as/src/pages/checklist-management/ui/cklst-rvw-grid.tsx +++ b/apps/as/src/pages/checklist-management/ui/cklst-rvw-grid.tsx @@ -279,12 +279,13 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) { ) : (
- - - - + + + + + - + @@ -292,6 +293,7 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) { 주요 검토내용 사용 여부 체크 사용 + 정렬 순서 구분 관리 diff --git a/apps/as/src/pages/checklist-management/ui/cklst-rvw-row.tsx b/apps/as/src/pages/checklist-management/ui/cklst-rvw-row.tsx index c8bc7cb76..c31a6ee2a 100644 --- a/apps/as/src/pages/checklist-management/ui/cklst-rvw-row.tsx +++ b/apps/as/src/pages/checklist-management/ui/cklst-rvw-row.tsx @@ -107,6 +107,20 @@ function CklstRvwRowComponent({ + + + onChange({ + sortSeq: e.target.value ? Number(e.target.value) : undefined, + isDirty: true, + }) + } + className="w-20" + /> + {levelLabel}
From 399645a80a4a1f5de8dc1c6bf9db4cdeeec63cd6 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 14:43:55 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[as]=20feat:=20=EC=84=A4=EA=B3=84?= =?UTF-8?q?=EC=9E=90=20=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema.ts | 5 ++++- apps/as/src/pages/instl-sncn/lib/instl-sncn-regist-schema.ts | 5 ++++- .../src/widgets/pbadms-sprt/ui/contact-info-form-section.tsx | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) 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 332ef9e14..276fe6e9c 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 @@ -10,7 +10,10 @@ export const fnrsCnsltnRegistSchema = z.object({ cfmtnBgtBizNo: z.string().optional(), bgtAplyYr: z.string().optional(), dsgnrNm: z.string().min(1, "설계자 이름은 필수입니다"), - dsgnrTelno: z.string().min(1, "설계자 전화번호는 필수입니다"), + dsgnrTelno: z + .string() + .min(1, "설계자 전화번호는 필수입니다") + .regex(/^[0-9-]+$/, "숫자와 하이픈(-)만 입력 가능합니다"), dsgnrOgdpNm: z.string().min(1, "설계자 소속은 필수입니다"), dsgnrEmlAddr: z .string() 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 17badc640..ae55e92b9 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 @@ -9,7 +9,10 @@ export const instlSncnRegistSchema = z.object({ taskSeComCd: z.array(z.string()).min(1, "구분은 필수입니다"), sncnSeComCd: z.string().min(1, "사업구분은 필수입니다"), dsgnrNm: z.string().min(1, "설계자 이름은 필수입니다"), - dsgnrTelno: z.string().min(1, "설계자 전화번호는 필수입니다"), + dsgnrTelno: z + .string() + .min(1, "설계자 전화번호는 필수입니다") + .regex(/^[0-9-]+$/, "숫자와 하이픈(-)만 입력 가능합니다"), dsgnrOgdpNm: z.string().min(1, "설계자 소속은 필수입니다"), dsgnrEmlAddr: z .string() diff --git a/apps/as/src/widgets/pbadms-sprt/ui/contact-info-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/contact-info-form-section.tsx index 7787f7a40..1d6e31a16 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/contact-info-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/contact-info-form-section.tsx @@ -70,6 +70,8 @@ function ContactInfoFormSection({ fieldPrefix }: ContactInfoFormSectionProps) { id={field.name} placeholder="설계자 전화번호" aria-invalid={fieldState.invalid} + maxLength={13} + onChange={(e) => field.onChange(e.target.value.replace(/[^0-9-]/g, ""))} /> From 8d29f2510156d2199e499dc735005b5c63a3f429 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 13 Jul 2026 16:43:01 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[as]=20feat:=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=A0=84=EC=B2=B4=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/components/checklist-task-panel.tsx | 44 ++++- .../ui/components/checklist-task-panel.tsx | 44 ++++- .../pbadms-sprt/ui/checklist-form-section.tsx | 152 ++++++++++-------- 3 files changed, 163 insertions(+), 77 deletions(-) diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/components/checklist-task-panel.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/components/checklist-task-panel.tsx index 4e0100089..1e4d186e6 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/components/checklist-task-panel.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/components/checklist-task-panel.tsx @@ -2,6 +2,7 @@ import type { Dispatch, SetStateAction } from "react"; import type { CklstRvwRsltTreeDto } from "@/entities/fnrs-cnsltn"; import { + Button, cn, Table, TableBody, @@ -36,6 +37,24 @@ function ChecklistTaskPanel({ const currentUserReviewStep = userRoleCode ? ROLE_TO_REVIEW_STEP[userRoleCode] : undefined; const isStepEditable = currentUserReviewStep === selectedReviewStep; + function handleSelectAll(value: "Y" | "N") { + if (!isStepEditable) return; + setFormState((prev) => { + const nextResults = { ...prev.results }; + selectedRows + .filter((row) => row.bchckUseYn === "Y") + .forEach((row) => { + nextResults[buildChecklistRowKey(row, selectedReviewStep)] = value; + }); + return patchChecklistFormState( + prev, + { results: nextResults }, + allRows, + selectedReviewStep, + ); + }); + } + return ( <>
@@ -54,8 +73,27 @@ function ChecklistTaskPanel({ ) : null} - 예 - 아니오 + + + + + + @@ -131,7 +169,7 @@ function ChecklistTaskPanel({