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 f9198549b..d194f7a68 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 @@ -16,18 +16,18 @@ export interface SearchUserParams extends PageableParams { } export interface User { + mode: string; ognzSn: number; userUnqId: string; - userUnqNm: string; + userUnqNm?: string; jbpsSeCd: string; - jbpsSeNm: string; + jbpsSeNm?: string; jbgdSeCd: string; - jbgdSeNm: string; + jbgdSeNm?: string; sortSeq: number; - mdfcnDt: string; - mdfr: string; - mdfrNm: string; - mode: string; + mdfcnDt?: string; + mdfr?: string; + mdfrNm?: string; } export interface SearchUser { diff --git a/apps/cs/src/pages/organization/model/user-organization-table-columns.tsx b/apps/cs/src/pages/organization/model/user-organization-table-columns.tsx deleted file mode 100644 index 2378c92d2..000000000 --- a/apps/cs/src/pages/organization/model/user-organization-table-columns.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import dayjs from "dayjs"; -import type { UseFormReturn } from "react-hook-form"; -import type { ColumnDef } from "@tanstack/react-table"; -import { Checkbox, Select } from "@common/components/ui"; -import type { - User, - UserOrganizationFormData, -} from "@/entities/user-organization"; - -export const createColumns = ( - form: UseFormReturn, - renderJobPositionSelect: ( - props: React.ComponentPropsWithoutRef, - ) => React.ReactNode, - renderJobGradeSelect: ( - props: React.ComponentPropsWithoutRef, - ) => React.ReactNode, -): ColumnDef[] => { - return [ - { - id: "select", - header: ({ table }) => ( - - table.toggleAllPageRowsSelected(!!checked) - } - className="h-4 w-4 rounded border-gray-300" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!checked)} - className="h-4 w-4 rounded border-gray-300" - /> - ), - }, - { - id: "no", - header: "NO", - cell: ({ row }) => row.index + 1, - }, - { - accessorKey: "userUnqNm", - header: "이름", - }, - { - accessorKey: "jbpsSeCd", - header: "직위", - cell: ({ row }) => - renderJobPositionSelect({ - defaultValue: row.original.jbpsSeCd, - value: form.watch(`userOrganization.${row.index}.jbpsSeCd`), - onValueChange: (value) => { - const mode = form.watch(`userOrganization.${row.index}.mode`); - if (mode === "R") - form.setValue(`userOrganization.${row.index}.mode`, "U"); - form.setValue(`userOrganization.${row.index}.jbpsSeCd`, value); - }, - }), - }, - { - accessorKey: "jbgdSeCd", - header: "직급", - cell: ({ row }) => - renderJobGradeSelect({ - defaultValue: row.original.jbgdSeCd, - value: form.watch(`userOrganization.${row.index}.jbgdSeCd`), - onValueChange: (value) => { - const mode = form.watch(`userOrganization.${row.index}.mode`); - if (mode === "R") - form.setValue(`userOrganization.${row.index}.mode`, "U"); - form.setValue(`userOrganization.${row.index}.jbgdSeCd`, value); - }, - }), - }, - { - accessorKey: "mdfrNm", - header: "최종수정자", - }, - { - accessorKey: "mdfcnDt", - header: "최종수정일", - cell: ({ row }) => { - if (!row.original.mdfcnDt) return ""; - return dayjs(row.original.mdfcnDt).format("YYYY-MM-DD"); - }, - }, - ]; -}; 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 7e7eaa2a0..38be359f0 100644 --- a/apps/cs/src/pages/organization/ui/user-organization-section.tsx +++ b/apps/cs/src/pages/organization/ui/user-organization-section.tsx @@ -1,10 +1,18 @@ -import { useEffect, useState } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; +import dayjs from "dayjs"; +import React, { useEffect, useState, useCallback, memo } from "react"; +import { useFieldArray, useForm, type UseFormReturn } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery, keepPreviousData } from "@tanstack/react-query"; -import { useReactTable, getCoreRowModel } from "@tanstack/react-table"; -import { cn } from "@common/components/lib"; -import { Button, DataTable } from "@common/components/ui"; +import { + Button, + Checkbox, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@common/components/ui"; import { Select, SelectContent, @@ -21,20 +29,22 @@ import { } from "@/entities/user-organization"; import { CodeApi, type ICode } from "@/entities/code"; import { ENDPOINT } from "@/shared/config"; -import { createColumns } from "../model/user-organization-table-columns"; import { AdditionalUserDialog } from "./additional-user-dialog"; type UserOrganizationSectionProps = { - ognzSn?: number | null; + ognzSn: number; }; function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { const [userList, setUserList] = useState([]); + const [selectedUsers, setSelectedUsers] = useState([]); + + const isAllSelected = + userList.length > 0 && selectedUsers.length === userList.length; const { data: userOrganizationList } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.USER_ORGANIZATION, ognzSn], - queryFn: () => - UserOrganizationApi.getUserOrganizationList({ ognzSn: ognzSn! }), + queryFn: () => UserOrganizationApi.getUserOrganizationList({ ognzSn }), enabled: !!ognzSn, placeholderData: keepPreviousData, }); @@ -52,37 +62,26 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { 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: userList.map((user) => ({ + mode: user.mode, + ognzSn: user.ognzSn, + userUnqId: user.userUnqId, + jbpsSeCd: user.jbpsSeCd, + jbgdSeCd: user.jbgdSeCd, + sortSeq: user.sortSeq, + })), }, }); - const { append } = useFieldArray({ - control: form.control, - name: "userOrganization", - }); - const table = useReactTable({ - data: userList, - columns: createColumns( - form, - (props) => , - (props) => , - ), - getCoreRowModel: getCoreRowModel(), + const { append } = useFieldArray({ + name: "userOrganization", + control: form.control, }); const handleAddUsers = (users: SearchUser[]) => { const newForm = users.map((user) => ({ mode: "C", - ognzSn: ognzSn!, + ognzSn, userUnqId: user.userUnqId, jbpsSeCd: "", jbgdSeCd: "", @@ -91,51 +90,184 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) { append(newForm); const newUserList = users.map((user) => ({ - ognzSn: ognzSn!, + mode: "C", + ognzSn, userUnqId: user.userUnqId, userUnqNm: user.userNm, jbpsSeCd: "", - jbpsSeNm: "", jbgdSeCd: "", - jbgdSeNm: "", sortSeq: 0, - mdfcnDt: "", - mdfr: "", - mdfrNm: "", - mode: "C", })); setUserList([...newUserList, ...userList]); }; + const handleToggleSelectUser = useCallback((user: User) => { + setSelectedUsers((prevSelected) => { + const isSelected = prevSelected.some( + (item) => item.userUnqId === user.userUnqId, + ); + + if (isSelected) { + return prevSelected.filter((item) => item.userUnqId !== user.userUnqId); + } else { + return [...prevSelected, user]; + } + }); + }, []); + + const handleToggleSelectAll = () => { + if (isAllSelected) { + setSelectedUsers([]); + } else { + setSelectedUsers([...userList]); + } + }; + + const handleDeleteUsers = () => { + if (selectedUsers.length === 0) return; + + const selectedUserIds = selectedUsers.map((user) => user.userUnqId); + + setUserList((prevUsers) => + prevUsers.filter((user) => !selectedUserIds.includes(user.userUnqId)), + ); + setSelectedUsers([]); + }; + + const isUserSelected = useCallback( + (userUnqId: string) => + selectedUsers.some((item) => item.userUnqId === userUnqId), + [selectedUsers], + ); + useEffect(() => { - if (userOrganizationList?.content) + if (userOrganizationList?.content) { setUserList(userOrganizationList.content); + } }, [userOrganizationList]); return ( -
+

소속원 목록

-
- + + + + + + + NO + 이름 + 직위 + 직급 + 최종수정자 + 최종수정일 + + + + {userList.map((user, index) => ( + + + handleToggleSelectUser(user)} + /> + + + + ))} + +
); } +type UserTableRowProps = { + form: UseFormReturn; + user: User; + index: number; + jobPositionCodes?: ICode[]; + jobGradeCodes?: ICode[]; +}; + +const UserTableRow = memo( + ({ + form, + user, + index, + jobPositionCodes, + jobGradeCodes, + }: UserTableRowProps) => ( + <> + {index + 1} + {user.userUnqNm} + + { + const mode = form.watch(`userOrganization.${index}.mode`); + if (mode === "R") { + form.setValue(`userOrganization.${index}.mode`, "U"); + } + form.setValue(`userOrganization.${index}.jbpsSeCd`, value); + }} + {...form.register(`userOrganization.${index}.jbpsSeCd`)} + /> + + + { + const mode = form.watch(`userOrganization.${index}.mode`); + if (mode === "R") { + form.setValue(`userOrganization.${index}.mode`, "U"); + } + form.setValue(`userOrganization.${index}.jbgdSeCd`, value); + }} + {...form.register(`userOrganization.${index}.jbgdSeCd`)} + /> + + {user.mdfrNm} + + {dayjs(user.mdfcnDt).format("YYYY-MM-DD")} + + + ), +); + +UserTableRow.displayName = "UserTableRow"; + type CodeSelectProps = React.ComponentPropsWithoutRef & { codeData?: ICode[]; }; -const CodeSelect = ({ codeData, ...props }: CodeSelectProps) => { +const CodeSelect = memo(({ codeData, ...props }: CodeSelectProps) => { if (!codeData) return null; const defaultValueText = @@ -156,6 +288,8 @@ const CodeSelect = ({ codeData, ...props }: CodeSelectProps) => { ); -}; +}); + +CodeSelect.displayName = "CodeSelect"; export { UserOrganizationSection };