feat: 모듈API 권한관리 다이얼로그 작업

This commit is contained in:
2025-04-16 17:53:40 +09:00
parent 6148ac2df9
commit 7d4cdb095d
7 changed files with 285 additions and 18 deletions
@@ -0,0 +1,44 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
RoleAuthorizationsService,
RoleAuthorizationsParams,
RoleAuthorizationsList,
RoleAuthorizationsData,
RoleAuthorizations,
} from "../model/role-authorizations-interface";
export class RoleAuthorizationsApiService implements RoleAuthorizationsService {
public async getRoleAuthorizationsList(
params: RoleAuthorizationsParams,
): Promise<RoleAuthorizationsList> {
const response = await client.get<RoleAuthorizationsList>(
`${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}/api`,
{ params },
);
return response.data;
}
public async createRoleAuthorizations(
data: RoleAuthorizationsData,
): Promise<RoleAuthorizations> {
const response = await client.post<RoleAuthorizations>(
`${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}`,
data,
);
return response.data;
}
public async updateRoleAuthorizations(
authorizationNo: string,
data: RoleAuthorizationsData,
): Promise<RoleAuthorizations> {
const response = await client.put<RoleAuthorizations>(
`${ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS}/${authorizationNo}`,
data,
);
return response.data;
}
}
export const RoleAuthorizationsApi = new RoleAuthorizationsApiService();
@@ -4,6 +4,9 @@ import type { PageableList, PageableParams } from "@/shared/model";
export interface AuthorizationsParams extends PageableParams {
keywordType?: string;
keyword?: string;
authorizationName?: string;
httpMethodCode?: string;
useYn?: string;
}
export interface Authorizations {
@@ -17,16 +20,18 @@ export interface Authorizations {
mdfcnDt: string;
}
export interface RoleAuthorizations {
roleId: string;
authorizationNo: number;
authorizationName: string;
urlPatternValue: string;
httpMethodCode: string;
sortSeq: number;
createdAt: string;
}
export interface WithRoleAuthorizations extends Authorizations {
roleAuthorizations: {
roleId: string;
authorizationNo: number;
authorizationName: string;
urlPatternValue: string;
httpMethodCode: string;
sortSeq: number;
createdAt: string;
}[];
roleAuthorizations: RoleAuthorizations[];
}
export const authorizationFormSchema = z.object({
@@ -0,0 +1,44 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export interface RoleAuthorizationsParams extends PageableParams {
keywordType?: string;
keyword?: string;
authorizationNo?: string;
}
export interface RoleAuthorizations {
roleId: string;
roleName: string;
roleContent: string;
useYn: string;
authorizationNo: number;
}
export const roleAuthorizationsSchema = z.object({
roleId: z.string(),
authorizationNo: z.number(),
});
export const roleAuthorizationsFormSchema = z.object({
roleAuthorizations: z.array(roleAuthorizationsSchema),
});
export type RoleAuthorizationsList = PageableList<RoleAuthorizations>;
export type RoleAuthorizationsData = z.infer<typeof roleAuthorizationsSchema>[];
export type RoleAuthorizationsFormData = z.infer<
typeof roleAuthorizationsFormSchema
>;
export interface RoleAuthorizationsService {
getRoleAuthorizationsList: (
params: RoleAuthorizationsParams,
) => Promise<RoleAuthorizationsList>;
createRoleAuthorizations: (
data: RoleAuthorizationsData,
) => Promise<RoleAuthorizations>;
updateRoleAuthorizations: (
authorizationNo: string,
data: RoleAuthorizationsData,
) => Promise<RoleAuthorizations>;
}
@@ -0,0 +1,39 @@
import { type ColumnDef } from "@tanstack/react-table";
import type { RoleAuthorizations } from "./role-authorizations-interface";
import { Checkbox } from "@common/components/ui";
export const columns: ColumnDef<RoleAuthorizations>[] = [
{
id: "select",
header: ({ table }) => (
<Checkbox
checked={table.getIsAllPageRowsSelected()}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) =>
table.toggleAllPageRowsSelected(!!checked)
}
className="h-4 w-4 rounded border-gray-300"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) => row.toggleSelected(!!checked)}
className="h-4 w-4 rounded border-gray-300"
/>
),
},
{
accessorKey: "roleId",
header: "권한ID",
},
{
accessorKey: "roleName",
header: "권한명",
},
{
accessorKey: "roleContent",
header: "권한설명",
},
];
@@ -1,6 +1,7 @@
import dayjs from "dayjs";
import { type ColumnDef } from "@tanstack/react-table";
import type { Authorizations } from "./authorizations-interface";
import { ManagementAuthorityDialog } from "../ui/management-authority-dialog";
export const columns: ColumnDef<Authorizations>[] = [
{
@@ -36,4 +37,22 @@ export const columns: ColumnDef<Authorizations>[] = [
return date.format("YYYY-MM-DD");
},
},
{
id: "action",
header: "권한관리",
cell: ({ row }) => {
return (
<div
onClick={(e) => {
e.stopPropagation();
}}
>
<ManagementAuthorityDialog
authorizationNo={row.original.authorizationNo.toString()}
rowData={row.original}
/>
</div>
);
},
},
];
@@ -0,0 +1,114 @@
import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
useReactTable,
type RowSelectionState,
} from "@tanstack/react-table";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import {
Button,
DataTable,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@common/components/ui";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import {
roleAuthorizationFormSchema,
type RoleAuthorizationFormData,
type RoleAuthorizationData,
} from "../model/role-authorizations-interface";
import { columns } from "../model/role-authorizations-table-columns";
import type { Authorizations } from "../model/authorizations-interface";
import { RoleAuthorizationsApi } from "../api/role-authorization-service";
type ManagementAuthorityDialogProps = {
authorizationNo?: string;
rowData?: Authorizations;
} & React.ComponentProps<typeof Dialog>;
function ManagementAuthorityDialog({
authorizationNo,
...props
}: ManagementAuthorityDialogProps) {
const { confirm } = useConfirm();
const { setMessage: alert } = useAlertStore((state) => state);
const [isOpened, setIsOpened] = useState(false);
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const { data: roleAuthorizationList } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS, authorizationNo],
queryFn: () =>
RoleAuthorizationsApi.getRoleAuthorizationsList({ authorizationNo }),
enabled: !!authorizationNo && isOpened,
});
const { mutateAsync: saveRoleAuthorization } = useMutation({
mutationFn: (data: RoleAuthorizationData) =>
RoleAuthorizationsApi.createRoleAuthorization(data),
onSuccess: () => {
alert("저장되었습니다.");
},
});
const form = useForm<RoleAuthorizationFormData>({
resolver: zodResolver(roleAuthorizationFormSchema),
values: {
roleAuthorizations: roleAuthorizationList?.content ?? [],
},
});
const table = useReactTable({
data: roleAuthorizationList?.content ?? [],
columns,
state: {
rowSelection,
},
getCoreRowModel: getCoreRowModel(),
onRowSelectionChange: setRowSelection,
});
const handleSave = async () => {
if (await confirm("선택한 권한을 허용하시겠습니까?")) {
const data = form.getValues("roleAuthorizations");
await saveRoleAuthorization(data);
}
};
return (
<Dialog {...props}>
<DialogTrigger asChild>
<Button size="sm" onClick={() => setIsOpened(true)}>
권한관리
</Button>
</DialogTrigger>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>모듈API 권한관리</DialogTitle>
</DialogHeader>
<DataTable table={table} />
<DialogFooter className="!justify-center">
<Button size="lg" onClick={handleSave}>
허용
</Button>
<DialogClose asChild>
<Button size="lg" color="white">
취소
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
export { ManagementAuthorityDialog };
+11 -9
View File
@@ -23,8 +23,8 @@ function ModulePage() {
const [authorizationQuery, setAuthorizationQuery] =
useState<AuthorizationsParams>({
keywordType: "",
keyword: "",
authorizationName: "",
httpMethodCode: "",
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
@@ -32,7 +32,7 @@ function ModulePage() {
});
const { data: authorizationsList, refetch } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS],
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS],
queryFn: () =>
AuthorizationsApi.getAuthorizationsList({
...authorizationQuery,
@@ -79,24 +79,26 @@ function ModulePage() {
<form onSubmit={handleSubmit} className="flex gap-2 p-2">
<div className="flex space-x-6">
<div className="flex items-center gap-2">
<Label htmlFor="keywordType" className="text-label">
<Label htmlFor="httpMethodCode" className="text-label">
요청 메소드
</Label>
<CodeSelect
upCd="HTTPMHD"
onValueChange={handleAuthorizationQueryChange("keywordType")}
onValueChange={handleAuthorizationQueryChange("httpMethodCode")}
/>
</div>
<div className="flex items-center gap-2">
<Label htmlFor="keyword" className="text-label">
<Label htmlFor="authorizationName" className="text-label">
API명
</Label>
<Input
id="keyword"
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
value={authorizationQuery.keyword}
value={authorizationQuery.authorizationName}
onChange={(e) =>
handleAuthorizationQueryChange("keyword")(e.target.value)
handleAuthorizationQueryChange("authorizationName")(
e.target.value,
)
}
/>
</div>
@@ -111,7 +113,7 @@ function ModulePage() {
<h3 className="text-2xl font-medium text-label">모듈API 목록</h3>
<Button onClick={() => router.push("/module/register")}>등록</Button>
</div>
<div className="card !m-[1.2rem] h-[49.5rem] overflow-auto !p-0">
<div className="card !m-[1.2rem] h-[59rem] overflow-auto !p-0">
<DataTable table={table} onRowClick={handleRowClick} viewPagination />
</div>
</div>