From f6bcf8ebfe3ede8e9db11ea62f4939e9f36c7001 Mon Sep 17 00:00:00 2001 From: bicochan Date: Thu, 24 Apr 2025 11:10:17 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A1=B0=EC=A7=81=EB=8F=84=20=EC=86=8C?= =?UTF-8?q?=EC=86=8D=EC=9B=90=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EC=82=AD=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user-organization-service.ts | 12 ++ .../model/user-organization-interface.ts | 1 + .../ui/additional-user-dialog.tsx | 2 + .../ui/user-organization-section.tsx | 118 +++++++++++------- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/apps/cs/src/entities/user-organization/api/user-organization-service.ts b/apps/cs/src/entities/user-organization/api/user-organization-service.ts index cbd17363f..bbb7abeef 100644 --- a/apps/cs/src/entities/user-organization/api/user-organization-service.ts +++ b/apps/cs/src/entities/user-organization/api/user-organization-service.ts @@ -5,8 +5,10 @@ import type { UserOrganizationParams, UserOrganizationList, UserOrganizationService, + UserOrganizationPayloadData, SearchUserParams, SearchUserList, + User, } from "../model/user-organization-interface"; export class UserOrganizationApiService implements UserOrganizationService { @@ -34,6 +36,16 @@ export class UserOrganizationApiService implements UserOrganizationService { ); return response.data; } + + public async saveUserOrganization( + data: UserOrganizationPayloadData[], + ): Promise { + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}`, + data, + ); + return response.data; + } } export const UserOrganizationApi = new UserOrganizationApiService(); diff --git a/apps/cs/src/entities/user-organization/model/user-organization-interface.ts b/apps/cs/src/entities/user-organization/model/user-organization-interface.ts index d194f7a68..d7901c86b 100644 --- a/apps/cs/src/entities/user-organization/model/user-organization-interface.ts +++ b/apps/cs/src/entities/user-organization/model/user-organization-interface.ts @@ -81,4 +81,5 @@ export interface UserOrganizationService { ): Promise; getUserOrganizationTreeList(): Promise; searchUser(params: SearchUserParams): Promise; + saveUserOrganization(data: UserOrganizationPayloadData[]): Promise; } diff --git a/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx b/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx index 48e6dc219..0dc63dcb0 100644 --- a/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx +++ b/apps/cs/src/pages/organization/ui/additional-user-dialog.tsx @@ -54,7 +54,9 @@ function AdditionalUserDialog({ const selectedRows = table.getSelectedRowModel().rows; const userData = selectedRows.map((row) => row.original); if (!userData) return; + onAddUsers(userData); + table.setRowSelection({}); }; const handleCancel = () => { diff --git a/apps/cs/src/pages/organization/ui/user-organization-section.tsx b/apps/cs/src/pages/organization/ui/user-organization-section.tsx index c53afe04b..d21d57be2 100644 --- a/apps/cs/src/pages/organization/ui/user-organization-section.tsx +++ b/apps/cs/src/pages/organization/ui/user-organization-section.tsx @@ -1,8 +1,14 @@ import dayjs from "dayjs"; import React, { useEffect, useState, useCallback, memo } from "react"; import { useFieldArray, useForm, type UseFormReturn } from "react-hook-form"; +import { useConfirm } from "@frontend-opensource/use-react-hooks"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useQuery, keepPreviousData } from "@tanstack/react-query"; +import { + useQuery, + useMutation, + useQueryClient, + keepPreviousData, +} from "@tanstack/react-query"; import { Button, Checkbox, @@ -24,11 +30,13 @@ import { UserOrganizationApi, userOrganizationFormSchema, type UserOrganizationFormData, + type UserOrganizationPayloadData, type User, type SearchUser, } from "@/entities/user-organization"; import { CodeApi, type ICode } from "@/entities/code"; import { ENDPOINT } from "@/shared/config"; +import { useAlertStore } from "@/shared/lib/use-alert-store"; import { AdditionalUserDialog } from "./additional-user-dialog"; type UserOrganizationSectionProps = { @@ -36,6 +44,10 @@ type UserOrganizationSectionProps = { }; function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { + const queryClient = useQueryClient(); + const { confirm } = useConfirm(); + const { setMessage: alert } = useAlertStore((state) => state); + const [userList, setUserList] = useState([]); const [selectedUsers, setSelectedUsers] = useState([]); @@ -59,17 +71,29 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { queryFn: () => CodeApi.getCode("JDGDCLCD"), }); + const { mutate: saveUserOrganization } = useMutation({ + mutationFn: (data: UserOrganizationPayloadData[]) => + UserOrganizationApi.saveUserOrganization(data), + onSuccess: () => { + alert("저장되었습니다."); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION, ognzSn], + }); + }, + }); + const form = useForm({ resolver: zodResolver(userOrganizationFormSchema), values: { - userOrganization: userList.map((user) => ({ - mode: user.mode, - ognzSn: user.ognzSn, - userUnqId: user.userUnqId, - jbpsSeCd: user.jbpsSeCd, - jbgdSeCd: user.jbgdSeCd, - sortSeq: user.sortSeq, - })), + userOrganization: + userOrganizationList?.content.map((user) => ({ + mode: user.mode, + ognzSn: user.ognzSn, + userUnqId: user.userUnqId, + jbpsSeCd: user.jbpsSeCd, + jbgdSeCd: user.jbgdSeCd, + sortSeq: user.sortSeq, + })) ?? [], }, }); @@ -79,30 +103,25 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { }); const handleAddUsers = (users: SearchUser[]) => { - const newForm = users.map((user) => ({ + const generateUserData = (user: SearchUser) => ({ mode: "C", ognzSn, userUnqId: user.userUnqId, jbpsSeCd: "", jbgdSeCd: "", sortSeq: 0, - })); - append(newForm); + }); + + const newFormData = users.map(generateUserData); + append(newFormData); const newUserList = users.map((user) => ({ - mode: "C", - ognzSn, - userUnqId: user.userUnqId, + ...generateUserData(user), userUnqNm: user.userNm, - jbpsSeCd: "", - jbgdSeCd: "", - sortSeq: 0, })); + const existingUserIds = new Set(newUserList.map((user) => user.userUnqId)); const filteredUserList = userList.filter( - (existingUser) => - !newUserList.some( - (newUser) => newUser.userUnqId === existingUser.userUnqId, - ), + (user) => !existingUserIds.has(user.userUnqId), ); setUserList([...newUserList, ...filteredUserList]); }; @@ -130,16 +149,29 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { }; const handleDeleteUsers = () => { - if (selectedUsers.length === 0) return; - const selectedUserIds = selectedUsers.map((user) => user.userUnqId); - + const updatedFormData = form.getValues("userOrganization").map((item) => { + if (selectedUserIds.includes(item.userUnqId)) { + return { ...item, mode: "D" }; + } + return item; + }); + form.setValue("userOrganization", updatedFormData); setUserList((prevUsers) => prevUsers.filter((user) => !selectedUserIds.includes(user.userUnqId)), ); setSelectedUsers([]); }; + const handleSaveUsers = async () => { + if (await confirm("저장하시겠습니까?")) { + const filteredData = form + .watch("userOrganization") + .filter((item) => item.mode !== "R"); + saveUserOrganization(filteredData); + } + }; + const isUserSelected = useCallback( (userUnqId: string) => selectedUsers.some((item) => item.userUnqId === userUnqId), @@ -166,7 +198,9 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { > 선택삭제 - +
@@ -196,12 +230,12 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { onCheckedChange={() => handleToggleSelectUser(user)} /> - ))} @@ -212,28 +246,28 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { ); } -type UserTableRowProps = { +type UserCellsProps = { form: UseFormReturn; user: User; index: number; - jobPositionCodes?: ICode[]; - jobGradeCodes?: ICode[]; + jobPositionCodeData?: ICode[]; + jobGradeCodeData?: ICode[]; }; -const UserTableRow = memo( +const UserCells = memo( ({ form, user, index, - jobPositionCodes, - jobGradeCodes, - }: UserTableRowProps) => ( + jobPositionCodeData, + jobGradeCodeData, + }: UserCellsProps) => ( <> {index + 1} {user.userUnqNm} { const mode = form.watch(`userOrganization.${index}.mode`); @@ -247,7 +281,7 @@ const UserTableRow = memo( { const mode = form.watch(`userOrganization.${index}.mode`); @@ -267,7 +301,7 @@ const UserTableRow = memo( ), ); -UserTableRow.displayName = "UserTableRow"; +UserCells.displayName = "UserCells"; type CodeSelectProps = React.ComponentPropsWithoutRef & { codeData?: ICode[]; @@ -276,14 +310,14 @@ type CodeSelectProps = React.ComponentPropsWithoutRef & { const CodeSelect = memo(({ codeData, ...props }: CodeSelectProps) => { if (!codeData) return null; - const defaultValueText = + const placeholderText = codeData.find((item) => item.lwpoCd === props.defaultValue)?.lwpoCdNm ?? "선택"; return (