From 07a625e03b071660680ea0af0063e2ccfe37b62a Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 7 Aug 2026 15:34:26 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[as]=20rename:=20SectionCollapsible=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20shared=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elapr/ui/elapr-doc-prgrs-status.tsx | 41 ++++++---- .../design-cnstn/ui/design-view-page.tsx | 5 +- .../ui/sections/attachments-section.tsx | 2 +- .../ui/sections/basic-info-form-section.tsx | 2 +- .../ui/sections/basic-info-section.tsx | 2 +- .../ui/sections/business-scale-section.tsx | 3 +- .../ui/sections/contact-info-section.tsx | 2 +- .../sections/tech-review-opinion-section.tsx | 2 +- .../pages/fnrs-cnsltn/ui/fnrs-view-page.tsx | 6 +- .../ui/sections/attachments-section.tsx | 2 +- .../ui/sections/basic-info-form-section.tsx | 2 +- .../ui/sections/basic-info-section.tsx | 2 +- .../ui/sections/business-cost-section.tsx | 2 +- .../ui/sections/business-scale-section.tsx | 2 +- .../ui/sections/checklist-section.tsx | 2 +- .../ui/sections/contact-info-section.tsx | 2 +- .../ui/sections/fnrs-shar-section.tsx | 3 +- .../sections/tech-review-opinion-section.tsx | 2 +- .../pages/instl-sncn/ui/instl-view-page.tsx | 6 +- .../ui/sections/attachments-section.tsx | 2 +- .../ui/sections/basic-info-form-section.tsx | 2 +- .../ui/sections/basic-info-section.tsx | 2 +- .../ui/sections/business-cost-section.tsx | 2 +- .../ui/sections/business-scale-section.tsx | 2 +- .../ui/sections/checklist-section.tsx | 2 +- .../ui/sections/contact-info-section.tsx | 2 +- .../sections/tech-review-opinion-section.tsx | 2 +- .../ui/previous-plan-lookup-dialog.tsx | 2 +- .../ui/sections/attachments-section.tsx | 2 +- .../ui/sections/basic-info-form-section.tsx | 2 +- .../ui/sections/basic-info-section.tsx | 2 +- .../sections/biz-cst-matrix-form-section.tsx | 3 +- .../ui/sections/biz-cst-matrix-section.tsx | 3 +- .../ui/sections/checklist-section.tsx | 2 +- .../ui/sections/contact-info-section.tsx | 2 +- .../ui/sections/fnrs-cst-form-section.tsx | 3 +- .../ui/sections/fnrs-cst-section.tsx | 3 +- .../ui/sections/outline-form-section.tsx | 2 +- .../ui/sections/outline-section.tsx | 3 +- .../sections/tech-review-opinion-section.tsx | 2 +- .../ui/sections/tmplnt-form-section.tsx | 2 +- .../ui/sections/tmplnt-section.tsx | 3 +- .../ui/sewer-bsc-plan-view-page.tsx | 6 +- apps/as/src/shared/ui/index.ts | 1 + .../ui/section-collapsible.tsx | 74 +++++++++---------- .../bns-picker/ui/bns-picker-dialog.tsx | 2 +- apps/as/src/widgets/cnfm/ui/attachments.tsx | 1 + apps/as/src/widgets/pbadms-sprt/index.ts | 1 - .../ui/attachments-form-section.tsx | 5 +- .../ui/business-cost-form-section.tsx | 5 +- .../ui/business-scale-form-section.tsx | 5 +- .../pbadms-sprt/ui/checklist-form-section.tsx | 5 +- .../ui/contact-info-form-section.tsx | 2 +- .../pbadms-sprt/ui/fnrs-shar-form-section.tsx | 2 +- .../ui/privacy-consent-section.tsx | 5 +- .../ui/splmnt-dmnd-form-section.tsx | 3 +- .../ui/supplement-demand-section.tsx | 3 +- 57 files changed, 133 insertions(+), 129 deletions(-) rename apps/as/src/{widgets/pbadms-sprt => shared}/ui/section-collapsible.tsx (96%) diff --git a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx index 731e39a07..77ec13b78 100644 --- a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx @@ -4,7 +4,9 @@ import { Fragment } from "react"; import { ChevronRight } from "lucide-react"; +import { useUserStore } from "@/shared/lib"; import type { ElaprDocPrgrsClientResponseDto } from "@/shared/model"; +import { SectionCollapsible } from "@/shared/ui"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; import { cn, Skeleton } from "@common/components"; @@ -61,16 +63,23 @@ function ElaprDocPrgrsStep({ item }: { item: ElaprDocPrgrsClientResponseDto }) { } function ElaprDocPrgrsStatus({ elaprDocNo }: ElaprDocPrgrsStatusProps) { + const userInfo = useUserStore((state) => state.userInfo); const { data, isLoading } = useElaprDocPrgrsByDocNo(elaprDocNo); const steps = sortByAtrzSeqNo(data ?? []); + if (userInfo?.dtlInstCd !== "ROLE_USER01") { + return null; + } + if (isLoading) { return ( -
- - - -
+ +
+ + + +
+
); } @@ -79,16 +88,18 @@ function ElaprDocPrgrsStatus({ elaprDocNo }: ElaprDocPrgrsStatusProps) { } return ( -
- {steps.map((item, index) => ( - -
- {index > 0 && } -
- -
- ))} -
+ +
+ {steps.map((item, index) => ( + +
+ {index > 0 && } +
+ +
+ ))} +
+
); } diff --git a/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx b/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx index aefd42317..c900ab530 100644 --- a/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx +++ b/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx @@ -6,7 +6,6 @@ import { useUserStore } from "@/shared/lib"; import { useDesignCnstnMng } from "@/entities/design-cnstn"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; @@ -80,9 +79,7 @@ function DesignViewPage({ id }: DesignViewPageProps) {
- - - + - - - + 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 0ac529c18..b55b87c47 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 @@ -1,12 +1,12 @@ "use client"; import { formatFileSize } from "@/shared/lib/file-utils"; +import { SectionCollapsible } from "@/shared/ui"; import { useDownloadAttachmentByAtflCd } from "@/entities/attach"; import type { AtflMngClientResponseDto, EsntlAtchFileMngTreeResponseDto, } from "@/entities/fnrs-cnsltn"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; interface AttachmentsSectionProps { diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-form-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-form-section.tsx index 751312ac6..759b1f0df 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-form-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-form-section.tsx @@ -3,10 +3,10 @@ import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; +import { SectionCollapsible } from "@/shared/ui"; import { Field, FieldError, FieldGroup, FieldLabel, FieldSet } from "@/shared/ui/field"; import { findCodeLabel, useCodeList } from "@/entities/code"; import { type BnsApplyData, BnsPickerDialog } from "@/widgets/bns-picker"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Button, Checkbox, diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-section.tsx index 939af68fd..48f14eceb 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/basic-info-section.tsx @@ -1,7 +1,7 @@ "use client"; +import { SectionCollapsible } from "@/shared/ui"; import type { FnrsCnsltnMngResponseDto } from "@/entities/fnrs-cnsltn"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; interface BasicInfoSectionProps { diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-cost-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-cost-section.tsx index 285fe8bed..d880811b6 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-cost-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-cost-section.tsx @@ -2,9 +2,9 @@ import { useMemo } from "react"; +import { SectionCollapsible } from "@/shared/ui"; import type { BizCstTreeDto } from "@/entities/fnrs-cnsltn"; import { calcDiffPercent, fmtNumber, indentPx } from "@/widgets/pbadms-sprt"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; import { sortBizCstListByTreePath } from "../../lib/business-cost-utils"; diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-scale-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-scale-section.tsx index f8849cf8f..c1f5a28af 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-scale-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/business-scale-section.tsx @@ -1,8 +1,8 @@ "use client"; +import { SectionCollapsible } from "@/shared/ui"; import type { BizSclTreeDto } from "@/entities/fnrs-cnsltn"; import { fmtNumber, indentPx } from "@/widgets/pbadms-sprt"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; interface BusinessScaleSectionProps { 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 09aa48052..e50f5a6b8 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 @@ -2,8 +2,8 @@ import { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from "react"; +import { SectionCollapsible } from "@/shared/ui"; import type { CklstRvwRsltTreeDto } from "@/entities/fnrs-cnsltn"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components"; import { diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/contact-info-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/contact-info-section.tsx index 7443460e8..f5273fb33 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/contact-info-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/contact-info-section.tsx @@ -1,7 +1,7 @@ "use client"; +import { SectionCollapsible } from "@/shared/ui"; import type { FnrsCnsltnMngResponseDto } from "@/entities/fnrs-cnsltn"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; interface ContactInfoSectionProps { diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/sections/fnrs-shar-section.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/sections/fnrs-shar-section.tsx index 198261c41..ad83c53e4 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/sections/fnrs-shar-section.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/sections/fnrs-shar-section.tsx @@ -2,8 +2,9 @@ import { useMemo } from "react"; +import { SectionCollapsible } from "@/shared/ui"; import type { FnrsSharBizCstTreeDto } from "@/entities/fnrs-shar-se"; -import { fmtNumber, indentPx, SectionCollapsible } from "@/widgets/pbadms-sprt"; +import { fmtNumber, indentPx } from "@/widgets/pbadms-sprt"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; interface FnrsSharSectionProps { 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 f8d416af1..bdbd4e622 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 @@ -7,10 +7,10 @@ import { Paperclip } from "lucide-react"; import { PROXY_HOST } from "@/shared/config"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { TextShimmer } from "@/shared/ui"; +import { SectionCollapsible } from "@/shared/ui"; import { Field, FieldLabel, FieldSet } from "@/shared/ui/field"; import { attachService } from "@/entities/attach"; import type { FnrsCnsltnRvwOpnnResponseDto } from "@/entities/fnrs-cnsltn"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Button, RadioGroup, diff --git a/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx b/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx index 094072f1a..88677bbf3 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx @@ -6,7 +6,7 @@ import { useUserStore } from "@/shared/lib"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useInstlSncnDetail } from "@/entities/instl-sncn"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { SectionCollapsible, SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { SupplementDemandSection } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; @@ -90,9 +90,7 @@ function InstlViewPage({ id }: InstlViewPageProps) {
- - - + - - - + diff --git a/apps/as/src/shared/ui/index.ts b/apps/as/src/shared/ui/index.ts index 926342b84..577618146 100644 --- a/apps/as/src/shared/ui/index.ts +++ b/apps/as/src/shared/ui/index.ts @@ -1,4 +1,5 @@ export { NoData } from "./no-data"; export { PageLayout } from "./page-layout"; +export { SectionCollapsible } from "./section-collapsible"; export type { TextShimmerProps } from "./text-shimmer"; export { TEXT_SHIMMER_CLASS, TextShimmer } from "./text-shimmer"; diff --git a/apps/as/src/widgets/pbadms-sprt/ui/section-collapsible.tsx b/apps/as/src/shared/ui/section-collapsible.tsx similarity index 96% rename from apps/as/src/widgets/pbadms-sprt/ui/section-collapsible.tsx rename to apps/as/src/shared/ui/section-collapsible.tsx index 2410ff325..2d9d6f27e 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/section-collapsible.tsx +++ b/apps/as/src/shared/ui/section-collapsible.tsx @@ -1,37 +1,37 @@ -"use client"; - -import type { ReactNode } from "react"; - -import { ChevronDown } from "lucide-react"; - -import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from "@common/components"; - -interface SectionCollapsibleProps { - title: ReactNode; - defaultOpen?: boolean; - children: ReactNode; -} - -function SectionCollapsible({ title, defaultOpen = true, children }: SectionCollapsibleProps) { - return ( - - - - - {children} - - ); -} - -export { SectionCollapsible }; +"use client"; + +import type { ReactNode } from "react"; + +import { ChevronDown } from "lucide-react"; + +import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from "@common/components"; + +interface SectionCollapsibleProps { + title: ReactNode; + defaultOpen?: boolean; + children: ReactNode; +} + +function SectionCollapsible({ title, defaultOpen = true, children }: SectionCollapsibleProps) { + return ( + + + + + {children} + + ); +} + +export { SectionCollapsible }; diff --git a/apps/as/src/widgets/bns-picker/ui/bns-picker-dialog.tsx b/apps/as/src/widgets/bns-picker/ui/bns-picker-dialog.tsx index a259c8aca..4153aec1a 100644 --- a/apps/as/src/widgets/bns-picker/ui/bns-picker-dialog.tsx +++ b/apps/as/src/widgets/bns-picker/ui/bns-picker-dialog.tsx @@ -12,12 +12,12 @@ import { import { useDataTable } from "@/shared/lib"; import type { PageResponse } from "@/shared/model"; +import { SectionCollapsible } from "@/shared/ui"; import { NoData } from "@/shared/ui/no-data"; import type { CmCdAdstDstrComboResponseDto } from "@/entities/adstdstr"; import { useFindGugunCombo, useFindSidoCombo } from "@/entities/adstdstr"; import type { BnsDetailResponseDto, BnsListResponseDto, BnsSearchDto } from "@/entities/bns-if"; import { buildBnsSearchDto, useBnsDetail, useBnsList } from "@/entities/bns-if"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Button, Dialog, diff --git a/apps/as/src/widgets/cnfm/ui/attachments.tsx b/apps/as/src/widgets/cnfm/ui/attachments.tsx index dcd68209e..4a585d231 100644 --- a/apps/as/src/widgets/cnfm/ui/attachments.tsx +++ b/apps/as/src/widgets/cnfm/ui/attachments.tsx @@ -1,4 +1,5 @@ import React from "react"; + import { formatFileSize } from "@/shared/lib/file-utils"; type EtcFile = { diff --git a/apps/as/src/widgets/pbadms-sprt/index.ts b/apps/as/src/widgets/pbadms-sprt/index.ts index 8f2c44b95..5b759af65 100644 --- a/apps/as/src/widgets/pbadms-sprt/index.ts +++ b/apps/as/src/widgets/pbadms-sprt/index.ts @@ -39,7 +39,6 @@ export { type PbadmsTaskApplySelection, } from "./ui/pbadms-task-apply-dialog"; export { PrivacyConsentSection } from "./ui/privacy-consent-section"; -export { SectionCollapsible } from "./ui/section-collapsible"; export { SplmntDmndDialog, type SplmntDmndDialogConfirmItem, 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 03d812211..b4bce6b8a 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 @@ -9,6 +9,7 @@ import { ENDPOINT } from "@/shared/config"; import { useTus } from "@/shared/lib"; import type { AtflMngClientResponseDto, EsntlAtchFileMngTreeResponseDto } from "@/shared/model"; import { TextShimmer } from "@/shared/ui"; +import { SectionCollapsible } from "@/shared/ui"; import { attachService } from "@/entities/attach"; import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { @@ -29,9 +30,7 @@ import { type FieldPrefix, isEtcFileKey, toFileAccept, -} from "../lib/attachments-form-utils"; - -import { SectionCollapsible } from "./section-collapsible"; +} from "../lib/attachments-form-utils"; interface AttachmentsFormData { esntlAtchFileList: Array<{ diff --git a/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx index 51d7a82a1..969a4613b 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/business-cost-form-section.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo } from "react"; import { Controller, useFormContext, useWatch } from "react-hook-form"; +import { SectionCollapsible } from "@/shared/ui"; import { Field } from "@/shared/ui/field"; import { useAcntgSeMngTree } from "@/entities/acntg"; import { @@ -17,9 +18,7 @@ import { } from "@common/components"; import { buildAcntgCostItems, recalculateBizCstTotals } from "../lib/business-cost-form-utils"; -import { calcDiffPercent } from "../lib/format-utils"; - -import { SectionCollapsible } from "./section-collapsible"; +import { calcDiffPercent } from "../lib/format-utils"; interface BusinessCostFormData { bizCstList: Array<{ diff --git a/apps/as/src/widgets/pbadms-sprt/ui/business-scale-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/business-scale-form-section.tsx index 63698dd08..2bee5e104 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/business-scale-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/business-scale-form-section.tsx @@ -3,6 +3,7 @@ import { useEffect, useMemo } from "react"; import { Controller, useFormContext, useWatch } from "react-hook-form"; +import { SectionCollapsible } from "@/shared/ui"; import { Field } from "@/shared/ui/field"; import { useCnstpSeMngTree } from "@/entities/cnstp"; import { @@ -16,9 +17,7 @@ import { TableRow, } from "@common/components"; -import { buildCnstpScaleItems, recalculateBzScaleTotals } from "../lib/business-scale-form-utils"; - -import { SectionCollapsible } from "./section-collapsible"; +import { buildCnstpScaleItems, recalculateBzScaleTotals } from "../lib/business-scale-form-utils"; interface BusinessScaleFormData { bzScaleList: Array<{ diff --git a/apps/as/src/widgets/pbadms-sprt/ui/checklist-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/checklist-form-section.tsx index 36fb793ae..4b9866877 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/checklist-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/checklist-form-section.tsx @@ -5,6 +5,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form"; import { useQuery } from "@tanstack/react-query"; +import { SectionCollapsible } from "@/shared/ui"; import { Field, FieldLabel } from "@/shared/ui/field"; import { cklstMngService, type CklstTreeNode } from "@/entities/cklst"; import { useCodeList } from "@/entities/code"; @@ -29,9 +30,7 @@ import { buildChecklistTabs, normalizeTaskSeComCdArray, syncCklstResultListFromTree, -} from "../lib/checklist-form-utils"; - -import { SectionCollapsible } from "./section-collapsible"; +} from "../lib/checklist-form-utils"; type FieldPrefix = "fnrsCnsltnMng" | "instlSncnMng" | "designCnstnMng" | "sewerBscPlan"; 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 c3dc02fcc..0f5b659a0 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 @@ -3,8 +3,8 @@ import { Controller, useFormContext } from "react-hook-form"; import { useUserStore } from "@/shared/lib"; +import { SectionCollapsible } from "@/shared/ui"; import { Field, FieldError } from "@/shared/ui/field"; -import { SectionCollapsible } from "@/widgets/pbadms-sprt"; import { Input, Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; type FieldPrefix = diff --git a/apps/as/src/widgets/pbadms-sprt/ui/fnrs-shar-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/fnrs-shar-form-section.tsx index 5365db9c9..0ab921ef9 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/fnrs-shar-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/fnrs-shar-form-section.tsx @@ -5,6 +5,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form"; import { getDefaultFileKey, useTus } from "@/shared/lib"; import { TextShimmer } from "@/shared/ui"; +import { SectionCollapsible } from "@/shared/ui"; import { Field } from "@/shared/ui/field"; import { useAcntgSeMngTree } from "@/entities/acntg"; import type { AtflMngClientResponseDto } from "@/entities/fnrs-cnsltn"; @@ -34,7 +35,6 @@ import { import { calcDiffPercent } from "../lib/format-utils"; import { BizCstTotalDisplayTable } from "./biz-cst-total-display-table"; -import { SectionCollapsible } from "./section-collapsible"; const SHAR_FILE_KEY_PREFIX = "fnrs-shar::"; diff --git a/apps/as/src/widgets/pbadms-sprt/ui/privacy-consent-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/privacy-consent-section.tsx index d51d9aeef..eefd96163 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/privacy-consent-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/privacy-consent-section.tsx @@ -2,6 +2,7 @@ import { Controller, useFormContext } from "react-hook-form"; +import { SectionCollapsible } from "@/shared/ui"; import { Field, FieldError, FieldLabel, FieldSet } from "@/shared/ui/field"; import { RadioGroup, @@ -11,9 +12,7 @@ import { TableHead, TableHeader, TableRow, -} from "@common/components"; - -import { SectionCollapsible } from "./section-collapsible"; +} from "@common/components"; interface PrivacyConsentFormData { privacyConsent: "Y" | "N"; diff --git a/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-form-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-form-section.tsx index 6a8d94670..a8efff36d 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-form-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/splmnt-dmnd-form-section.tsx @@ -7,6 +7,7 @@ import { Paperclip } from "lucide-react"; import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib"; import { TextShimmer } from "@/shared/ui"; +import { SectionCollapsible } from "@/shared/ui"; import { attachService, useDownloadAttachmentByAtflCd } from "@/entities/attach"; import { Badge, @@ -20,8 +21,6 @@ import { Textarea, } from "@common/components"; -import { SectionCollapsible } from "./section-collapsible"; - const SPLMNT_CMPTN_FILE_KEY_PREFIX = "splmnt-cmptn::"; interface UploadedSplmntFile { diff --git a/apps/as/src/widgets/pbadms-sprt/ui/supplement-demand-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/supplement-demand-section.tsx index 8c7d6a8c7..1da222fdf 100644 --- a/apps/as/src/widgets/pbadms-sprt/ui/supplement-demand-section.tsx +++ b/apps/as/src/widgets/pbadms-sprt/ui/supplement-demand-section.tsx @@ -4,6 +4,7 @@ import { Paperclip } from "lucide-react"; import { dayjs } from "@/shared/lib"; import type { SplmntDmndResponseExtension, SplmntDmndSaveBase } from "@/shared/model"; +import { SectionCollapsible } from "@/shared/ui"; import { useDownloadAttachmentByAtflCd } from "@/entities/attach"; import { Badge, @@ -15,8 +16,6 @@ import { TableRow, } from "@common/components"; -import { SectionCollapsible } from "./section-collapsible"; - type SplmntDmndItem = SplmntDmndSaveBase & SplmntDmndResponseExtension; interface SupplementDemandSectionProps { From 82d67bb4c6aa849f98238efa4312c18581390be6 Mon Sep 17 00:00:00 2001 From: rsone001 Date: Fri, 7 Aug 2026 17:22:08 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[ST]=20-=20fix:=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=ED=95=AD=EB=AA=A9=20=EB=AA=85=EC=B9=AD=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/statistics/VerificationSelectionReport.tsx | 4 ++-- .../statistics/verification/VerificationArticleManagement.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/st/src/components/statistics/VerificationSelectionReport.tsx b/apps/st/src/components/statistics/VerificationSelectionReport.tsx index 68b0f6a5d..85bc298e1 100644 --- a/apps/st/src/components/statistics/VerificationSelectionReport.tsx +++ b/apps/st/src/components/statistics/VerificationSelectionReport.tsx @@ -52,8 +52,8 @@ const TYPE_CODE_COLOR_MAP: Record = { const TYPE_CODE_NAME_MAP: Record = { NV: "일반", - IMR: "I-MR", - HB: "HB", + IMR: "시계열", + HB: "모집단", }; const PAGE_SIZE = 100; diff --git a/apps/st/src/components/statistics/verification/VerificationArticleManagement.tsx b/apps/st/src/components/statistics/verification/VerificationArticleManagement.tsx index 99b8391ed..b504b9734 100644 --- a/apps/st/src/components/statistics/verification/VerificationArticleManagement.tsx +++ b/apps/st/src/components/statistics/verification/VerificationArticleManagement.tsx @@ -39,8 +39,8 @@ import { VerificationTargetDialog } from "./VerificationTargetDialog"; import DataGrid from "@/components/module/DataGrid"; const TAB_CONFIG = { - HB: { value: "HB", label: "H-B", columnInfo: hbArticleColumnInfo }, - IMR: { value: "IMR", label: "I-MR", columnInfo: imrArticleColumnInfo }, + HB: { value: "HB", label: "모집단", columnInfo: hbArticleColumnInfo }, + IMR: { value: "IMR", label: "시계열", columnInfo: imrArticleColumnInfo }, GENERAL: { value: "GENERAL", label: "일반", From 0459c0b23157be5fe8a6148524b21da44db20259 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 7 Aug 2026 17:01:00 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[as]=20feat:=20=ED=96=89=EC=A0=95=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EC=97=85=EB=AC=B4=EB=B3=84=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EB=82=B4=20=EC=A7=84=ED=96=89=EC=83=81=ED=99=A9=20ui=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/fnrs-cnsltn-interface.ts | 1 + .../instl-sncn/model/instl-sncn-interface.ts | 2 + .../model/sewer-bsc-plan-interface.ts | 1 + .../src/pages/design-cnstn/model/columns.tsx | 60 +++++---------- .../src/pages/fnrs-cnsltn/model/columns.tsx | 77 +++++++++++-------- .../as/src/pages/instl-sncn/model/columns.tsx | 69 ++++++++++------- .../pages/sewer-bsc-plan/model/columns.tsx | 76 +++++++++--------- 7 files changed, 145 insertions(+), 141 deletions(-) diff --git a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts index d0a43a71b..44045341d 100644 --- a/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts +++ b/apps/as/src/entities/fnrs-cnsltn/model/fnrs-cnsltn-interface.ts @@ -92,6 +92,7 @@ interface FnrsCnsltnMngResponseExtension { mdfcnDt?: string; sido?: string; gugun?: string; + techRplyHstryYn?: "Y" | "N"; } /** 백엔드 FnrsCnsltnMngResponseDto */ diff --git a/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts b/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts index 4bbd7ae24..a5e4ce791 100644 --- a/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts +++ b/apps/as/src/entities/instl-sncn/model/instl-sncn-interface.ts @@ -148,6 +148,8 @@ export interface InstlSncnMngResponseDto extends InstlSncnMngSaveRequestDto { taskSeCdNm: string; /** 진행상태공통코드명 */ prgrsSttsCdNm: string; + /** 기술검토회신이력여부 */ + techRplyHstryYn: "Y" | "N"; /** 재이용시설구분공통코드명 */ rtlztnFcltSeComNm: string; /** 재이용용도구분공통코드명 */ 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 0e8025278..a6833bec0 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 @@ -61,6 +61,7 @@ export interface SewerBscPlanResponseDto { rgtr?: string; rgtrNm?: string; mdfcnDt?: string; + techRplyHstryYn?: "Y" | "N"; } /** 백엔드 SewerBscPlanDtlResponseDto */ diff --git a/apps/as/src/pages/design-cnstn/model/columns.tsx b/apps/as/src/pages/design-cnstn/model/columns.tsx index 6cfdb7f70..c74345041 100644 --- a/apps/as/src/pages/design-cnstn/model/columns.tsx +++ b/apps/as/src/pages/design-cnstn/model/columns.tsx @@ -11,62 +11,48 @@ import { Badge } from "@common/components"; const columnHelper = createColumnHelper(); const MAIN_STEP_LABELS = { - save: "저장", - apply: "신청완료", - review: "설계자문 검토", - reply: "설계검토 회신", + apply: "설계자문 신청", + techReview: "기술검토", + reply: "검토결과 회신", } as const; -const REVIEW_STATUS_CODES = new Set(["CS", "CR"]); - -type BadgeState = "completed" | "current" | "pending" | "action"; +type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { label: string; state: BadgeState; } -function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): ProgressBadge[] { - const reviewLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.review; - const replyLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.reply; - +function buildProgressBadges(prgrsSttsComCd: string): ProgressBadge[] { if (prgrsSttsComCd === "CV") { return [ - { label: MAIN_STEP_LABELS.save, state: "current" }, { label: MAIN_STEP_LABELS.apply, state: "pending" }, - { label: MAIN_STEP_LABELS.review, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (prgrsSttsComCd === "CT") { + if (prgrsSttsComCd === "ER" || prgrsSttsComCd === "CS" || prgrsSttsComCd === "SL") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, - { label: MAIN_STEP_LABELS.apply, state: "current" }, - { label: MAIN_STEP_LABELS.review, state: "pending" }, - { label: MAIN_STEP_LABELS.reply, state: "pending" }, - ]; - } - - if (REVIEW_STATUS_CODES.has(prgrsSttsComCd)) { - return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: reviewLabel, state: "action" }, + { label: MAIN_STEP_LABELS.techReview, state: "current" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (prgrsSttsComCd === "CP" || prgrsSttsComCd === "EP") { + if (prgrsSttsComCd === "CP") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.review, state: "completed" }, - { label: replyLabel, state: "action" }, + { label: MAIN_STEP_LABELS.techReview, state: "completed" }, + { label: MAIN_STEP_LABELS.reply, state: "completed" }, ]; } - return [{ label: prgrsSttsCdNm || prgrsSttsComCd || "-", state: "current" }]; + return [ + { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; } function getBadgeColor(state: BadgeState) { @@ -77,8 +63,6 @@ function getBadgeColor(state: BadgeState) { return "blue"; case "pending": return "lightGray"; - case "action": - return "orange"; } } @@ -88,11 +72,10 @@ function formatOptional(value: string | undefined) { interface ProgressStepBadgeProps { prgrsSttsComCd: string; - prgrsSttsCdNm: string; } -function ProgressStepBadge({ prgrsSttsComCd, prgrsSttsCdNm }: ProgressStepBadgeProps) { - const badges = buildProgressBadges(prgrsSttsComCd, prgrsSttsCdNm); +function ProgressStepBadge({ prgrsSttsComCd }: ProgressStepBadgeProps) { + const badges = buildProgressBadges(prgrsSttsComCd); return (
@@ -142,12 +125,7 @@ export function createDesignCnstnColumns() { id: "progress", header: "진행상황", meta: { align: "center" }, - cell: ({ row }) => ( - - ), + cell: ({ row }) => , }), columnHelper.accessor("regDt", { header: "신청일", diff --git a/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx b/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx index ba660c342..a29e2096c 100644 --- a/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx @@ -11,29 +11,24 @@ import { Badge } from "@common/components"; const columnHelper = createColumnHelper(); const MAIN_STEP_LABELS = { - save: "저장", - apply: "신청완료", + apply: "재원협의 신청", + envoReview: "환경청 검토", techReview: "기술검토", reply: "검토결과 회신", } as const; -const TECH_REVIEW_STATUS_CODES = new Set(["LE", "ER", "EE", "TE", "TS", "TR", "TP"]); - -type BadgeState = "completed" | "current" | "pending" | "action"; +type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { label: string; state: BadgeState; } -function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): ProgressBadge[] { - const techReviewLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.techReview; - const replyLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.reply; - +function buildProgressBadges(prgrsSttsComCd: string, techRplyHstryYn: "Y" | "N"): ProgressBadge[] { if (prgrsSttsComCd === "SV") { return [ - { label: MAIN_STEP_LABELS.save, state: "current" }, { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; @@ -41,41 +36,61 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro if (prgrsSttsComCd === "ST") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "current" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (TECH_REVIEW_STATUS_CODES.has(prgrsSttsComCd)) { + if (prgrsSttsComCd === "LE") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: techReviewLabel, state: "action" }, + { label: MAIN_STEP_LABELS.envoReview, state: "current" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } + if (prgrsSttsComCd === "ER" || prgrsSttsComCd === "TE" || prgrsSttsComCd === "SL") { + return [ + { label: MAIN_STEP_LABELS.apply, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { label: MAIN_STEP_LABELS.techReview, state: "current" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; + } + + if (prgrsSttsComCd === "TP") { + return [ + { label: MAIN_STEP_LABELS.apply, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, + { label: MAIN_STEP_LABELS.reply, state: "current" }, + ]; + } + if (prgrsSttsComCd === "EP") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "completed" }, - { label: replyLabel, state: "action" }, - ]; - } - - if (prgrsSttsComCd === "TA" || prgrsSttsComCd === "TB") { - return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, - { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, { label: MAIN_STEP_LABELS.reply, state: "completed" }, ]; } - return [{ label: prgrsSttsCdNm || prgrsSttsComCd || "-", state: "current" }]; + return [ + { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; } function getBadgeColor(state: BadgeState) { @@ -86,8 +101,6 @@ function getBadgeColor(state: BadgeState) { return "blue"; case "pending": return "lightGray"; - case "action": - return "orange"; } } @@ -97,11 +110,11 @@ function formatOptional(value: string | undefined) { interface ProgressStepBadgeProps { prgrsSttsComCd: string; - prgrsSttsCdNm: string; + techRplyHstryYn: "Y" | "N"; } -function ProgressStepBadge({ prgrsSttsComCd, prgrsSttsCdNm }: ProgressStepBadgeProps) { - const badges = buildProgressBadges(prgrsSttsComCd, prgrsSttsCdNm); +function ProgressStepBadge({ prgrsSttsComCd, techRplyHstryYn }: ProgressStepBadgeProps) { + const badges = buildProgressBadges(prgrsSttsComCd, techRplyHstryYn); return (
@@ -154,7 +167,7 @@ export function createFnrsCnsltnColumns() { cell: ({ row }) => ( ), }), diff --git a/apps/as/src/pages/instl-sncn/model/columns.tsx b/apps/as/src/pages/instl-sncn/model/columns.tsx index 772e20df0..ee9ddb055 100644 --- a/apps/as/src/pages/instl-sncn/model/columns.tsx +++ b/apps/as/src/pages/instl-sncn/model/columns.tsx @@ -11,29 +11,24 @@ import { Badge } from "@common/components"; const columnHelper = createColumnHelper(); const MAIN_STEP_LABELS = { - save: "저장", - apply: "신청완료", + apply: "설치인가 신청", + envoReview: "환경청 검토", techReview: "기술검토", reply: "검토결과 회신", } as const; -const TECH_REVIEW_STATUS_CODES = new Set(["LE", "ER", "EE", "TE", "TS", "TR", "TP"]); - -type BadgeState = "completed" | "current" | "pending" | "action"; +type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { label: string; state: BadgeState; } -function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): ProgressBadge[] { - const techReviewLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.techReview; - const replyLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.reply; - +function buildProgressBadges(prgrsSttsComCd: string, techRplyHstryYn: "Y" | "N"): ProgressBadge[] { if (prgrsSttsComCd === "SV") { return [ - { label: MAIN_STEP_LABELS.save, state: "current" }, { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; @@ -41,41 +36,61 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro if (prgrsSttsComCd === "ST") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "current" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (TECH_REVIEW_STATUS_CODES.has(prgrsSttsComCd)) { + if (prgrsSttsComCd === "LE") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: techReviewLabel, state: "action" }, + { label: MAIN_STEP_LABELS.envoReview, state: "current" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (prgrsSttsComCd === "EP") { + if (prgrsSttsComCd === "ER" || prgrsSttsComCd === "TE" || prgrsSttsComCd === "SL") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "completed" }, - { label: replyLabel, state: "action" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { label: MAIN_STEP_LABELS.techReview, state: "current" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (prgrsSttsComCd === "TA" || prgrsSttsComCd === "TB") { + if (prgrsSttsComCd === "TP" || prgrsSttsComCd === "EP") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.techReview, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, + { label: MAIN_STEP_LABELS.reply, state: "current" }, + ]; + } + + if (prgrsSttsComCd === "TA") { + return [ + { label: MAIN_STEP_LABELS.apply, state: "completed" }, + { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, { label: MAIN_STEP_LABELS.reply, state: "completed" }, ]; } - return [{ label: prgrsSttsCdNm || prgrsSttsComCd || "-", state: "current" }]; + return [ + { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; } function getBadgeColor(state: BadgeState) { @@ -86,8 +101,6 @@ function getBadgeColor(state: BadgeState) { return "blue"; case "pending": return "lightGray"; - case "action": - return "orange"; } } @@ -97,11 +110,11 @@ function formatOptional(value: string | undefined) { interface ProgressStepBadgeProps { prgrsSttsComCd: string; - prgrsSttsCdNm: string; + techRplyHstryYn: "Y" | "N"; } -function ProgressStepBadge({ prgrsSttsComCd, prgrsSttsCdNm }: ProgressStepBadgeProps) { - const badges = buildProgressBadges(prgrsSttsComCd, prgrsSttsCdNm); +function ProgressStepBadge({ prgrsSttsComCd, techRplyHstryYn }: ProgressStepBadgeProps) { + const badges = buildProgressBadges(prgrsSttsComCd, techRplyHstryYn); return (
@@ -154,7 +167,7 @@ export function createInstlSncnColumns() { cell: ({ row }) => ( ), }), 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 ec387c8cb..9ad252381 100644 --- a/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx @@ -11,30 +11,22 @@ import { Badge } from "@common/components"; const columnHelper = createColumnHelper(); const MAIN_STEP_LABELS = { - save: "저장", - apply: "신청완료", + apply: "기본계획 신청", envoReview: "환경청 검토", techReview: "기술검토", reply: "검토결과 회신", - supplement: "보완요청", } as const; -const TECH_REVIEW_STATUS_CODES = new Set(["ER", "TE", "TP"]); - -type BadgeState = "completed" | "current" | "pending" | "action"; +type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { label: string; state: BadgeState; } -function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): ProgressBadge[] { - const techReviewLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.techReview; - const replyLabel = prgrsSttsCdNm || MAIN_STEP_LABELS.reply; - +function buildProgressBadges(prgrsSttsComCd: string, techRplyHstryYn: "Y" | "N"): ProgressBadge[] { if (prgrsSttsComCd === "SV") { return [ - { label: MAIN_STEP_LABELS.save, state: "current" }, { label: MAIN_STEP_LABELS.apply, state: "pending" }, { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, @@ -42,57 +34,63 @@ function buildProgressBadges(prgrsSttsComCd: string, prgrsSttsCdNm: string): Pro ]; } + if (prgrsSttsComCd === "ST") { + return [ + { label: MAIN_STEP_LABELS.apply, state: "current" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; + } + if (prgrsSttsComCd === "LE") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, - { label: MAIN_STEP_LABELS.envoReview, state: "action" }, + { label: MAIN_STEP_LABELS.envoReview, state: "current" }, { label: MAIN_STEP_LABELS.techReview, state: "pending" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (prgrsSttsComCd === "SL") { + if (prgrsSttsComCd === "ER" || prgrsSttsComCd === "TE" || prgrsSttsComCd === "SL") { 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.supplement, state: "action" }, + { label: MAIN_STEP_LABELS.techReview, state: "current" }, { label: MAIN_STEP_LABELS.reply, state: "pending" }, ]; } - if (TECH_REVIEW_STATUS_CODES.has(prgrsSttsComCd)) { + if (prgrsSttsComCd === "TP") { return [ - { label: MAIN_STEP_LABELS.save, state: "completed" }, { label: MAIN_STEP_LABELS.apply, state: "completed" }, { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, - { label: techReviewLabel, state: "action" }, - { label: MAIN_STEP_LABELS.reply, state: "pending" }, - ]; - } - - if (prgrsSttsComCd === "TA") { - 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: MAIN_STEP_LABELS.reply, state: "completed" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, + { label: MAIN_STEP_LABELS.reply, state: "current" }, ]; } 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" }, + { + label: MAIN_STEP_LABELS.techReview, + state: techRplyHstryYn === "Y" ? "completed" : "pending", + }, + { label: MAIN_STEP_LABELS.reply, state: "completed" }, ]; } - return [{ label: prgrsSttsCdNm || prgrsSttsComCd || "-", state: "current" }]; + return [ + { label: MAIN_STEP_LABELS.apply, state: "pending" }, + { label: MAIN_STEP_LABELS.envoReview, state: "pending" }, + { label: MAIN_STEP_LABELS.techReview, state: "pending" }, + { label: MAIN_STEP_LABELS.reply, state: "pending" }, + ]; } function getBadgeColor(state: BadgeState) { @@ -103,8 +101,6 @@ function getBadgeColor(state: BadgeState) { return "blue"; case "pending": return "lightGray"; - case "action": - return "orange"; } } @@ -114,11 +110,11 @@ function formatOptional(value: string | undefined) { interface ProgressStepBadgeProps { prgrsSttsComCd: string; - prgrsSttsCdNm: string; + techRplyHstryYn: "Y" | "N"; } -function ProgressStepBadge({ prgrsSttsComCd, prgrsSttsCdNm }: ProgressStepBadgeProps) { - const badges = buildProgressBadges(prgrsSttsComCd, prgrsSttsCdNm); +function ProgressStepBadge({ prgrsSttsComCd, techRplyHstryYn }: ProgressStepBadgeProps) { + const badges = buildProgressBadges(prgrsSttsComCd, techRplyHstryYn); return (
@@ -177,7 +173,7 @@ export function createSewerBscPlanColumns() { cell: ({ row }) => ( ), }), From dbda77a75b0fefbc828551fe94523d293f5a9310 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 10 Aug 2026 15:27:53 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[as]=20feat:=20=EA=B2=B0=EC=9E=AC=ED=98=84?= =?UTF-8?q?=ED=99=A9=20ui=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/entities/elapr/lib/approval-utils.ts | 40 +++++++++++++++---- .../elapr/ui/elapr-doc-prgrs-status.tsx | 31 ++++++++------ .../as/src/pages/instl-sncn/model/columns.tsx | 2 +- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/apps/as/src/entities/elapr/lib/approval-utils.ts b/apps/as/src/entities/elapr/lib/approval-utils.ts index 00d7d7b2a..831c06949 100644 --- a/apps/as/src/entities/elapr/lib/approval-utils.ts +++ b/apps/as/src/entities/elapr/lib/approval-utils.ts @@ -6,11 +6,26 @@ export interface ApproverStatusConfig { } export const APPROVER_STATUS_MAP: Record = { - ATRZ_PRGRS_001: { label: "대기", badgeClass: "bg-gray-100 text-gray-700" }, - ATRZ_PRGRS_002: { label: "요청", badgeClass: "bg-yellow-100 text-yellow-700" }, - ATRZ_PRGRS_003: { label: "승인", badgeClass: "bg-green-100 text-green-700" }, - ATRZ_PRGRS_004: { label: "반려", badgeClass: "bg-red-100 text-red-700" }, - ATRZ_PRGRS_999: { label: "결재안함", badgeClass: "bg-blue-100 text-blue-700" }, + ATRZ_PRGRS_001: { + label: "대기", + badgeClass: "bg-gray-100 text-gray-700", + }, + ATRZ_PRGRS_002: { + label: "결재요청", + badgeClass: "bg-yellow-100 text-yellow-700", + }, + ATRZ_PRGRS_003: { + label: "결재승인", + badgeClass: "bg-green-100 text-green-700", + }, + ATRZ_PRGRS_004: { + label: "반려", + badgeClass: "bg-red-100 text-red-700", + }, + ATRZ_PRGRS_999: { + label: "결재안함", + badgeClass: "bg-blue-100 text-blue-700", + }, }; export function sortByAtrzSeqNo( @@ -19,10 +34,19 @@ export function sortByAtrzSeqNo( return list.slice().sort((a, b) => Number(a.atrzSeqNo ?? 0) - Number(b.atrzSeqNo ?? 0)); } -export function getApproverStatus(status: string | undefined): ApproverStatusConfig { +export function getApproverStatus( + atrzPrgrsSttsComCd?: string, + atrzSeComCd?: string, +): ApproverStatusConfig { + if (atrzPrgrsSttsComCd === "ATRZ_PRGRS_003" && atrzSeComCd === "ATRZ01") + return { + label: "기술검토 접수", + badgeClass: "bg-green-100 text-green-700", + }; + return ( - APPROVER_STATUS_MAP[status ?? ""] ?? { - label: status || "-", + APPROVER_STATUS_MAP[atrzPrgrsSttsComCd ?? ""] ?? { + label: "-", badgeClass: "bg-gray-100 text-gray-700", } ); diff --git a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx index 77ec13b78..4be4cf0c3 100644 --- a/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-doc-prgrs-status.tsx @@ -4,7 +4,7 @@ import { Fragment } from "react"; import { ChevronRight } from "lucide-react"; -import { useUserStore } from "@/shared/lib"; +import { dayjs, useUserStore } from "@/shared/lib"; import type { ElaprDocPrgrsClientResponseDto } from "@/shared/model"; import { SectionCollapsible } from "@/shared/ui"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip"; @@ -20,7 +20,7 @@ interface ElaprDocPrgrsStatusProps { } function ElaprDocPrgrsStep({ item }: { item: ElaprDocPrgrsClientResponseDto }) { - const status = getApproverStatus(item.atrzPrgrsSttsComCd); + const status = getApproverStatus(item.atrzPrgrsSttsComCd, item.atrzSeComCd); const isRejected = item.atrzPrgrsSttsComCd === REJECT_STATUS_CD; const rjctRsn = item.rjctRsn?.trim(); const metaParts = [item.ognzNm, item.jbpsSeNm].filter(Boolean); @@ -28,7 +28,7 @@ function ElaprDocPrgrsStep({ item }: { item: ElaprDocPrgrsClientResponseDto }) { const statusBadge = ( +
{isRejected && rjctRsn ? ( {statusBadge} @@ -51,11 +51,16 @@ function ElaprDocPrgrsStep({ item }: { item: ElaprDocPrgrsClientResponseDto }) { )}
- [{item.atrzSeComNm || "-"}] - {item.aprvrNm || item.aprvrUuid || "-"} +

+ [{item.atrzSeComNm || "-"}]{" "} + {item.aprvrNm || item.aprvrUuid || "-"} +

{metaParts.length > 0 && ( - {metaParts.join(" · ")} + {metaParts.join(" · ")} + )} + {item.atrzDt && ( + {dayjs(item.atrzDt).format("YYYY-MM-DD")} )}
@@ -84,17 +89,19 @@ function ElaprDocPrgrsStatus({ elaprDocNo }: ElaprDocPrgrsStatusProps) { } if (steps.length === 0) { - return
결재 정보가 없습니다.
; + return null; } return ( -
+
{steps.map((item, index) => ( -
- {index > 0 && } -
+ {index > 0 && ( +
+ {} +
+ )}
))} diff --git a/apps/as/src/pages/instl-sncn/model/columns.tsx b/apps/as/src/pages/instl-sncn/model/columns.tsx index ee9ddb055..cca857fd9 100644 --- a/apps/as/src/pages/instl-sncn/model/columns.tsx +++ b/apps/as/src/pages/instl-sncn/model/columns.tsx @@ -73,7 +73,7 @@ function buildProgressBadges(prgrsSttsComCd: string, techRplyHstryYn: "Y" | "N") ]; } - if (prgrsSttsComCd === "TA") { + if (prgrsSttsComCd === "TA" || prgrsSttsComCd === "TB") { return [ { label: MAIN_STEP_LABELS.apply, state: "completed" }, { label: MAIN_STEP_LABELS.envoReview, state: "completed" }, From 678af2f06e1011c37f0691614150270ee1f7ac46 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 10 Aug 2026 15:48:19 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[st]=20chore:=20=EC=8A=B9=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=9E=85=EB=A0=A5=20=ED=8F=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/verification/ApprovalInputDialog.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/st/src/components/statistics/verification/ApprovalInputDialog.tsx b/apps/st/src/components/statistics/verification/ApprovalInputDialog.tsx index 811d3bb8c..d9bab2b14 100644 --- a/apps/st/src/components/statistics/verification/ApprovalInputDialog.tsx +++ b/apps/st/src/components/statistics/verification/ApprovalInputDialog.tsx @@ -12,11 +12,9 @@ import { DialogTitle, Input, Label, - Textarea, } from "@common/components"; export interface ApprovalFormData { - rjctOpnnCn: string; lastCfmtnPicNm: string; lastCfmtnPicTelNo: string; lastCfmtnPicDeptNm: string; @@ -34,7 +32,6 @@ export function ApprovalInputDialog({ onOpenChange, onConfirm, }: ApprovalInputDialogProps) { - const [rjctOpnnCn, setRjctOpnnCn] = useState(""); const [lastCfmtnPicNm, setLastCfmtnPicNm] = useState(""); const [lastCfmtnPicTelNo, setLastCfmtnPicTelNo] = useState(""); const [lastCfmtnPicDeptNm, setLastCfmtnPicDeptNm] = useState(""); @@ -43,7 +40,6 @@ export function ApprovalInputDialog({ const [isSubmitting, setIsSubmitting] = useState(false); const resetForm = () => { - setRjctOpnnCn(""); setLastCfmtnPicNm(""); setLastCfmtnPicTelNo(""); setLastCfmtnPicDeptNm(""); @@ -65,7 +61,6 @@ export function ApprovalInputDialog({ try { setIsSubmitting(true); const formData: ApprovalFormData = { - rjctOpnnCn: rjctOpnnCn.trim(), lastCfmtnPicNm: lastCfmtnPicNm.trim(), lastCfmtnPicTelNo: lastCfmtnPicTelNo.trim(), lastCfmtnPicDeptNm: lastCfmtnPicDeptNm.trim(), From d79e0e585a3ae0a54a9d6d3cf66c387c797a9a94 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 10 Aug 2026 17:57:39 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[as]=20feat:=20=ED=96=89=EC=A0=95=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=EC=97=85=EB=AC=B4=EB=B3=84=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=82=B4=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=84=B9=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../elapr/ui/elapr-approval-dialog.tsx | 4 +- .../elapr/ui/sortable-prgrs-stts-row.tsx | 4 +- .../model/sewer-bsc-plan-interface.ts | 2 + .../src/pages/design-cnstn/model/columns.tsx | 6 ++ .../design-cnstn/ui/design-form-page.tsx | 9 ++ .../design-cnstn/ui/design-view-page.tsx | 20 +++-- .../src/pages/fnrs-cnsltn/model/columns.tsx | 9 ++ .../pages/fnrs-cnsltn/ui/fnrs-form-page.tsx | 9 ++ .../pages/fnrs-cnsltn/ui/fnrs-view-page.tsx | 29 +++--- .../as/src/pages/instl-sncn/model/columns.tsx | 10 +++ .../pages/instl-sncn/ui/instl-form-page.tsx | 9 ++ .../pages/instl-sncn/ui/instl-view-page.tsx | 9 +- .../pages/sewer-bsc-plan/model/columns.tsx | 8 ++ .../ui/sewer-bsc-plan-form-page.tsx | 9 ++ .../ui/sewer-bsc-plan-view-page.tsx | 33 ++++--- apps/as/src/shared/model/index.ts | 1 + apps/as/src/shared/model/pbadms-sprt.ts | 17 ++++ apps/as/src/widgets/pbadms-sprt/index.ts | 1 + .../pbadms-sprt/lib/progress-status-utils.ts | 59 +++++++++++++ .../ui/progress-status-section.tsx | 88 +++++++++++++++++++ 20 files changed, 304 insertions(+), 32 deletions(-) create mode 100644 apps/as/src/widgets/pbadms-sprt/lib/progress-status-utils.ts create mode 100644 apps/as/src/widgets/pbadms-sprt/ui/progress-status-section.tsx diff --git a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx index c4cb3956d..ad9abfa3e 100644 --- a/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx +++ b/apps/as/src/entities/elapr/ui/elapr-approval-dialog.tsx @@ -314,8 +314,8 @@ function ElaprApprovalDialog({ - - + + diff --git a/apps/as/src/entities/elapr/ui/sortable-prgrs-stts-row.tsx b/apps/as/src/entities/elapr/ui/sortable-prgrs-stts-row.tsx index 601d272f2..3c2bb457a 100644 --- a/apps/as/src/entities/elapr/ui/sortable-prgrs-stts-row.tsx +++ b/apps/as/src/entities/elapr/ui/sortable-prgrs-stts-row.tsx @@ -73,14 +73,14 @@ function SortablePrgrsSttsRow({ const isDcarbable = isPrgrsSttsRowDcarbable(row); const rowDisabled = isLocked && !isEditing; const atrzSeLabel = row.atrzSeComNm || findCodeLabel(atrzSeCodes, row.atrzSeComCd); - const status = getApproverStatus(row.atrzPrgrsSttsComCd); + const status = getApproverStatus(row.atrzPrgrsSttsComCd, row.atrzSeComCd); const isRejectedRow = row.atrzPrgrsSttsComCd === "ATRZ_PRGRS_004"; const rjctRsn = row.rjctRsn?.trim(); const statusBadge = (
+ {initialData?.detDto?.designCnstnDocNo && ( + + )} diff --git a/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx b/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx index c900ab530..cc4315025 100644 --- a/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx +++ b/apps/as/src/pages/design-cnstn/ui/design-view-page.tsx @@ -6,10 +6,12 @@ import { useUserStore } from "@/shared/lib"; import { useDesignCnstnMng } from "@/entities/design-cnstn"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; +import { ProgressStatusSection } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; import { getEditableReviewStep } from "../lib/review-step-utils"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { FloatingActionButtons } from "./components/floating-action-buttons"; import { AttachmentsSection } from "./sections/attachments-section"; @@ -64,11 +66,14 @@ function DesignViewPage({ id }: DesignViewPageProps) { return
상세 정보를 찾을 수 없습니다.
; } - const detDto = data.detDto; - const bizSclList = data.bizSclList ?? []; - const esntlFileList = data.esntlFileList ?? []; - const etcFileList = data.etcFileList ?? []; - const rvwList = data.rvwList ?? []; + const { + detDto, + bizSclList = [], + esntlFileList = [], + etcFileList = [], + rvwList = [], + sttsChgHstryList = [], + } = data; const editableReviewStep = getEditableReviewStep( userInfo?.dtlInstCd, @@ -77,6 +82,11 @@ function DesignViewPage({ id }: DesignViewPageProps) { return (
+ diff --git a/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx b/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx index a29e2096c..fff55d21a 100644 --- a/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/model/columns.tsx @@ -17,6 +17,15 @@ const MAIN_STEP_LABELS = { reply: "검토결과 회신", } as const; +export const PROGRESS_STATUS_COLUMNS = [ + { label: "재원협의 신청 (시군)", sttsComCd: "ST" }, + { label: "재원협의 신청 (시도)", sttsComCd: "LE" }, + { label: "환경청 검토", sttsComCd: "ER" }, + { label: "기술검토", sttsComCd: "TE" }, + { label: "기술검토 회신", sttsComCd: "TP" }, + { label: "환경청 검토 결과 회신", sttsComCd: "EP" }, +]; + type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { 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 5a4222553..e189e28f6 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 @@ -30,6 +30,7 @@ import { isFormChecklistComplete, isFormEsntlAtchComplete, PrivacyConsentSection, + ProgressStatusSection, recalculateFnrsSharTotals, SplmntDmndFormSection, toFnrsSharSaveList, @@ -43,6 +44,7 @@ import { formatTaskSeComCdForApi, } from "../lib/fnrs-cnsltn-regist-schema"; import { getCreateDefaultValues, mapDetailToFormData } from "../lib/form-data-mapper"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { BasicInfoFormSection } from "./sections/basic-info-form-section"; interface FnrsFormPageProps { @@ -270,6 +272,13 @@ function FnrsFormPage({ id, mode, initialData, createDefaults }: FnrsFormPagePro return ( + {initialData?.detDto?.fnrsCnsltnDocNo && ( + + )} diff --git a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-view-page.tsx b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-view-page.tsx index 650383f9f..6fa957d44 100644 --- a/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-view-page.tsx +++ b/apps/as/src/pages/fnrs-cnsltn/ui/fnrs-view-page.tsx @@ -6,12 +6,13 @@ import { useUserStore } from "@/shared/lib"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useFnrsCnsltnMng } from "@/entities/fnrs-cnsltn"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; import { getEditableReviewStep } from "../lib/review-step-utils"; import { EMPTY_CKLST_RESULT_LIST, useChecklistFormState } from "../lib/use-checklist-form-state"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { FloatingActionButtons } from "./components/floating-action-buttons"; import { AttachmentsSection } from "./sections/attachments-section"; @@ -71,15 +72,18 @@ function FnrsViewPage({ id }: FnrsViewPageProps) { return
상세 정보를 찾을 수 없습니다.
; } - const detDto = data.detDto; - const bizSclList = data.bizSclList ?? []; - const bizCstList = data.bizCstList ?? []; - const cklstResultList = data.cklstResultList ?? []; - const esntlFileList = data.esntlFileList ?? []; - const etcFileList = data.etcFileList ?? []; - const rvwList = data.rvwList ?? []; - const splmntDmndList = data.splmntDmndList ?? []; - const fnrsSharList = data.fnrsSharList ?? []; + const { + detDto, + bizSclList = [], + bizCstList = [], + cklstResultList = [], + esntlFileList = [], + etcFileList = [], + rvwList = [], + splmntDmndList = [], + fnrsSharList = [], + sttsChgHstryList = [], + } = data; const editableReviewStep = getEditableReviewStep( userInfo?.dtlInstCd, @@ -88,6 +92,11 @@ function FnrsViewPage({ id }: FnrsViewPageProps) { return (
+ diff --git a/apps/as/src/pages/instl-sncn/model/columns.tsx b/apps/as/src/pages/instl-sncn/model/columns.tsx index cca857fd9..2dcca5c58 100644 --- a/apps/as/src/pages/instl-sncn/model/columns.tsx +++ b/apps/as/src/pages/instl-sncn/model/columns.tsx @@ -17,6 +17,16 @@ const MAIN_STEP_LABELS = { reply: "검토결과 회신", } as const; +export const PROGRESS_STATUS_COLUMNS = [ + { label: "설치인가 신청 (시군)", sttsComCd: "ST" }, + { label: "설치인가 신청 (시도)", sttsComCd: "LE" }, + { label: "환경청 검토", sttsComCd: "ER" }, + { label: "기술검토", sttsComCd: "TE" }, + { label: "기술검토 회신", sttsComCd: "TP" }, + { label: "검토결과 회신", sttsComCd: "EP" }, + { label: "설치인가 승인", sttsComCd: "TA" }, +]; + type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { 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 bc248e3b2..c2bd574b9 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 @@ -25,6 +25,7 @@ import { isFormChecklistComplete, isFormEsntlAtchComplete, PrivacyConsentSection, + ProgressStatusSection, SplmntDmndFormSection, } from "@/widgets/pbadms-sprt"; import { Button } from "@common/components"; @@ -35,6 +36,7 @@ import { type InstlSncnRegistFormData, instlSncnRegistSchema, } from "../lib/instl-sncn-regist-schema"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { BasicInfoFormSection } from "./sections/basic-info-form-section"; interface InstlFormPageProps { @@ -228,6 +230,13 @@ function InstlFormPage({ id, mode, initialData, createDefaults }: InstlFormPageP return ( + {initialData?.detDto.instlSncnDocNo && ( + + )} diff --git a/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx b/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx index 88677bbf3..d8f637f59 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-view-page.tsx @@ -6,12 +6,13 @@ import { useUserStore } from "@/shared/lib"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useInstlSncnDetail } from "@/entities/instl-sncn"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; import { getEditableReviewStep } from "../lib/review-step-utils"; import { EMPTY_CKLST_RESULT_LIST, useChecklistFormState } from "../lib/use-checklist-form-state"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { FloatingActionButtons } from "./components/floating-action-buttons"; import { AttachmentsSection } from "./sections/attachments-section"; @@ -79,6 +80,7 @@ function InstlViewPage({ id }: InstlViewPageProps) { etcFileList, rvwList = [], splmntDmndList = [], + sttsChgHstryList = [], } = data; const editableReviewStep = getEditableReviewStep( @@ -88,6 +90,11 @@ function InstlViewPage({ id }: InstlViewPageProps) { return (
+ 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 9ad252381..4dc1af0c6 100644 --- a/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/model/columns.tsx @@ -17,6 +17,14 @@ const MAIN_STEP_LABELS = { reply: "검토결과 회신", } as const; +export const PROGRESS_STATUS_COLUMNS = [ + { label: "기본계획 신청 (시군)", sttsComCd: "LE" }, + { label: "환경청 검토", sttsComCd: "ER" }, + { label: "기술검토", sttsComCd: "TE" }, + { label: "기술검토 회신", sttsComCd: "TP" }, + { label: "횐경청 검토 결과 회신", sttsComCd: "EP" }, +]; + type BadgeState = "completed" | "current" | "pending"; interface ProgressBadge { diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-form-page.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-form-page.tsx index 03c605ebe..ca8f2c9d0 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-form-page.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-form-page.tsx @@ -25,6 +25,7 @@ import { isFormChecklistComplete, isFormEsntlAtchComplete, PrivacyConsentSection, + ProgressStatusSection, SplmntDmndFormSection, } from "@/widgets/pbadms-sprt"; import { Button } from "@common/components"; @@ -39,6 +40,7 @@ import { type SewerBscPlanRegistFormData, sewerBscPlanRegistSchema, } from "../lib/sewer-bsc-plan-regist-schema"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { BasicInfoFormSection } from "./sections/basic-info-form-section"; import { BizCstMatrixFormSection } from "./sections/biz-cst-matrix-form-section"; @@ -242,6 +244,13 @@ function SewerBscPlanFormPage({ return ( + {initialData?.detDto?.sewerBscPlanDocNo && ( + + )} diff --git a/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-view-page.tsx b/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-view-page.tsx index 9fd161733..e04483925 100644 --- a/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-view-page.tsx +++ b/apps/as/src/pages/sewer-bsc-plan/ui/sewer-bsc-plan-view-page.tsx @@ -6,12 +6,13 @@ import { useUserStore } from "@/shared/lib"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; import { useSewerBscPlanDetails } from "@/entities/sewer-bsc-plan"; -import { SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; import { getEditableReviewStep } from "../lib/review-step-utils"; import { EMPTY_CKLST_RESULT_LIST, useChecklistFormState } from "../lib/use-checklist-form-state"; +import { PROGRESS_STATUS_COLUMNS } from "../model/columns"; import { FloatingActionButtons } from "./components/floating-action-buttons"; import { AttachmentsSection } from "./sections/attachments-section"; @@ -74,17 +75,20 @@ function SewerBscPlanViewPage({ id, propChgCycl }: SewerBscPlanViewPageProps) { return
상세 정보를 찾을 수 없습니다.
; } - const detDto = data.detDto; - const activeDtlDto = data.activeDtlDto; - const otlnList = data.otlnList ?? []; - const bizCstList = data.bizCstList ?? []; - const fnrsCstList = data.fnrsCstList ?? []; - const tmplntList = data.tmplntList ?? []; - const cklstResultRows = data.cklstResultRows ?? []; - const esntlFileList = data.esntlFileList ?? []; - const etcFileList = data.etcFileList ?? []; - const rvwOpnnList = data.rvwOpnnList ?? []; - const splmntDmndList = data.splmntDmndList ?? []; + const { + detDto, + activeDtlDto, + otlnList = [], + bizCstList = [], + fnrsCstList = [], + tmplntList = [], + cklstResultRows = [], + esntlFileList = [], + etcFileList = [], + rvwOpnnList = [], + splmntDmndList = [], + sttsChgHstryList = [], + } = data; const editableReviewStep = getEditableReviewStep( userInfo?.dtlInstCd, @@ -93,6 +97,11 @@ function SewerBscPlanViewPage({ id, propChgCycl }: SewerBscPlanViewPageProps) { return (
+ diff --git a/apps/as/src/shared/model/index.ts b/apps/as/src/shared/model/index.ts index bdf339145..75c66834d 100644 --- a/apps/as/src/shared/model/index.ts +++ b/apps/as/src/shared/model/index.ts @@ -38,6 +38,7 @@ export type { CklstResultSaveBase, CklstRvwRsltTreeDto, DocPrgrsUpdateRequestDto, + DocSttsChgHstryResponseDto, ElaprDocPrgrsClientResponseDto, EsntlAtchFileMngTreeResponseDto, EsntlAtchFileSaveBase, diff --git a/apps/as/src/shared/model/pbadms-sprt.ts b/apps/as/src/shared/model/pbadms-sprt.ts index 67b0b831e..272e6d110 100644 --- a/apps/as/src/shared/model/pbadms-sprt.ts +++ b/apps/as/src/shared/model/pbadms-sprt.ts @@ -155,6 +155,22 @@ export interface ElaprDocPrgrsClientResponseDto { dcarbYn?: string; } +/** 문서 상태 변경 이력 응답 DTO */ +export interface DocSttsChgHstryResponseDto { + chgHstrySn: number; + docSeComCd: string; + docSeComNm: string; + docNo: string; + chgBfrSttsComCd: string; + chgBfrSttsComNm: string; + chgAftrSttsComCd: string; + chgAftrSttsComNm: string; + rmrk: string; + rgtrUuid: string; + rgtrNm: string; + regDt: string; +} + /** 사업규모 저장 공통 필드 */ export interface BzScaleSaveBase { cnstpSeCd?: string; @@ -244,4 +260,5 @@ export interface PbadmsMngDetResponseBase item.chgAftrSttsComCd === sttsComCd) + .map((item) => ({ + rgtrNm: item.rgtrNm, + regDt: item.regDt, + })); +} + +function findTechReviewProgressItem( + elaprDocPrgrsList: ElaprDocPrgrsClientResponseDto[], +): ProgressStatusItem[] { + return elaprDocPrgrsList + .filter( + (item) => + item.atrzSeComCd === TECH_REVIEW_ATRZ_SE_COM_CD && + item.atrzPrgrsSttsComCd === TECH_REVIEW_ATRZ_PRGRS_STTS_COM_CD, + ) + .map((item) => ({ + rgtrNm: item.aprvrNm ?? "", + regDt: item.atrzDt ?? "", + })); +} + +function resolveProgressStatusItem( + sttsComCd: string, + sttsChgHstryList: DocSttsChgHstryResponseDto[], + elaprDocPrgrsList: ElaprDocPrgrsClientResponseDto[], +): ProgressStatusItem[] { + if (sttsComCd === TECH_REVIEW_STTS_COM_CD) { + return findTechReviewProgressItem(elaprDocPrgrsList); + } + + return findProgressStatusItem(sttsChgHstryList, sttsComCd); +} + +export { + findProgressStatusItem, + findTechReviewProgressItem, + resolveProgressStatusItem, + TECH_REVIEW_STTS_COM_CD, +}; diff --git a/apps/as/src/widgets/pbadms-sprt/ui/progress-status-section.tsx b/apps/as/src/widgets/pbadms-sprt/ui/progress-status-section.tsx new file mode 100644 index 000000000..4ce4d8a0a --- /dev/null +++ b/apps/as/src/widgets/pbadms-sprt/ui/progress-status-section.tsx @@ -0,0 +1,88 @@ +"use client"; + +import dayjs from "dayjs"; + +import { DocSttsChgHstryResponseDto } from "@/shared/model/pbadms-sprt"; +import { SectionCollapsible } from "@/shared/ui"; +import { useElaprDocPrgrsByDocNo } from "@/entities/elapr"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; + +import { resolveProgressStatusItem } from "../lib/progress-status-utils"; + +interface ProgressStatusColumn { + label: string; + sttsComCd: string; +} + +interface ProgressStatusItem { + rgtrNm: string; + regDt: string; +} + +interface ProgressStatusSectionProps { + columns: ProgressStatusColumn[]; + sttsChgHstryList: DocSttsChgHstryResponseDto[]; + elaprDocNo: string; +} + +interface ProgressStatusCellProps { + items: ProgressStatusItem[]; +} + +function ProgressStatusCell({ items }: ProgressStatusCellProps) { + if (items.length === 0) return "-"; + + return ( +
+ {items.map((item, index) => ( +
+ {item.rgtrNm || "-"} + {item.regDt && ( +

+ {dayjs(item.regDt).format("YYYY-MM-DD")} +

+ )} +
+ ))} +
+ ); +} + +function ProgressStatusSection({ + columns, + sttsChgHstryList, + elaprDocNo, +}: ProgressStatusSectionProps) { + const { data: elaprDocPrgrsList = [] } = useElaprDocPrgrsByDocNo(elaprDocNo); + + return ( + + + + + {columns.map((column) => ( + {column.label} + ))} + + + + + {columns.map((column) => ( + + + + ))} + + +
+
+ ); +} + +export { ProgressStatusSection }; From 181730bcde8f61f107401207efbd804f61e4c2e1 Mon Sep 17 00:00:00 2001 From: rsone001 Date: Mon, 10 Aug 2026 17:58:05 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[ST]=20-=20fix:=20=EC=9E=85=EB=A0=A5:=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EC=8B=9C=20=EC=97=B4?= =?UTF-8?q?=EB=A0=A4=EC=9E=88=EB=8A=94=20=EB=AA=A8=EB=93=A0=20=ED=85=9D?= =?UTF-8?q?=EC=9D=98=20=EA=B3=B5=ED=86=B5=20api=EA=B0=80=20=EC=8B=A4?= =?UTF-8?q?=ED=96=89=EB=90=98=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=ED=95=98=EC=88=98=EB=8F=84=EB=B3=B4=EA=B8=89?= =?UTF-8?q?=EB=A5=A0=20=EC=A0=80=EC=9E=A5&=EC=A0=9C=EC=B6=9C=20request=20?= =?UTF-8?q?=EC=88=98=EC=A0=95(=EC=99=B8=EA=B5=AD=EC=9D=B8=EC=B4=9D?= =?UTF-8?q?=EC=9D=B8=EA=B5=AC=20=EC=A0=9C=EA=B1=B0,=20=EC=B4=9D=EB=A9=B4?= =?UTF-8?q?=EC=A0=81=20=EC=B6=94=EA=B0=80),=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=ED=88=B4=ED=8C=81=20max-width=20=EC=B6=94=EA=B0=80,=20?= =?UTF-8?q?=EC=A7=80=EB=82=9C=20=EA=B2=80=EC=A6=9D=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A0=9C=EC=B6=9C=20=EA=B2=B0=EA=B3=BC=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=A7=88=EC=9A=B0=EC=8A=A4=20=ED=88=B4?= =?UTF-8?q?=ED=8C=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/st/src/components/css/DataGrid.css | 6 + apps/st/src/components/module/DataGrid.tsx | 250 ++++++-- .../module/VerificationResultDialog.tsx | 2 + .../VerificationResultHistoryDialog.tsx | 2 + .../module/verificationResultCellTooltip.ts | 11 + .../input/ExcretaTreatmentFacilityInp.tsx | 6 +- .../input/PrivateSewageFacilityInp.tsx | 48 +- .../input/PublicSewageFacilityInp.tsx | 41 +- .../statistics/input/PumpingStationInp.tsx | 6 +- .../input/SewageExcretaRelatedInp.tsx | 39 +- .../input/SewageExcretaSlurgeInp.tsx | 39 +- .../statistics/input/SewagePipeInp.tsx | 36 +- .../input/SewagePipeMaintainInp.tsx | 40 +- .../input/SewageReservoirPumpInp.tsx | 44 +- .../statistics/input/SewerageFinanceInp.tsx | 42 +- .../input/SeweragePenetrationRateInp.tsx | 4 +- .../input/SewerageServiceLevelInp.tsx | 49 +- .../statistics/input/WaterReuseInp.tsx | 51 +- .../GeneralVerificationManagement.tsx | 8 +- .../generalVerification/DetailComponent.tsx | 555 +++++++++++++----- .../generalVerification/FormulaComponents.tsx | 72 ++- .../generalVerification/RegistDialog.tsx | 5 +- .../PublicSewageFactColumn.ts | 6 +- .../src/components/util/grid/columnGuides.ts | 28 + .../util/grid/computedFieldCallbacks.ts | 12 +- ...tics-sewage-supply-rate-input-interface.ts | 2 +- 26 files changed, 905 insertions(+), 499 deletions(-) create mode 100644 apps/st/src/components/module/verificationResultCellTooltip.ts create mode 100644 apps/st/src/components/util/grid/columnGuides.ts diff --git a/apps/st/src/components/css/DataGrid.css b/apps/st/src/components/css/DataGrid.css index 2f3519372..f4a2020f0 100644 --- a/apps/st/src/components/css/DataGrid.css +++ b/apps/st/src/components/css/DataGrid.css @@ -1,5 +1,11 @@ @tailwind components; +/* RealGrid 기본은 white-space: nowrap → max-width만 주면 잘림. 너비 초과 시에만 줄바꿈 */ +.rg-tooltip { + max-width: 60rem; + white-space: pre-line; +} + @layer components { .rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell { @apply !bg-white; diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx index aba00d672..896659c00 100644 --- a/apps/st/src/components/module/DataGrid.tsx +++ b/apps/st/src/components/module/DataGrid.tsx @@ -86,6 +86,40 @@ function mergeGridRowsWithSourceMetadata( }); } +/** row.verificationResult 필드별 오류 배열이 하나라도 있으면 true */ +function rowHasVerificationError( + row: Record, +): boolean { + const verification = ( + row as { verificationResult?: Record } + ).verificationResult; + if (!verification || typeof verification !== "object") return false; + for (const value of Object.values(verification)) { + if (Array.isArray(value) && value.length > 0) return true; + } + return false; +} + +function rowsHaveVerificationErrors( + rows: Record[], +): boolean { + return rows.some(rowHasVerificationError); +} + +function sortRowsVerifyErrorsFirst( + rows: Record[], +): Record[] { + return rows + .map((row, index) => ({ row, index })) + .sort((a, b) => { + const aErr = rowHasVerificationError(a.row) ? 0 : 1; + const bErr = rowHasVerificationError(b.row) ? 0 : 1; + if (aErr !== bErr) return aErr - bErr; + return a.index - b.index; + }) + .map(({ row }) => row); +} + function applyCellStyleCallbacksToColumns( gridView: unknown, columns: ColumnConfig[], @@ -804,6 +838,12 @@ const DataGrid = ({ const emptyCellHoverRafRef = useRef(null); const [emptyCellHoverTooltip, setEmptyCellHoverTooltip] = useState(null); + /** 검증 오류 행 우선보기 (오류 있는 데이터일 때만 UI 노출) */ + const [preferVerifyErrors, setPreferVerifyErrors] = useState(false); + const verifyPriorityBaselineRef = useRef< + Record[] | null + >(null); + const skipVerifyPriorityResetRef = useRef(false); columnsRef.current = columns; rowDataRef.current = rowData; cellStyleCallbackRef.current = cellStyleCallback; @@ -814,6 +854,90 @@ const DataGrid = ({ onCellEditedRef.current = onCellEdited; onDataCellClickRef.current = onDataCellClick; + const hasVerificationErrors = rowsHaveVerificationErrors(rowData); + const showVerifyPriorityToggle = hasVerificationErrors; + + // 조회 등으로 rowData가 교체되면 우선보기 해제 (자체 setRowData는 스킵) + useEffect(() => { + if (skipVerifyPriorityResetRef.current) { + skipVerifyPriorityResetRef.current = false; + return; + } + verifyPriorityBaselineRef.current = null; + setPreferVerifyErrors(false); + }, [rowData]); + + // 오류가 없어지면 체크 해제 + 원본 순서 복원 + useEffect(() => { + if (hasVerificationErrors || !preferVerifyErrors) return; + const baseline = verifyPriorityBaselineRef.current; + verifyPriorityBaselineRef.current = null; + setPreferVerifyErrors(false); + if (baseline) { + skipVerifyPriorityResetRef.current = true; + setRowData(baseline); + } + }, [hasVerificationErrors, preferVerifyErrors, setRowData]); + + const collectCurrentRowsWithMetadata = () => { + const fieldNames = columns + .map((c) => c.fieldName) + .filter(Boolean) as string[]; + const gv = gridRef.current?.gridView as + | { + getDataSource?: () => { + getRowCount(): number; + getValue(r: number, f: string): unknown; + }; + commit?: (force?: boolean) => boolean; + } + | undefined; + if (!gv?.getDataSource) { + return rowDataRef.current.map((r) => ({ ...r })); + } + try { + gv.commit?.(); + } catch { + /* ignore */ + } + const ds = gv.getDataSource(); + if (!ds) return rowDataRef.current.map((r) => ({ ...r })); + const cnt = ds.getRowCount(); + const rows: Record[] = []; + for (let i = 0; i < cnt; i++) { + const r: Record = {}; + for (const f of fieldNames) + r[f] = ds.getValue(i, f) as string | number; + rows.push(r); + } + return mergeGridRowsWithSourceMetadata( + rows, + rowDataRef.current, + fieldNames, + ); + }; + + const handlePreferVerifyErrorsChange = (checked: boolean) => { + if (checked) { + const current = collectCurrentRowsWithMetadata(); + if (!rowsHaveVerificationErrors(current)) return; + verifyPriorityBaselineRef.current = current.map((r) => ({ ...r })); + sortDirRef.current = {}; + originalRowsRef.current = {}; + skipVerifyPriorityResetRef.current = true; + setPreferVerifyErrors(true); + setRowData(sortRowsVerifyErrorsFirst(current)); + return; + } + const baseline = verifyPriorityBaselineRef.current; + verifyPriorityBaselineRef.current = null; + skipVerifyPriorityResetRef.current = true; + setPreferVerifyErrors(false); + if (baseline) { + setRowData(baseline); + } + }; + // rowData 변경 시 편집 중인 셀을 commit한 뒤 수동으로 setRows (RealGridReact에 rows prop을 넘기지 않음) const rowDataMountedRef = useRef(false); useEffect(() => { @@ -2086,60 +2210,79 @@ useEffect(() => { onChange={handleFileChange} />
- {statusInfo && ( -
- - 진행상황:{" "} - {statusInfo.laeSbmsnSttsCd === null || - statusInfo.laeSbmsnSttsCd === "0" ? ( - "미작성" - ) : statusInfo.laeSbmsnSttsCd === "1" ? ( - 작성중 - ) : statusInfo.laeSbmsnSttsCd === "2" ? ( - 제출 - ) : statusInfo.laeSbmsnSttsCd === "3" ? ( - 승인 - ) : statusInfo.laeSbmsnSttsCd === "4" ? ( - - 반려 - - - +
+ {statusInfo && ( +
+ + 진행상황:{" "} + {statusInfo.laeSbmsnSttsCd === null || + statusInfo.laeSbmsnSttsCd === "0" ? ( + "미작성" + ) : statusInfo.laeSbmsnSttsCd === "1" ? ( + 작성중 + ) : statusInfo.laeSbmsnSttsCd === "2" ? ( + 제출 + ) : statusInfo.laeSbmsnSttsCd === "3" ? ( + 승인 + ) : statusInfo.laeSbmsnSttsCd === "4" ? ( + + 반려 + + + + - - ) : ( - 검증중 - )} - - - 반려사유:{" "} - {statusInfo.rjctOpnnCn - ? statusInfo.rjctOpnnCn - : "-"} - - - 제출일:{" "} - {statusInfo.laeSbmsnYmd - ? statusInfo.laeSbmsnYmd.length === 8 && - /^\d{8}$/.test(statusInfo.laeSbmsnYmd) - ? `${statusInfo.laeSbmsnYmd.slice(0, 4)}-${statusInfo.laeSbmsnYmd.slice(4, 6)}-${statusInfo.laeSbmsnYmd.slice(6, 8)}` - : statusInfo.laeSbmsnYmd - : "미제출"} - - - 담당자:{" "} - {statusInfo.laePicNm ? statusInfo.laePicNm : "-"} - - - 연락처:{" "} - {statusInfo.laePicTelNo - ? statusInfo.laePicTelNo - : "-"} - -
- )} + ) : ( + 검증중 + )} + + + 반려사유:{" "} + {statusInfo.rjctOpnnCn + ? statusInfo.rjctOpnnCn + : "-"} + + + 제출일:{" "} + {statusInfo.laeSbmsnYmd + ? statusInfo.laeSbmsnYmd.length === 8 && + /^\d{8}$/.test(statusInfo.laeSbmsnYmd) + ? `${statusInfo.laeSbmsnYmd.slice(0, 4)}-${statusInfo.laeSbmsnYmd.slice(4, 6)}-${statusInfo.laeSbmsnYmd.slice(6, 8)}` + : statusInfo.laeSbmsnYmd + : "미제출"} + + + 담당자:{" "} + {statusInfo.laePicNm ? statusInfo.laePicNm : "-"} + + + 연락처:{" "} + {statusInfo.laePicTelNo + ? statusInfo.laePicTelNo + : "-"} + +
+ )} + {showVerifyPriorityToggle && ( + + )} +
{isExcelImport && ( +
+ +
+ 조건연산자는 규칙 전체에서 1개만 선택할 + 수 있습니다. 조건연산자를 기준으로 + 앞쪽은 기준 대상, 뒤쪽은 비교대상으로 + 구분됩니다. + {conditionBoundary === -1 && ( + + {" "} + 현재 조건연산자가 선택되지 않아 + 전체가 비교대상과 동일하게 + 처리됩니다. + + )} +
-
+ +
-
-
-
- : + ) + ) : (
- } + )}
{ setShowFomulaDialog(open); @@ -369,4 +636,4 @@ const DetailComponent = ({ selectedVrfcRow }: DetailComponentProps) => { ); }; -export default DetailComponent; \ No newline at end of file +export default DetailComponent; diff --git a/apps/st/src/components/statistics/verification/generalVerification/FormulaComponents.tsx b/apps/st/src/components/statistics/verification/generalVerification/FormulaComponents.tsx index d1e7f7940..90dcedc06 100644 --- a/apps/st/src/components/statistics/verification/generalVerification/FormulaComponents.tsx +++ b/apps/st/src/components/statistics/verification/generalVerification/FormulaComponents.tsx @@ -1,41 +1,83 @@ -import { Button, Input } from "@common/components";; -import OperatorsComponents from "./OperatorsComponents"; +import { Button, Input } from "@common/components"; + import type { VerifyDetailResponseDto } from "@/entities/statistics/model/general-verification-interface"; +import OperatorsComponents from "./OperatorsComponents"; + +type ClauseZone = "" | "zone-condition" | "zone-result"; + interface FormulaComponentsProps { data?: VerifyDetailResponseDto | null; index?: number; + zone?: ClauseZone; + groupBy?: boolean; + onGroupByChange?: (checked: boolean) => void; onDelete?: () => void; onOperatorChange?: (value: string) => void; - handleFomulaDialog?: (show: boolean, data?: VerifyDetailResponseDto | null, index?: number) => void; + handleFomulaDialog?: ( + show: boolean, + data?: VerifyDetailResponseDto | null, + index?: number, + ) => void; } -const FormulaComponents = ({ data, index, onDelete, onOperatorChange, handleFomulaDialog }: FormulaComponentsProps) => { +const zoneClassName: Record = { + "": "bg-[#f9fafb]", + "zone-condition": "bg-[#eef4ff]", + "zone-result": "bg-[#fff4ea]", +}; + +const FormulaComponents = ({ + data, + index, + zone = "", + groupBy = false, + onGroupByChange, + onDelete, + onOperatorChange, + handleFomulaDialog, +}: FormulaComponentsProps) => { const cmprOpert = data?.cmprOpert; return ( -
+
handleFomulaDialog?.(true, data, index)} readOnly /> - +
+ +
+
); }; -export default FormulaComponents; \ No newline at end of file +export default FormulaComponents; diff --git a/apps/st/src/components/statistics/verification/generalVerification/RegistDialog.tsx b/apps/st/src/components/statistics/verification/generalVerification/RegistDialog.tsx index 84c24aec4..9775a7b73 100644 --- a/apps/st/src/components/statistics/verification/generalVerification/RegistDialog.tsx +++ b/apps/st/src/components/statistics/verification/generalVerification/RegistDialog.tsx @@ -384,9 +384,8 @@ const RegistDialog = memo( - 공통 검증 - 도단위 검증 - 시단위 검증 + 총계 검증 + 행 검증 diff --git a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts index 7dbda56e5..9d2067de0 100644 --- a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts +++ b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn/PublicSewageFactColumn.ts @@ -50,9 +50,9 @@ export const PublicSewageFactColumn = { { name: 'prcsQnt', fieldName: 'prcsQnt', header: { text: '방류량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' }, // 방류량 - (m³/일) - { name: 'prcsPhysCpct', fieldName: 'prcsPhysCpct', header: { text: '물리적' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' }, - { name: 'prcsBoilCpct', fieldName: 'prcsBoilCpct', header: { text: '생물학적' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' }, - { name: 'prcsAltdCpct', fieldName: 'prcsAltdCpct', header: { text: '고도' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' }, + { name: 'prcsPhysCpct', fieldName: 'prcsPhysCpct', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'prcsBoilCpct', fieldName: 'prcsBoilCpct', header: { text: '생물학적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'prcsAltdCpct', fieldName: 'prcsAltdCpct', header: { text: '고도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // 수질 { name: 'inflwBodVl', fieldName: 'inflwBodVl', header: { text: '유입BOD' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' }, diff --git a/apps/st/src/components/util/grid/columnGuides.ts b/apps/st/src/components/util/grid/columnGuides.ts new file mode 100644 index 000000000..d230bb948 --- /dev/null +++ b/apps/st/src/components/util/grid/columnGuides.ts @@ -0,0 +1,28 @@ +import { statisticsApi } from "@/entities/statistics/api/statistics-service"; + +/** column-guides API 응답 → fieldName(engColNm) → guide 텍스트 */ +export function mapColumnGuideListToRecord( + data: { engColNm?: string; columnGuide?: string }[], +): Record { + const next: Record = {}; + for (const item of data) { + const key = item.engColNm?.trim(); + const guide = item.columnGuide?.trim(); + if (key && guide) next[key] = guide; + } + return next; +} + +export async function fetchColumnGuidesRecord( + statsCd: string, +): Promise> { + try { + const res = await statisticsApi.getColumnGuides(statsCd); + if (res.status !== "success" || !Array.isArray(res.data)) { + return {}; + } + return mapColumnGuideListToRecord(res.data); + } catch { + return {}; + } +} diff --git a/apps/st/src/components/util/grid/computedFieldCallbacks.ts b/apps/st/src/components/util/grid/computedFieldCallbacks.ts index 0506bee8a..231234e86 100644 --- a/apps/st/src/components/util/grid/computedFieldCallbacks.ts +++ b/apps/st/src/components/util/grid/computedFieldCallbacks.ts @@ -14,6 +14,16 @@ function truncateToFirstDecimal(value: number): number { return Math.floor(value * 10) / 10; } +/** + * IEEE 754 가산 잡음 제거. + * 통계 표시 자릿수(대개 0~3)보다 충분히 큰 자리로 반올림해 + * 112.229999… → 112.23 처럼 만들어 RealGrid `#,##0.000` 표시 오차를 막는다. + */ +function sanitizeFloatNoise(value: number): number { + if (!Number.isFinite(value)) return 0; + return Math.round(value * 1e6) / 1e6; +} + /** 합계용 valueCallback 생성 */ export function createSumValueCallback(fieldNames: string[]): DataFieldValueCallback { return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { @@ -22,7 +32,7 @@ export function createSumValueCallback(fieldNames: string[]): DataFieldValueCall const idx = fieldNamesArr.indexOf(f); if (idx >= 0) sum += toNum(values[idx]); } - return sum; + return sanitizeFloatNoise(sum); }; } diff --git a/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts index d58f3c41e..c13243912 100644 --- a/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts +++ b/apps/st/src/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface.ts @@ -121,7 +121,7 @@ export interface SewerageSupplyRateSaveRequest { sggNm: string; sewerBzmnCd: string; userId: string; - sewerBzmnFrgnrWholPpltnCnt?: number; + sewerBzmnArea?: number; saveList?: SewerageSupplyRateSaveItem[]; submitList?: SewerageSupplyRateSaveItem[]; laePicNm?: string;