feat: 그룹 사용자 선택 삭제 기능
This commit is contained in:
@@ -20,6 +20,7 @@ export interface UserSearchParams extends PageableParams {
|
||||
grpId?: string;
|
||||
frDateYmd?: string;
|
||||
toDateYmd?: string;
|
||||
userName?: string;
|
||||
}
|
||||
|
||||
// 사용자 목록 응답 인터페이스
|
||||
|
||||
@@ -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<GroupUserList> {
|
||||
const response = await client.get<GroupUserList>(
|
||||
): Promise<PageGroupUserList> {
|
||||
const response = await client.get<PageGroupUserList>(
|
||||
`${ENDPOINT.CMS_SERVICE.GROUP_USERS}`,
|
||||
{ params },
|
||||
);
|
||||
@@ -34,9 +35,9 @@ export class GroupUsersApiService implements GroupUserService {
|
||||
|
||||
// 그룹 사용자 저장
|
||||
public async saveGroupUser(
|
||||
data: GroupUserSaveData,
|
||||
): Promise<GroupUserDetailResponse> {
|
||||
const response = await client.post<GroupUserDetailResponse>(
|
||||
data: GroupUserSaveData[],
|
||||
): Promise<GroupUserListResponseDto[]> {
|
||||
const response = await client.post<GroupUserListResponseDto[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.GROUP_USERS}`,
|
||||
data,
|
||||
);
|
||||
|
||||
@@ -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<GroupUserListResponseDto>;
|
||||
|
||||
// 그룹 사용자 상세 응답 인터페이스
|
||||
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<GroupUserList>;
|
||||
findPageGroupUser: (
|
||||
params: GroupUserSearchParams,
|
||||
) => Promise<PageGroupUserList>;
|
||||
|
||||
// 그룹 사용자 단건 조회
|
||||
findByGroupAndUserId: (
|
||||
@@ -90,7 +61,9 @@ export interface GroupUserService {
|
||||
) => Promise<GroupUserDetailResponse>;
|
||||
|
||||
// 그룹 사용자 저장
|
||||
saveGroupUser: (data: GroupUserSaveData) => Promise<GroupUserDetailResponse>;
|
||||
saveGroupUser: (
|
||||
data: GroupUserSaveData[],
|
||||
) => Promise<GroupUserListResponseDto[]>;
|
||||
|
||||
// 그룹 사용자 수정
|
||||
updateGroupUser: (
|
||||
|
||||
@@ -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<UserSearchParams>({
|
||||
userName: "",
|
||||
});
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [userRowSelection, setUserRowSelection] = useState<RowSelectionState>(
|
||||
{},
|
||||
);
|
||||
|
||||
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<UserListResponse>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="전체 선택"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => 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 (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button disabled={!groupId}>사용자 추가</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-4xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>사용자 추가</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="mb-4 flex items-end gap-2">
|
||||
<Input
|
||||
value={userSearchQuery.userName}
|
||||
onChange={(e) =>
|
||||
setUserSearchQuery({
|
||||
...userSearchQuery,
|
||||
userName: e.target.value,
|
||||
})
|
||||
}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="사용자 이름으로 검색"
|
||||
/>
|
||||
<Button onClick={handleSearch}>검색</Button>
|
||||
</div>
|
||||
<DataTable table={userTable} viewPagination />
|
||||
<DialogFooter>
|
||||
<Button color="white" onClick={() => setOpen(false)}>
|
||||
취소
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleAddUsers}
|
||||
disabled={userTable.getSelectedRowModel().rows.length === 0}
|
||||
>
|
||||
추가
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { AdditionalUserDialog };
|
||||
@@ -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<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
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) {
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">그룹 사용자 목록</h3>
|
||||
<div className="flex gap-2">
|
||||
<Button>등록</Button>
|
||||
<AdditionalUserDialog groupId={groupId} />
|
||||
<Button
|
||||
color="red"
|
||||
disabled={table.getSelectedRowModel().rows.length === 0}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { GroupsPage } from "./groups-page";
|
||||
Reference in New Issue
Block a user