From 20fd291cc3456c5a99a27d4f81ba8540d38b5fa3 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 29 Jun 2026 16:08:21 +0900 Subject: [PATCH] =?UTF-8?q?[as]=20feat:=20=EC=84=A4=EC=B9=98=EC=9D=B8?= =?UTF-8?q?=EA=B0=80=20=EC=8B=A0=EC=B2=AD=20=EC=8B=9C=20=EC=97=85=EB=AC=B4?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=20=EC=84=A0=ED=83=9D=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../instl-sncn/regist/page.tsx | 13 ++- .../pages/instl-sncn/lib/form-data-mapper.ts | 7 +- .../ui/components/task-se-picker-dialog.tsx | 107 ++++++++++++++++++ .../pages/instl-sncn/ui/instl-form-page.tsx | 5 +- .../as/src/pages/instl-sncn/ui/instl-list.tsx | 38 +++++-- .../pages/instl-sncn/ui/instl-regist-page.tsx | 8 +- .../ui/sections/basic-info-form-section.tsx | 41 ++----- 7 files changed, 171 insertions(+), 48 deletions(-) create mode 100644 apps/as/src/pages/instl-sncn/ui/components/task-se-picker-dialog.tsx diff --git a/apps/as/app/(with-base-layout)/instl-sncn/regist/page.tsx b/apps/as/app/(with-base-layout)/instl-sncn/regist/page.tsx index b19f7abb6..476d0b7c2 100644 --- a/apps/as/app/(with-base-layout)/instl-sncn/regist/page.tsx +++ b/apps/as/app/(with-base-layout)/instl-sncn/regist/page.tsx @@ -1,3 +1,14 @@ import { InstlRegistPage } from "@/pages/instl-sncn"; -export default InstlRegistPage; +interface InstlSncnRegistRouteProps { + searchParams: Promise<{ taskSeComCd?: string }>; +} + +export default async function Page({ searchParams }: InstlSncnRegistRouteProps) { + const params = await searchParams; + const taskSeComCd = params.taskSeComCd + ? params.taskSeComCd.split(",").filter(Boolean) + : undefined; + + return ; +} diff --git a/apps/as/src/pages/instl-sncn/lib/form-data-mapper.ts b/apps/as/src/pages/instl-sncn/lib/form-data-mapper.ts index 6110d7be7..777229093 100644 --- a/apps/as/src/pages/instl-sncn/lib/form-data-mapper.ts +++ b/apps/as/src/pages/instl-sncn/lib/form-data-mapper.ts @@ -89,14 +89,17 @@ export function mapDetailToFormData(data: InstlSncnMngDetResponse): CnfmRegistFo }; } -export function getCreateDefaultValues(userInfo: IUserInfo | null): CnfmRegistFormData { +export function getCreateDefaultValues( + userInfo: IUserInfo | null, + taskSeComCd?: string[], +): CnfmRegistFormData { return { instlSncnMng: { bizNm: "", bizBgngYr: "", bizEndYr: "", bizCn: "", - taskSeComCd: [], + taskSeComCd: taskSeComCd ?? [], sncnSeComCd: "", dsgnrNm: "", dsgnrTelno: "", diff --git a/apps/as/src/pages/instl-sncn/ui/components/task-se-picker-dialog.tsx b/apps/as/src/pages/instl-sncn/ui/components/task-se-picker-dialog.tsx new file mode 100644 index 000000000..b1a4fd135 --- /dev/null +++ b/apps/as/src/pages/instl-sncn/ui/components/task-se-picker-dialog.tsx @@ -0,0 +1,107 @@ +"use client"; + +import { useEffect, useState } from "react"; + +import { Field, FieldGroup, FieldLabel, FieldSet } from "@/shared/ui/field"; +import { useCodeList } from "@/entities/code"; +import { + Button, + Checkbox, + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, + Skeleton, + Table, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@common/components"; + +interface TaskSePickerDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onConfirm: (selectedCodes: string[]) => void; +} + +function TaskSePickerDialog({ open, onOpenChange, onConfirm }: TaskSePickerDialogProps) { + const [selectedCodes, setSelectedCodes] = useState([]); + const { data: taskOptions, isPending } = useCodeList("INSTL"); + + useEffect(() => { + if (!open) return; + setSelectedCodes([]); + }, [open]); + + function handleCheckedChange(code: string, checked: boolean) { + setSelectedCodes((prev) => + checked ? [...prev, code] : prev.filter((value) => value !== code), + ); + } + + function handleConfirm() { + onConfirm(selectedCodes); + } + + return ( + + + + 설치인가 신청 + + + {isPending ? ( + + ) : ( + + + + 업무구분 + +
+ + {taskOptions?.map((option) => ( + + + handleCheckedChange(option.lwpoCd, checked === true) + } + /> + + {option.lwpoCdNm} + + + ))} + +
+
+
+
+
+ )} + + + + + +
+
+ ); +} + +export { TaskSePickerDialog }; 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 3e06ca069..9973648fa 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 @@ -28,9 +28,10 @@ import { PrivacyConsentSection } from "./sections/privacy-consent-section"; interface InstlFormPageProps { mode: "create" | "edit"; initialData?: InstlSncnMngDetResponse; + taskSeComCd?: string[]; } -function InstlFormPage({ mode, initialData }: InstlFormPageProps) { +function InstlFormPage({ mode, initialData, taskSeComCd }: InstlFormPageProps) { const router = useRouter(); const userInfo = useUserStore((state) => state.userInfo); const alert = useAlertStore((state) => state.setMessage); @@ -43,7 +44,7 @@ function InstlFormPage({ mode, initialData }: InstlFormPageProps) { defaultValues: isEditMode && initialData ? mapDetailToFormData(initialData) - : getCreateDefaultValues(userInfo), + : getCreateDefaultValues(userInfo, taskSeComCd), }); async function onSave(data: CnfmRegistFormData) { diff --git a/apps/as/src/pages/instl-sncn/ui/instl-list.tsx b/apps/as/src/pages/instl-sncn/ui/instl-list.tsx index 4f3660dcc..fe202f45a 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-list.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-list.tsx @@ -1,6 +1,6 @@ "use client"; -import { useMemo } from "react"; +import { useMemo, useState } from "react"; import { useRouter } from "next/navigation"; import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; @@ -11,6 +11,8 @@ import { Button } from "@common/components"; import { createInstlSncnColumns } from "../model/columns"; +import { TaskSePickerDialog } from "./components/task-se-picker-dialog"; + interface InstlListProps { searchParams: InstlSncnMngSearchParams; pagination: PaginationState; @@ -19,6 +21,7 @@ interface InstlListProps { function InstlList({ searchParams, pagination, onPaginationChange }: InstlListProps) { const router = useRouter(); + const [isTaskSeDialogOpen, setIsTaskSeDialogOpen] = useState(false); const queryParams = useMemo( () => ({ @@ -55,19 +58,32 @@ function InstlList({ searchParams, pagination, onPaginationChange }: InstlListPr }); const handleRegisterClick = () => { - router.push("/instl-sncn/regist"); + setIsTaskSeDialogOpen(true); + }; + + const handleConfirmTaskSe = (selectedCodes: string[]) => { + setIsTaskSeDialogOpen(false); + const query = selectedCodes.join(","); + router.push(`/instl-sncn/regist?taskSeComCd=${query}`); }; return ( -
-
-

설치인가 목록

- -
- {tableComponent} -
+ <> +
+
+

설치인가 목록

+ +
+ {tableComponent} +
+ + ); } diff --git a/apps/as/src/pages/instl-sncn/ui/instl-regist-page.tsx b/apps/as/src/pages/instl-sncn/ui/instl-regist-page.tsx index ba4044e8a..0b9472a15 100644 --- a/apps/as/src/pages/instl-sncn/ui/instl-regist-page.tsx +++ b/apps/as/src/pages/instl-sncn/ui/instl-regist-page.tsx @@ -1,7 +1,11 @@ import { InstlFormPage } from "./instl-form-page"; -function InstlRegistPage() { - return ; +interface InstlRegistPageProps { + taskSeComCd?: string[]; +} + +function InstlRegistPage({ taskSeComCd }: InstlRegistPageProps) { + return ; } export { InstlRegistPage }; diff --git a/apps/as/src/pages/instl-sncn/ui/sections/basic-info-form-section.tsx b/apps/as/src/pages/instl-sncn/ui/sections/basic-info-form-section.tsx index 2a9a7f276..585835571 100644 --- a/apps/as/src/pages/instl-sncn/ui/sections/basic-info-form-section.tsx +++ b/apps/as/src/pages/instl-sncn/ui/sections/basic-info-form-section.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import { Field, FieldError, FieldGroup, FieldLabel, FieldSet } from "@/shared/ui/field"; -import { useCodeList } from "@/entities/code"; +import { findCodeLabel, useCodeList } from "@/entities/code"; import { Button, Checkbox, @@ -41,6 +41,12 @@ function BasicInfoFormSection() { const { data: rtlztnFcltOptions } = useCodeList("RTLZTNFCLTSE"); const { data: rtlztnUsgOptions } = useCodeList("RTLZTNUSGSE"); + const taskSeComCdLabel = + taskSeComCd + .map((code) => findCodeLabel(taskOptions, code)) + .filter(Boolean) + .join(", ") || "-"; + return ( @@ -139,36 +145,11 @@ function BasicInfoFormSection() { ( -
- - {taskOptions?.map((option) => ( - - { - const newValue = checked - ? [...field.value, option.lwpoCd] - : field.value.filter((value) => value !== option.lwpoCd); - field.onChange(newValue); - }} - /> - - {option.lwpoCdNm} - - - ))} - + render={({ fieldState }) => ( + + {taskSeComCdLabel} -
+ )} />