From 6e2a957e99054f8efe5d91bbd4a8cc691d213e07 Mon Sep 17 00:00:00 2001 From: bicochan Date: Fri, 29 May 2026 14:40:41 +0900 Subject: [PATCH] =?UTF-8?q?[as]=20feat:=20=ED=95=84=EC=88=98=EC=B2=A8?= =?UTF-8?q?=EB=B6=80=ED=8C=8C=EC=9D=BC=EA=B4=80=EB=A6=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entities/esntl/model/esntl-interface.ts | 4 +- .../model/esntl-atch-file-search-params.ts | 30 ---- .../model/generate-esntl-atch-file-id.ts | 12 -- .../ui/esntl-atch-file-form-dialog.tsx | 20 +-- .../ui/esntl-atch-file-list.tsx | 108 ++++++++++--- .../ui/esntl-atch-file-mng-page.tsx | 143 +++--------------- 6 files changed, 120 insertions(+), 197 deletions(-) delete mode 100644 apps/as/src/pages/esntl-atch-file-mng/model/esntl-atch-file-search-params.ts delete mode 100644 apps/as/src/pages/esntl-atch-file-mng/model/generate-esntl-atch-file-id.ts diff --git a/apps/as/src/entities/esntl/model/esntl-interface.ts b/apps/as/src/entities/esntl/model/esntl-interface.ts index a36d1244a..56fd8b1dd 100644 --- a/apps/as/src/entities/esntl/model/esntl-interface.ts +++ b/apps/as/src/entities/esntl/model/esntl-interface.ts @@ -72,10 +72,8 @@ export interface EsntlAtchFileMngService { /** 필수첨부파일관리 저장 요청 DTO */ export interface EsntlAtchFileMngSaveRequest { - /** 필수첨부파일아이디 */ - esntlAtchFileId: string; /** 문서구분공통코드 */ - docSeComCd?: string; + docSeComCd: string; /** 필수첨부파일명 */ esntlAtchFileNm?: string; /** 필수첨부여부 ("Y" | "N") */ diff --git a/apps/as/src/pages/esntl-atch-file-mng/model/esntl-atch-file-search-params.ts b/apps/as/src/pages/esntl-atch-file-mng/model/esntl-atch-file-search-params.ts deleted file mode 100644 index 817ce97b4..000000000 --- a/apps/as/src/pages/esntl-atch-file-mng/model/esntl-atch-file-search-params.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { EsntlAtchFileMngSearchParams } from "@/entities/esntl"; - -export const ESNTL_ATCH_FILE_ALL_DOC_SE = ""; - -export const DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE = 100; - -export const DEFAULT_ESNTL_ATCH_FILE_SORT = "regDt,desc"; - -export function createDefaultEsntlAtchFileSearchParams(): EsntlAtchFileMngSearchParams { - return { - page: 0, - size: DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, - sort: DEFAULT_ESNTL_ATCH_FILE_SORT, - }; -} - -export function toEsntlAtchFileQueryParams( - params: EsntlAtchFileMngSearchParams, -): EsntlAtchFileMngSearchParams { - const { docSeComCd, esntlAtchFileNm, esntlAtchYn, page, size, sort } = params; - - return { - ...(docSeComCd ? { docSeComCd } : {}), - ...(esntlAtchFileNm ? { esntlAtchFileNm } : {}), - ...(esntlAtchYn ? { esntlAtchYn } : {}), - page: page ?? 0, - size: size ?? DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, - sort: sort ?? DEFAULT_ESNTL_ATCH_FILE_SORT, - }; -} diff --git a/apps/as/src/pages/esntl-atch-file-mng/model/generate-esntl-atch-file-id.ts b/apps/as/src/pages/esntl-atch-file-mng/model/generate-esntl-atch-file-id.ts deleted file mode 100644 index b5240a67c..000000000 --- a/apps/as/src/pages/esntl-atch-file-mng/model/generate-esntl-atch-file-id.ts +++ /dev/null @@ -1,12 +0,0 @@ -const EAF_ID_PREFIX = "EAF-"; -const EAF_ID_PATTERN = /^EAF-(\d+)$/; - -export function generateEsntlAtchFileId(existingIds: string[]): string { - const maxSeq = existingIds.reduce((max, id) => { - const match = id.match(EAF_ID_PATTERN); - if (!match) return max; - return Math.max(max, Number(match[1])); - }, 0); - - return `${EAF_ID_PREFIX}${String(maxSeq + 1).padStart(4, "0")}`; -} diff --git a/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-form-dialog.tsx b/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-form-dialog.tsx index 0ac2f8c4b..0f1faf8a1 100644 --- a/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-form-dialog.tsx +++ b/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-form-dialog.tsx @@ -20,7 +20,6 @@ import { } from "@common/components"; export interface EsntlAtchFileFormValues { - esntlAtchFileId: string; docSeComCd: string; esntlAtchFileNm: string; esntlAtchYn: "Y" | "N"; @@ -32,7 +31,6 @@ interface EsntlAtchFileFormDialogProps { open: boolean; onOpenChange: (open: boolean) => void; initial?: EsntlAtchFileMng | null; - generatedId?: string; isSubmitting?: boolean; onSubmit: (values: EsntlAtchFileFormValues) => void; } @@ -42,13 +40,11 @@ function EsntlAtchFileFormDialog({ open, onOpenChange, initial, - generatedId, isSubmitting, onSubmit, }: EsntlAtchFileFormDialogProps) { const { register, handleSubmit, reset, setValue, watch } = useForm({ defaultValues: { - esntlAtchFileId: "", docSeComCd: "", esntlAtchFileNm: "", esntlAtchYn: "Y", @@ -64,7 +60,6 @@ function EsntlAtchFileFormDialog({ if (mode === "edit" && initial) { reset({ - esntlAtchFileId: initial.esntlAtchFileId, docSeComCd: initial.docSeComCd ?? "", esntlAtchFileNm: initial.esntlAtchFileNm ?? "", esntlAtchYn: initial.esntlAtchYn ?? "Y", @@ -74,13 +69,12 @@ function EsntlAtchFileFormDialog({ } reset({ - esntlAtchFileId: generatedId ?? "", docSeComCd: "", esntlAtchFileNm: "", esntlAtchYn: "Y", esntlAtchFileRmrk: "", }); - }, [open, mode, initial, generatedId, reset]); + }, [open, mode, initial, reset]); return ( @@ -88,15 +82,15 @@ function EsntlAtchFileFormDialog({ {mode === "create" ? "필수첨부파일 등록" : "필수첨부파일 수정"} -
- {mode === "edit" ? ( + + {mode === "edit" && initial ? (
-

{watch("esntlAtchFileId")}

+

+ {initial.esntlAtchFileId} +

- ) : ( - - )} + ) : null}
void; - onEdit: (row: EsntlAtchFileMng) => void; - onDelete: (row: EsntlAtchFileMng) => void; } function EsntlAtchFileList({ searchParams, pagination, onPaginationChange, - onEdit, - onDelete, }: EsntlAtchFileListProps) { + const { confirm } = useConfirm(); const { data: docSeCodes } = useCodeList("ASDOCSECD"); - const queryParams = useMemo( - () => - toEsntlAtchFileQueryParams({ - ...searchParams, - page: pagination.pageIndex, - size: pagination.pageSize, - }), - [pagination.pageIndex, pagination.pageSize, searchParams], - ); + const [dialogMode, setDialogMode] = useState<"create" | "edit" | null>(null); + const [editingRow, setEditingRow] = useState(null); + + const createMutation = useCreateEsntlAtchFileMng(); + const updateMutation = useUpdateEsntlAtchFileMng(); + const deleteMutation = useDeleteEsntlAtchFileMng(); + + const queryParams = useMemo(() => { + return { + docSeComCd: searchParams.docSeComCd, + page: pagination.pageIndex, + size: pagination.pageSize, + sort: searchParams.sort, + }; + }, [searchParams, pagination]); const pageQuery = useEsntlAtchFileMngPage(queryParams); + const handleDelete = useCallback( + async (row: EsntlAtchFileMng) => { + if (!(await confirm("삭제하시겠습니까?"))) return; + await deleteMutation.mutateAsync(row.esntlAtchFileId); + }, + [confirm, deleteMutation], + ); + const columns = useMemo( () => createEsntlAtchFileColumns({ docSeCodes, - onEdit, - onDelete, + onEdit: (row) => { + setEditingRow(row); + setDialogMode("edit"); + }, + onDelete: handleDelete, }), - [docSeCodes, onDelete, onEdit], + [docSeCodes, handleDelete], ); const { tableComponent } = useDataTable({ @@ -71,12 +94,59 @@ function EsntlAtchFileList({ }, }); + const handleFormSubmit = async (values: EsntlAtchFileFormValues) => { + if (!values.docSeComCd || !values.esntlAtchFileNm || !values.esntlAtchYn) return; + + const body = { + docSeComCd: values.docSeComCd, + esntlAtchFileNm: values.esntlAtchFileNm, + esntlAtchYn: values.esntlAtchYn, + esntlAtchFileRmrk: values.esntlAtchFileRmrk || undefined, + }; + + if (dialogMode === "create") { + if (!(await confirm("등록하시겠습니까?"))) return; + await createMutation.mutateAsync(body); + } else if (dialogMode === "edit" && editingRow) { + await updateMutation.mutateAsync({ + esntlAtchFileId: editingRow.esntlAtchFileId, + body, + }); + } + + setDialogMode(null); + setEditingRow(null); + }; + + const handleOpenCreate = () => { + setEditingRow(null); + setDialogMode("create"); + }; + return (
-
+

필수첨부파일 목록

+
{tableComponent} + {dialogMode ? ( + { + if (!open) { + setDialogMode(null); + setEditingRow(null); + } + }} + initial={dialogMode === "edit" ? editingRow : null} + isSubmitting={createMutation.isPending || updateMutation.isPending} + onSubmit={handleFormSubmit} + /> + ) : null}
); } diff --git a/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-mng-page.tsx b/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-mng-page.tsx index b19660d9c..425169983 100644 --- a/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-mng-page.tsx +++ b/apps/as/src/pages/esntl-atch-file-mng/ui/esntl-atch-file-mng-page.tsx @@ -1,19 +1,11 @@ "use client"; -import { useCallback, useState } from "react"; +import { useState } from "react"; -import { useConfirm } from "@frontend-opensource/use-react-hooks"; import type { PaginationState } from "@tanstack/react-table"; import { useCodeList } from "@/entities/code"; -import { - type EsntlAtchFileMng, - type EsntlAtchFileMngSearchParams, - esntlAtchFileMngService, - useCreateEsntlAtchFileMng, - useDeleteEsntlAtchFileMng, - useUpdateEsntlAtchFileMng, -} from "@/entities/esntl"; +import type { EsntlAtchFileMngSearchParams } from "@/entities/esntl"; import { Button, Label, @@ -24,121 +16,51 @@ import { SelectValue, } from "@common/components"; -import { - createDefaultEsntlAtchFileSearchParams, - DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, - ESNTL_ATCH_FILE_ALL_DOC_SE, -} from "../model/esntl-atch-file-search-params"; -import { generateEsntlAtchFileId } from "../model/generate-esntl-atch-file-id"; - -import { - EsntlAtchFileFormDialog, - type EsntlAtchFileFormValues, -} from "./esntl-atch-file-form-dialog"; import { EsntlAtchFileList } from "./esntl-atch-file-list"; const ALL_DOC_SE_VALUE = "__ALL__"; +const ESNTL_ATCH_FILE_ALL_DOC_SE = ""; + +const DEFAULT_ESNTL_ATCH_FILE_PAGE_INDEX = 0; +const DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE = 10; +const DEFAULT_ESNTL_ATCH_FILE_SORT = "regDt,desc"; + +const DEFAULT_SEARCH_PARAMS = { + page: DEFAULT_ESNTL_ATCH_FILE_PAGE_INDEX, + size: DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, + sort: DEFAULT_ESNTL_ATCH_FILE_SORT, +}; function EsntlAtchFileMngPage() { - const { confirm } = useConfirm(); const { data: docSeCodes, isPending: isDocSeLoading } = useCodeList("ASDOCSECD"); - const [draftDocSeComCd, setDraftDocSeComCd] = useState(ESNTL_ATCH_FILE_ALL_DOC_SE); - const [searchParams, setSearchParams] = useState( - createDefaultEsntlAtchFileSearchParams(), - ); + const [docSeComCd, setDocSeComCd] = useState(ESNTL_ATCH_FILE_ALL_DOC_SE); const [pagination, setPagination] = useState({ - pageIndex: 0, + pageIndex: DEFAULT_ESNTL_ATCH_FILE_PAGE_INDEX, pageSize: DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, }); - const [dialogMode, setDialogMode] = useState<"create" | "edit" | null>(null); - const [editingRow, setEditingRow] = useState(null); - const [generatedId, setGeneratedId] = useState(""); - - const createMutation = useCreateEsntlAtchFileMng(); - const updateMutation = useUpdateEsntlAtchFileMng(); - const deleteMutation = useDeleteEsntlAtchFileMng(); + const [searchParams, setSearchParams] = + useState(DEFAULT_SEARCH_PARAMS); const handleSearch = (event: React.FormEvent) => { event.preventDefault(); - setPagination((prev) => ({ ...prev, pageIndex: 0 })); + setPagination((prev) => ({ ...prev, pageIndex: DEFAULT_ESNTL_ATCH_FILE_PAGE_INDEX })); setSearchParams({ - ...createDefaultEsntlAtchFileSearchParams(), - ...(draftDocSeComCd ? { docSeComCd: draftDocSeComCd } : {}), + ...DEFAULT_SEARCH_PARAMS, + docSeComCd, }); }; - const handleOpenCreate = async () => { - const page = await esntlAtchFileMngService.findPage({ - page: 0, - size: DEFAULT_ESNTL_ATCH_FILE_PAGE_SIZE, - sort: "esntlAtchFileId,desc", - }); - const existingIds = page.content.map((row) => row.esntlAtchFileId); - setGeneratedId(generateEsntlAtchFileId(existingIds)); - setEditingRow(null); - setDialogMode("create"); - }; - - const handleEdit = useCallback((row: EsntlAtchFileMng) => { - setEditingRow(row); - setDialogMode("edit"); - }, []); - - const handleDelete = useCallback( - async (row: EsntlAtchFileMng) => { - if (!(await confirm("삭제하시겠습니까?"))) return; - await deleteMutation.mutateAsync(row.esntlAtchFileId); - }, - [confirm, deleteMutation], - ); - - const handleFormSubmit = async (values: EsntlAtchFileFormValues) => { - if (!values.docSeComCd || !values.esntlAtchFileNm || !values.esntlAtchYn) return; - - const body = { - esntlAtchFileId: values.esntlAtchFileId, - docSeComCd: values.docSeComCd, - esntlAtchFileNm: values.esntlAtchFileNm, - esntlAtchYn: values.esntlAtchYn, - esntlAtchFileRmrk: values.esntlAtchFileRmrk || undefined, - }; - - if (dialogMode === "create") { - if (!(await confirm("등록하시겠습니까?"))) return; - await createMutation.mutateAsync(body); - alert("저장되었습니다."); - } else if (dialogMode === "edit" && editingRow) { - await updateMutation.mutateAsync({ - esntlAtchFileId: editingRow.esntlAtchFileId, - body, - }); - } - - setDialogMode(null); - setEditingRow(null); - setGeneratedId(""); - }; - - const closeDialog = () => { - setDialogMode(null); - setEditingRow(null); - setGeneratedId(""); - }; - return (
- +
-
@@ -166,23 +85,7 @@ function EsntlAtchFileMngPage() { searchParams={searchParams} pagination={pagination} onPaginationChange={setPagination} - onEdit={handleEdit} - onDelete={handleDelete} /> - - {dialogMode ? ( - { - if (!open) closeDialog(); - }} - initial={dialogMode === "edit" ? editingRow : null} - generatedId={generatedId} - isSubmitting={createMutation.isPending || updateMutation.isPending} - onSubmit={handleFormSubmit} - /> - ) : null}
); }