From c6c5eff11ef2d5bd67e38f75c29423d838dac548 Mon Sep 17 00:00:00 2001 From: bicochan Date: Mon, 21 Apr 2025 19:36:21 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A7=81=EB=8F=84=20=ED=8F=BC?= =?UTF-8?q?=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../organization/api/organization-service.ts | 24 ++- .../model/organization-interface.ts | 4 +- .../organization/ui/organization-form.tsx | 200 ++++++++++++++++++ .../organization/ui/organization-page.tsx | 53 ++++- apps/cs/src/widgets/dnd-tree/ui/dnd-tree.tsx | 16 +- 5 files changed, 284 insertions(+), 13 deletions(-) create mode 100644 apps/cs/src/pages/organization/ui/organization-form.tsx diff --git a/apps/cs/src/entities/organization/api/organization-service.ts b/apps/cs/src/entities/organization/api/organization-service.ts index ba66c5b76..3f8701045 100644 --- a/apps/cs/src/entities/organization/api/organization-service.ts +++ b/apps/cs/src/entities/organization/api/organization-service.ts @@ -15,13 +15,15 @@ export class OrganizationApiService implements OrganizationService { return response.data; } - public async createOrganization( - organization: OrganizationFormData, - ): Promise { - await client.post( - `${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}`, - organization, + public async getOrganizationInfo(ognzSn: number): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}`, ); + return response.data; + } + + public async createOrganization(form: OrganizationFormData): Promise { + await client.post(`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}`, form); } public async deleteOrganization(ognzSn: number): Promise { @@ -39,6 +41,16 @@ export class OrganizationApiService implements OrganizationService { ); return response.data; } + + public async updateOrganization( + ognzSn: number, + form: OrganizationFormData, + ): Promise { + await client.put( + `${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}`, + form, + ); + } } export const OrganizationApi = new OrganizationApiService(); diff --git a/apps/cs/src/entities/organization/model/organization-interface.ts b/apps/cs/src/entities/organization/model/organization-interface.ts index d22ce2dc1..755bcd77f 100644 --- a/apps/cs/src/entities/organization/model/organization-interface.ts +++ b/apps/cs/src/entities/organization/model/organization-interface.ts @@ -21,7 +21,9 @@ export type OrganizationTree = Organization & { export interface OrganizationService { getOrganizationsTreeList(): Promise; - createOrganization(organization: OrganizationFormData): Promise; + getOrganizationInfo(ognzSn: number): Promise; + createOrganization(form: OrganizationFormData): Promise; deleteOrganization(ognzSn: number): Promise; updateOrganizationName(ognzSn: number, name: string): Promise; + updateOrganization(ognzSn: number, form: OrganizationFormData): Promise; } diff --git a/apps/cs/src/pages/organization/ui/organization-form.tsx b/apps/cs/src/pages/organization/ui/organization-form.tsx new file mode 100644 index 000000000..9ff0fac22 --- /dev/null +++ b/apps/cs/src/pages/organization/ui/organization-form.tsx @@ -0,0 +1,200 @@ +"use client"; + +import type { UseFormReturn } from "react-hook-form"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; +import { + Button, + Input, + Label, + RadioGroup, + RadioGroupItem, + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@common/components"; +import type { OrganizationFormData } from "@/entities/organization"; +import { Form, FormControl, FormField, FormItem } from "@/shared/ui/form"; + +type OrganizationFormProps = { + form: UseFormReturn; + ognzSn?: number; + handleCreate: (data: OrganizationFormData) => Promise; + handleUpdate: ({ + ognzSn, + data, + }: { + ognzSn: number; + data: OrganizationFormData; + }) => Promise; + handleDelete?: (ognzSn: number) => Promise; +}; + +function OrganizationForm({ + form, + ognzSn, + handleCreate, + handleUpdate, + handleDelete, +}: OrganizationFormProps) { + const isModifyMode = !!ognzSn; + + const { confirm } = useConfirm(); + + const onSubmit = async (data: OrganizationFormData) => { + if (isModifyMode) { + await onUpdate(data); + } else { + await onCreate(data); + } + }; + + const onCreate = async (data: OrganizationFormData) => { + if (await confirm("저장하시겠습니까?")) { + handleCreate(data); + } + }; + + const onUpdate = async (data: OrganizationFormData) => { + if (await confirm("수정하시겠습니까?")) { + handleUpdate({ ognzSn: ognzSn!, data }); + } + }; + + const onDelete = async () => { + if (await confirm("삭제하시겠습니까?")) { + handleDelete?.(ognzSn!); + } + }; + + return ( +
+ +
+ + + + + + + + 조직명 + + ( + + + + + + )} + /> + + + + 조직설명 + + ( + + + + + + )} + /> + + + + 정렬순서 + + ( + + + + field.onChange(Number(e.target.value)) + } + className="!text-[1.3rem]" + placeholder="정렬순서를 입력해주세요" + /> + + + )} + /> + + + + 사용여부 + + ( + + + +
+ + +
+
+ + +
+
+
+
+ )} + /> +
+
+
+
+
+
+ + +
+
+ + ); +} + +export { OrganizationForm }; diff --git a/apps/cs/src/pages/organization/ui/organization-page.tsx b/apps/cs/src/pages/organization/ui/organization-page.tsx index a7766b756..d2a6d8128 100644 --- a/apps/cs/src/pages/organization/ui/organization-page.tsx +++ b/apps/cs/src/pages/organization/ui/organization-page.tsx @@ -17,6 +17,7 @@ import { } from "@/entities/organization"; import { ENDPOINT } from "@/shared/config"; import { useAlertStore } from "@/shared/lib/use-alert-store"; +import { OrganizationForm } from "./organization-form"; function OrganizationPage() { const queryClient = useQueryClient(); @@ -34,9 +35,19 @@ function OrganizationPage() { select: (data) => generateOrganizationTreeData(data), }); + const { data: organizationInfo } = useQuery({ + queryKey: [ + ENDPOINT.CMS_SERVICE.ORGANIZATIONS, + selectedOrganization?.ognzSn, + ], + queryFn: () => + OrganizationApi.getOrganizationInfo(selectedOrganization!.ognzSn!), + enabled: !!selectedOrganization?.ognzSn, + }); + const { mutateAsync: createOrganization } = useMutation({ - mutationFn: (organization: OrganizationFormData) => - OrganizationApi.createOrganization(organization), + mutationFn: (form: OrganizationFormData) => + OrganizationApi.createOrganization(form), onSuccess: () => { alert("저장되었습니다."); queryClient.invalidateQueries({ @@ -49,6 +60,7 @@ function OrganizationPage() { mutationFn: (ognzSn: number) => OrganizationApi.deleteOrganization(ognzSn), onSuccess: () => { alert("삭제되었습니다."); + setSelectedOrganization(null); queryClient.invalidateQueries({ queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], }); @@ -66,6 +78,32 @@ function OrganizationPage() { }, }); + const { mutateAsync: updateOrganization } = useMutation({ + mutationFn: ({ + ognzSn, + data, + }: { + ognzSn: number; + data: OrganizationFormData; + }) => OrganizationApi.updateOrganization(ognzSn, data), + onSuccess: () => { + alert("수정되었습니다."); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.ORGANIZATIONS], + }); + }, + }); + + const form = useForm({ + resolver: zodResolver(organizationFormSchema), + defaultValues: { + ognzNm: "", + sortSeq: 1, + useYn: "Y", + }, + values: organizationInfo, + }); + const additionalNodeForm = useForm({ resolver: zodResolver(organizationFormSchema), defaultValues: { @@ -159,10 +197,19 @@ function OrganizationPage() { updateNodeName={async (id, name) => { await updateOrganizationName({ ognzSn: id as number, name }); }} + classes={{ wrapper: "max-h-[calc(100vh-29rem)]" }} /> -
+
+ +
); } diff --git a/apps/cs/src/widgets/dnd-tree/ui/dnd-tree.tsx b/apps/cs/src/widgets/dnd-tree/ui/dnd-tree.tsx index 1a0a36816..c2aab22c0 100644 --- a/apps/cs/src/widgets/dnd-tree/ui/dnd-tree.tsx +++ b/apps/cs/src/widgets/dnd-tree/ui/dnd-tree.tsx @@ -11,6 +11,7 @@ import { type DropOptions, } from "@minoru/react-dnd-treeview"; import { Button } from "@common/components/ui"; +import { cn } from "@common/components/lib"; import { CustomNode } from "./custom-node"; interface TreeData extends Record { @@ -19,9 +20,12 @@ interface TreeData extends Record { type EditableNodeProps = { depth: number }; -type TreeProps = { +type DndTreeProps = { tree: NodeModel[]; selectedNodeId: number; + classes?: { + wrapper: string; + }; renderEditableNode: (props: EditableNodeProps) => ReactElement; handleSelectNode: (node: NodeModel) => void; handleDropNode: (newTree: NodeModel[], options: DropOptions) => void; @@ -31,11 +35,12 @@ type TreeProps = { function DndTree({ tree, selectedNodeId, + classes, renderEditableNode, handleSelectNode, handleDropNode, updateNodeName, -}: TreeProps) { +}: DndTreeProps) { const treeMethods = useRef(null); const isAdditionalParent = (node: NodeModel): boolean => { @@ -46,7 +51,12 @@ function DndTree({ return ( <> -
+