From cff7b7da301aa1a42b03e6b5ca4cd8e5558611a8 Mon Sep 17 00:00:00 2001 From: bicochan Date: Wed, 26 Aug 2026 11:04:37 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[as]=20feat:=20=ED=96=89=EC=A0=95=EC=A7=80?= =?UTF-8?q?=EC=9B=90=20=ED=8C=A8=EB=84=90=20=EC=A0=84=EC=B2=B4=EC=A0=91?= =?UTF-8?q?=EA=B8=B0/=ED=8E=BC=EC=B9=A8=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../design-cnstn/ui/design-view-page.tsx | 64 +++++++------ .../pages/fnrs-cnsltn/ui/fnrs-view-page.tsx | 92 ++++++++++-------- .../pages/instl-sncn/ui/instl-view-page.tsx | 96 ++++++++++--------- .../ui/sewer-bsc-plan-view-page.tsx | 96 ++++++++++--------- apps/as/src/shared/ui/index.ts | 4 + .../shared/ui/section-collapsible-group.tsx | 89 +++++++++++++++++ apps/as/src/shared/ui/section-collapsible.tsx | 17 +++- pnpm-lock.yaml | 11 +++ 8 files changed, 311 insertions(+), 158 deletions(-) create mode 100644 apps/as/src/shared/ui/section-collapsible-group.tsx 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 6915214d9..fbb1b0e43 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 @@ -3,6 +3,7 @@ import { useMemo, useRef } from "react"; import { useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { useDesignCnstnMng } from "@/entities/design-cnstn"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; @@ -80,35 +81,40 @@ function DesignViewPage({ id }: DesignViewPageProps) { ); return ( -
- - - - - - - - +
+ + +
+ + + + + + + + +
+
); } 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 96f3124b3..bb7b22549 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 @@ -3,10 +3,15 @@ import { useMemo, useRef } from "react"; import { useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useFnrsCnsltnMng } from "@/entities/fnrs-cnsltn"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { + AttachmentsViewSection, + ProgressStatusSection, + SupplementDemandSection, +} from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; @@ -90,46 +95,51 @@ function FnrsViewPage({ id }: FnrsViewPageProps) { ); return ( -
- - - - - - - - - - - - +
+ + +
+ + + + + + + + + + + + +
+
); } 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 6e5bd3eca..6a4f5ad2b 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 @@ -3,10 +3,15 @@ import { useMemo, useRef } from "react"; import { useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useInstlSncnDetail } from "@/entities/instl-sncn"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; -import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { + AttachmentsViewSection, + ProgressStatusSection, + SupplementDemandSection, +} from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; @@ -88,48 +93,53 @@ function InstlViewPage({ id }: InstlViewPageProps) { ); return ( -
- - - - - - - - - - - +
+ + +
+ + + + + + + + + + + +
+
); } 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 63b61052b..3099c4c37 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 @@ -3,10 +3,15 @@ import { useMemo, useRef } from "react"; import { useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { ElaprDocPrgrsStatus } from "@/entities/elapr"; import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw"; import { useSewerBscPlanDetails } from "@/entities/sewer-bsc-plan"; -import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt"; +import { + AttachmentsViewSection, + ProgressStatusSection, + SupplementDemandSection, +} from "@/widgets/pbadms-sprt"; import { Skeleton } from "@common/components"; import { needsRoleUser01AtrzResolve } from "../lib/button-config"; @@ -95,48 +100,53 @@ function SewerBscPlanViewPage({ id, propChgCycl }: SewerBscPlanViewPageProps) { ); return ( -
- - - - - - - - - - - - - +
+ + +
+ + + + + + + + + + + + + +
+
); } diff --git a/apps/as/src/shared/ui/index.ts b/apps/as/src/shared/ui/index.ts index 577618146..813bcafd5 100644 --- a/apps/as/src/shared/ui/index.ts +++ b/apps/as/src/shared/ui/index.ts @@ -1,5 +1,9 @@ export { NoData } from "./no-data"; export { PageLayout } from "./page-layout"; export { SectionCollapsible } from "./section-collapsible"; +export { + SectionCollapsibleGroup, + SectionCollapsibleToggle, +} from "./section-collapsible-group"; export type { TextShimmerProps } from "./text-shimmer"; export { TEXT_SHIMMER_CLASS, TextShimmer } from "./text-shimmer"; diff --git a/apps/as/src/shared/ui/section-collapsible-group.tsx b/apps/as/src/shared/ui/section-collapsible-group.tsx new file mode 100644 index 000000000..88828e335 --- /dev/null +++ b/apps/as/src/shared/ui/section-collapsible-group.tsx @@ -0,0 +1,89 @@ +"use client"; + +import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react"; + +import { ChevronsDownUp, ChevronsUpDown } from "lucide-react"; + +import { Button } from "@common/components"; + +interface SectionCollapsibleGroupValue { + batchOpen: boolean; + batchVersion: number; + expandAll: () => void; + collapseAll: () => void; +} + +const SectionCollapsibleGroupContext = createContext(null); + +function useSectionCollapsibleGroup() { + return useContext(SectionCollapsibleGroupContext); +} + +interface SectionCollapsibleGroupProps { + children: ReactNode; +} + +function SectionCollapsibleGroup({ children }: SectionCollapsibleGroupProps) { + const [batch, setBatch] = useState({ open: true, version: 0 }); + + const expandAll = useCallback(() => { + setBatch((prev) => ({ open: true, version: prev.version + 1 })); + }, []); + + const collapseAll = useCallback(() => { + setBatch((prev) => ({ open: false, version: prev.version + 1 })); + }, []); + + const value = useMemo( + () => ({ + batchOpen: batch.open, + batchVersion: batch.version, + expandAll, + collapseAll, + }), + [batch.open, batch.version, expandAll, collapseAll], + ); + + return ( + + {children} + + ); +} + +function SectionCollapsibleToggle() { + const group = useSectionCollapsibleGroup(); + + if (!group) return null; + + const { batchOpen, expandAll, collapseAll } = group; + + return ( +
+ + +
+ ); +} + +export { SectionCollapsibleGroup, SectionCollapsibleToggle, useSectionCollapsibleGroup }; diff --git a/apps/as/src/shared/ui/section-collapsible.tsx b/apps/as/src/shared/ui/section-collapsible.tsx index 2d9d6f27e..9b6fa050b 100644 --- a/apps/as/src/shared/ui/section-collapsible.tsx +++ b/apps/as/src/shared/ui/section-collapsible.tsx @@ -1,11 +1,13 @@ "use client"; -import type { ReactNode } from "react"; +import { type ReactNode, useState } from "react"; import { ChevronDown } from "lucide-react"; import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from "@common/components"; +import { useSectionCollapsibleGroup } from "./section-collapsible-group"; + interface SectionCollapsibleProps { title: ReactNode; defaultOpen?: boolean; @@ -13,8 +15,19 @@ interface SectionCollapsibleProps { } function SectionCollapsible({ title, defaultOpen = true, children }: SectionCollapsibleProps) { + const group = useSectionCollapsibleGroup(); + const [open, setOpen] = useState(group?.batchOpen ?? defaultOpen); + const [seenVersion, setSeenVersion] = useState(group?.batchVersion ?? 0); + + if (group && group.batchVersion !== seenVersion) { + setSeenVersion(group.batchVersion); + setOpen(group.batchOpen); + } + + const collapsibleProps = group ? { open, onOpenChange: setOpen } : { defaultOpen }; + return ( - + - - {isEditMode && ( - <> +
+
- - - )} + {isEditMode && ( + <> + + + + )} +
+
-
+ ); 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 69d680d5e..66a6a307c 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 @@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useQueryClient } from "@tanstack/react-query"; import { useAlertStore, useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { useAcntgSeMngTree } from "@/entities/acntg"; import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { @@ -259,79 +260,84 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps) return ( -
- {initialData?.detDto?.fnrsCnsltnDocNo && ( - - )} - - - - - - - - - - -
-
- - {!isSplmntPrgrsStts ? ( - <> - - {isEditMode && ( - <> - - - - )} - - ) : ( - + + + +
+ {initialData?.detDto?.fnrsCnsltnDocNo && ( + )} + + + + + + + + + + +
+
+ + {!isSplmntPrgrsStts ? ( + <> + + {isEditMode && ( + <> + + + + )} + + ) : ( + + )} +
+
-
+ ); 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 dfdda4a8f..adf563c65 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-form-page.tsx @@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useQueryClient } from "@tanstack/react-query"; import { useAlertStore, useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { type InstlSncnApplySaveRequestDto, @@ -222,75 +223,80 @@ function InstlFormPage({ mode, initialData, createDefaults }: InstlFormPageProps return ( -
- {initialData?.detDto.instlSncnDocNo && ( - - )} - - - - - - - - - -
-
- - {!isSplmntPrgrsStts ? ( - <> - - {isEditMode && ( - <> - - - - )} - - ) : ( - + + + +
+ {initialData?.detDto.instlSncnDocNo && ( + )} + + + + + + + + + +
+
+ + {!isSplmntPrgrsStts ? ( + <> + + {isEditMode && ( + <> + + + + )} + + ) : ( + + )} +
+
-
+ ); 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 f0dbca589..fe29ccaa7 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 @@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useQueryClient } from "@tanstack/react-query"; import { useAlertStore, useUserStore } from "@/shared/lib"; +import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui"; import { useAcntgSeMngTree } from "@/entities/acntg"; import { useEsntlAtchFileMngTree } from "@/entities/esntl"; import { @@ -240,76 +241,81 @@ function SewerBscPlanFormPage({ return (
- {initialData?.detDto?.sewerBscPlanDocNo && ( - - )} - - - - - - - - - - {isEditMode && } - -
-
- - {!isSplmntPrgrsStts ? ( - <> - - {isEditMode && ( - <> - - - - )} - - ) : ( - + + +
+ {initialData?.detDto?.sewerBscPlanDocNo && ( + )} + + + + + + + + + + {isEditMode && } + +
+
+ + {!isSplmntPrgrsStts ? ( + <> + + {isEditMode && ( + <> + + + + )} + + ) : ( + + )} +
+
-
+ );