diff --git a/apps/cs/src/entities/user/model/user-interface.ts b/apps/cs/src/entities/user/model/user-interface.ts index abc48f9ae..dd250bd9e 100644 --- a/apps/cs/src/entities/user/model/user-interface.ts +++ b/apps/cs/src/entities/user/model/user-interface.ts @@ -20,6 +20,7 @@ export interface UserSearchParams extends PageableParams { grpId?: string; frDateYmd?: string; toDateYmd?: string; + userName?: string; } // 사용자 목록 응답 인터페이스 diff --git a/apps/cs/src/pages/groups/api/group-users-service.ts b/apps/cs/src/pages/groups/api/group-users-service.ts index 2a11038e6..5af60a9ef 100644 --- a/apps/cs/src/pages/groups/api/group-users-service.ts +++ b/apps/cs/src/pages/groups/api/group-users-service.ts @@ -3,8 +3,9 @@ import { ENDPOINT } from "@/shared/config"; import type { GroupUserService, GroupUserSearchParams, - GroupUserList, + GroupUserListResponseDto, GroupUserDetailResponse, + PageGroupUserList, GroupUserSaveData, GroupUserUpdateData, } from "../model/group-users-interface"; @@ -13,8 +14,8 @@ export class GroupUsersApiService implements GroupUserService { // 그룹 사용자 목록 조회 public async findPageGroupUser( params: GroupUserSearchParams, - ): Promise { - const response = await client.get( + ): Promise { + const response = await client.get( `${ENDPOINT.CMS_SERVICE.GROUP_USERS}`, { params }, ); @@ -34,9 +35,9 @@ export class GroupUsersApiService implements GroupUserService { // 그룹 사용자 저장 public async saveGroupUser( - data: GroupUserSaveData, - ): Promise { - const response = await client.post( + data: GroupUserSaveData[], + ): Promise { + const response = await client.post( `${ENDPOINT.CMS_SERVICE.GROUP_USERS}`, data, ); diff --git a/apps/cs/src/pages/groups/model/group-users-interface.ts b/apps/cs/src/pages/groups/model/group-users-interface.ts index 1587522a8..38daf6fe0 100644 --- a/apps/cs/src/pages/groups/model/group-users-interface.ts +++ b/apps/cs/src/pages/groups/model/group-users-interface.ts @@ -1,4 +1,4 @@ -import type { PageableParams } from "@/shared/model"; +import type { PageableList, PageableParams } from "@/shared/model"; // 그룹 사용자 검색 파라미터 인터페이스 export interface GroupUserSearchParams extends PageableParams { @@ -7,49 +7,17 @@ export interface GroupUserSearchParams extends PageableParams { // 그룹 사용자 목록 응답 인터페이스 export interface GroupUserListResponseDto { - grpSn: number; + mode: string; + groupId: string; userUnqId: string; - userId: string; userName: string; - email: string; + ogdpSeCd: string; + ogdpSeNm: string; mphn: string; - regDt: string; - rgtr: string; - mdfcnDt: string; - mdfr: string; - rgtrNm: string; - mdfrNm: string; } // 그룹 사용자 페이지 응답 인터페이스 -export interface GroupUserList { - content: GroupUserListResponseDto[]; - pageable: { - sort: { - empty: boolean; - sorted: boolean; - unsorted: boolean; - }; - offset: number; - pageSize: number; - pageNumber: number; - paged: boolean; - unpaged: boolean; - }; - last: boolean; - totalPages: number; - totalElements: number; - size: number; - number: number; - sort: { - empty: boolean; - sorted: boolean; - unsorted: boolean; - }; - first: boolean; - numberOfElements: number; - empty: boolean; -} +export type PageGroupUserList = PageableList; // 그룹 사용자 상세 응답 인터페이스 export interface GroupUserDetailResponse { @@ -69,7 +37,8 @@ export interface GroupUserDetailResponse { // 그룹 사용자 저장 데이터 인터페이스 export interface GroupUserSaveData { - grpSn: number; + mode: string; + groupId: string; userUnqId: string; } @@ -81,7 +50,9 @@ export interface GroupUserUpdateData { // 그룹 사용자 서비스 인터페이스 export interface GroupUserService { // 그룹 사용자 목록 조회 - findPageGroupUser: (params: GroupUserSearchParams) => Promise; + findPageGroupUser: ( + params: GroupUserSearchParams, + ) => Promise; // 그룹 사용자 단건 조회 findByGroupAndUserId: ( @@ -90,7 +61,9 @@ export interface GroupUserService { ) => Promise; // 그룹 사용자 저장 - saveGroupUser: (data: GroupUserSaveData) => Promise; + saveGroupUser: ( + data: GroupUserSaveData[], + ) => Promise; // 그룹 사용자 수정 updateGroupUser: ( diff --git a/apps/cs/src/pages/groups/ui/additional-user-dialog.tsx b/apps/cs/src/pages/groups/ui/additional-user-dialog.tsx new file mode 100644 index 000000000..e3a3ee7a1 --- /dev/null +++ b/apps/cs/src/pages/groups/ui/additional-user-dialog.tsx @@ -0,0 +1,192 @@ +"use client"; + +import { useState } from "react"; +import { + keepPreviousData, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; +import { + getCoreRowModel, + useReactTable, + type PaginationState, + type RowSelectionState, + type ColumnDef, +} from "@tanstack/react-table"; +import { + Button, + DataTable, + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogFooter, + Input, + Checkbox, +} from "@common/components"; +import { + UserApi, + type UserListResponse, + type UserSearchParams, +} from "@/entities/user"; +import { ENDPOINT } from "@/shared/config"; +import { useAlertStore } from "@/shared/lib"; +import { GroupUsersApi } from "../api/group-users-service"; + +type AdditionalUserDialogProps = { + groupId?: string; +}; + +function AdditionalUserDialog({ groupId }: AdditionalUserDialogProps) { + const queryClient = useQueryClient(); + const alert = useAlertStore((state) => state.setMessage); + + const [open, setOpen] = useState(false); + const [userSearchQuery, setUserSearchQuery] = useState({ + userName: "", + }); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const [userRowSelection, setUserRowSelection] = useState( + {}, + ); + + const { data: usersList } = useQuery({ + queryKey: [ENDPOINT.USER_SERVICE.USERS, userSearchQuery, pagination], + queryFn: () => + UserApi.findPageUser({ + ...userSearchQuery, + page: pagination.pageIndex, + size: pagination.pageSize, + }), + enabled: open, + placeholderData: keepPreviousData, + }); + + const userColumns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="전체 선택" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="행 선택" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "userId", + header: "사용자ID", + }, + { + accessorKey: "userName", + header: "이름", + }, + { + accessorKey: "ogdpSeNm", + header: "소속", + }, + { + accessorKey: "mphn", + header: "연락처", + }, + ]; + + const userTable = useReactTable({ + data: usersList?.content ?? [], + columns: userColumns, + rowCount: usersList?.totalElements ?? 0, + state: { pagination, rowSelection: userRowSelection }, + manualPagination: true, + enableRowSelection: true, + enableMultiRowSelection: true, + getCoreRowModel: getCoreRowModel(), + onPaginationChange: setPagination, + onRowSelectionChange: setUserRowSelection, + getRowId: (row) => row.userUnqId, + }); + + const handleAddUsers = async () => { + const selectedRows = Object.keys(userRowSelection); + if (selectedRows.length === 0 || !groupId) return; + + try { + const usersToAdd = selectedRows.map((userUnqId) => ({ + mode: "C", + groupId, + userUnqId, + })); + + await GroupUsersApi.saveGroupUser(usersToAdd); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.GROUP_USERS, groupId], + }); + alert("사용자 추가를 완료했습니다.", () => { + setOpen(false); + setUserRowSelection({}); + }); + } catch (error) { + console.error(error); + } + }; + + const handleSearch = () => { + setPagination({ pageIndex: 0, pageSize: 10 }); + }; + + return ( + + + + + + + 사용자 추가 + +
+ + setUserSearchQuery({ + ...userSearchQuery, + userName: e.target.value, + }) + } + className="!text-[1.3rem]" + placeholder="사용자 이름으로 검색" + /> + +
+ + + + + +
+
+ ); +} + +export { AdditionalUserDialog }; diff --git a/apps/cs/src/pages/groups/ui/group-users-section.tsx b/apps/cs/src/pages/groups/ui/group-users-section.tsx index 87b5ae7af..b736e3d13 100644 --- a/apps/cs/src/pages/groups/ui/group-users-section.tsx +++ b/apps/cs/src/pages/groups/ui/group-users-section.tsx @@ -1,7 +1,11 @@ "use client"; import { useState } from "react"; -import { keepPreviousData, useQuery } from "@tanstack/react-query"; +import { + keepPreviousData, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import { getCoreRowModel, useReactTable, @@ -10,21 +14,26 @@ import { } from "@tanstack/react-table"; import { Button, DataTable } from "@common/components"; import { ENDPOINT } from "@/shared/config"; +import { useAlertStore } from "@/shared/lib"; import { columns } from "../model/group-users-table-columns"; import { GroupUsersApi } from "../api/group-users-service"; +import { AdditionalUserDialog } from "./additional-user-dialog"; type GroupUsersSectionProps = { groupId?: string; }; function GroupUsersSection({ groupId }: GroupUsersSectionProps) { + const queryClient = useQueryClient(); + const alert = useAlertStore((state) => state.setMessage); + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10, }); const [rowSelection, setRowSelection] = useState({}); - const { data: groupUsersList, refetch } = useQuery({ + const { data: groupUsersList } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.GROUP_USERS, groupId, pagination], queryFn: () => GroupUsersApi.findPageGroupUser({ @@ -55,11 +64,19 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) { if (selectedRows.length === 0 || !groupId) return; try { - for (const userUnqId of selectedRows) { - await GroupUsersApi.deleteGroupUser(groupId, userUnqId); - } - refetch(); - setRowSelection({}); + const usersToDelete = selectedRows.map((userUnqId) => ({ + mode: "D", + groupId, + userUnqId, + })); + + await GroupUsersApi.saveGroupUser(usersToDelete); + queryClient.invalidateQueries({ + queryKey: [ENDPOINT.CMS_SERVICE.GROUP_USERS, groupId], + }); + alert("사용자 삭제를 완료했습니다.", () => { + setRowSelection({}); + }); } catch (error) { console.error(error); } @@ -71,7 +88,7 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {

그룹 사용자 목록

- +