diff --git a/apps/cs/app/(with-base-layout)/apiman/register/[id]/page.tsx b/apps/cs/app/(with-base-layout)/apiman/register/[id]/page.tsx index 167a7e868..6d382c878 100644 --- a/apps/cs/app/(with-base-layout)/apiman/register/[id]/page.tsx +++ b/apps/cs/app/(with-base-layout)/apiman/register/[id]/page.tsx @@ -1,10 +1,12 @@ -import { ModifyPage } from "@/pages/apiman/ui/modify-page"; +import { ModifyPage as VModifyPage } from "@/pages/apiman/ui/modify-page"; -export default async function Page({ + +export default async function ModifyPage({ params, }: { - params: { id: string }; + params: Promise<{ id: number}>; }) { - const authorizationNo = parseInt(params.id); - return ; + const { id } = await params; + return ; } + \ No newline at end of file diff --git a/apps/cs/src/pages/apiman/api/apiman-api.ts b/apps/cs/src/pages/apiman/api/apiman-api.ts index 6f1d50256..ce9a8e189 100644 --- a/apps/cs/src/pages/apiman/api/apiman-api.ts +++ b/apps/cs/src/pages/apiman/api/apiman-api.ts @@ -6,7 +6,7 @@ export interface Api { authorizationNo: number; // API 일련번호 authorizationName: string; // API 명 urlPatternValue: string; // URL 패턴 - httpMethodCode: string; // 하이퍼텍스트 전송 프로토콜 메쏘드 값 (예: GET, POST) + httpMethodCode: string; // 하이퍼텍스트 전송 프로토콜 메쏘드 값 (예: GET, POST) sortSeq: number; // 정렬 순서 useYn: string; // 사용 여부 (Y/N) regDt: string; // 등록 일시 @@ -25,7 +25,7 @@ export interface ApiParams { export interface ApiFormData { authorizationNo: number | null; // API 일련번호 (신규 등록 시 null) - authorizationName: string; // API 명 + authorizationName: string; // API 명 urlPatternValue: string; // URL 패턴 httpMethodCode: string; // HTTP 메소드 sortSeq: number; // 정렬 순서 diff --git a/apps/cs/src/pages/apiman/model/apiman-table-columns.tsx b/apps/cs/src/pages/apiman/model/apiman-table-columns.tsx index f6817015c..22c0ba701 100644 --- a/apps/cs/src/pages/apiman/model/apiman-table-columns.tsx +++ b/apps/cs/src/pages/apiman/model/apiman-table-columns.tsx @@ -1,8 +1,13 @@ import { ColumnDef } from "@tanstack/react-table"; -import { Badge } from "@common/components"; -import { Api } from "../api/apiman-api"; +import { Badge, Button } from "@common/components"; +import type { Api } from "../api/apiman-api"; import dayjs from "dayjs"; +// API 권한 관리를 위한 타입 정의 +type ApiTableMeta = { + openRoleDialog: (api: Api) => void; +}; + export const columns: ColumnDef[] = [ { accessorKey: "authorizationNo", @@ -78,5 +83,29 @@ export const columns: ColumnDef[] = [ header: "수정일시", cell: ({ row }) => dayjs(row.original.mdfcnDt).format("YYYY-MM-DD"), }, - + { + id: "actions", + header: "권한", + cell: ({ row, table }) => { + // 메타 데이터에서 권한 다이얼로그 열기 함수 가져오기 + const meta = table.options.meta as ApiTableMeta | undefined; + + return ( + + ); + }, + }, ]; diff --git a/apps/cs/src/pages/apiman/ui/api-role-dialog.tsx b/apps/cs/src/pages/apiman/ui/api-role-dialog.tsx new file mode 100644 index 000000000..8c78fc5c0 --- /dev/null +++ b/apps/cs/src/pages/apiman/ui/api-role-dialog.tsx @@ -0,0 +1,201 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { + Dialog, + DialogTrigger, + Button, + DialogContent, + DialogHeader, + DialogTitle, + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, + Input, + RadioGroup, + RadioGroupItem, + DialogFooter, + DialogClose, + Checkbox, + Label, +} from "@common/components"; +import { ApiRoleApi, type ApiRoleByApi } from "../../apirole/api/apirole-api"; + +// 페이지네이션 응답을 위한 인터페이스 +interface Page { + content: T[]; + pageable: { + pageNumber: number; + pageSize: number; + }; + totalElements: number; + totalPages: number; +} + +// API 역할 인터페이스 정의 +interface Role extends ApiRoleByApi { + selected?: boolean; +} + +interface ApiRoleDialogProps { + api: ApiRoleByApi | null; + isOpen: boolean; + onClose: () => void; + onSave: (roleIds: string[]) => Promise; +} + +export function ApiRoleDialog({ api, isOpen, onClose, onSave }: ApiRoleDialogProps): JSX.Element { + const [roles, setRoles] = useState([]); + const [loading, setLoading] = useState(false); + + // API에서 역할 데이터 가져오기 + useEffect(() => { + if (isOpen && api && api.authorizationNo) { + // 실제 API 호출로 데이터 가져오기 + ApiRoleApi.getApiRoleByApiList({ authorizationNo: api.authorizationNo }) + .then((data: Page) => { + // 데이터를 Role 인터페이스에 맞게 변환 + const rolesWithSelected = data.content.map(role => ({ + ...role, + mode: role.useYn ==null?"C":"R", + selected: role.useYn === "Y" + })); + setRoles(rolesWithSelected); + }) + .catch(error => { + console.error("역할 데이터 가져오기 오류:", error); + // 오류 발생 시 빈 배열 설정 + setRoles([]); + }); + } + }, [isOpen, api]); + + const handleToggleRole = (roleId: string) => { + setRoles( + roles.map((role) => + role.roleId === roleId ? { ...role, useYn: role.useYn === "Y" ? "N" : "Y", mode: role.mode === "R" ? "U" : "C" } : role + ) + ); + }; + + const handleSave = async () => { + try { + setLoading(true); + //const selectedRoleIds = roles.filter((role) => role.useYn === "Y").map((role) => role.roleId ?? ""); + await onSave(roles); + onClose(); + } catch (error) { + console.error("권한 저장 중 오류 발생:", error); + } finally { + setLoading(false); + } + }; + + // Dialog가 닫힐 때 호출되는 함수 + const handleOpenChange = (open: boolean) => { + if (!open) { + onClose(); + } + }; + + return ( + + + + + API 권한 설정 - {api?.authorizationName} + + + +
+ + + + + + + + + + API 명 + + {api?.authorizationName || ""} + + HTTP 메소드 + + {api?.httpMethodCode || ""} + + + + URL 패턴 + + {api?.urlPatternValue || ""} + + + +
+
+ +
+ + + + 허용 + 권한 ID + 권한명 + 권한내용 + 사용여부 + + + + {roles.map((role) => ( + + + handleToggleRole(role.roleId)} + /> + + {role.roleId} + {role.roleName} + {role.roleContent} + + + {role.useYn === "Y" ? "사용" : "미사용"} + + + + ))} + {roles.length === 0 && ( + + + 권한 정보가 없습니다. + + + )} + +
+
+ + + + + +
+
+ ); +} diff --git a/apps/cs/src/pages/apiman/ui/apiman-form.tsx b/apps/cs/src/pages/apiman/ui/apiman-form.tsx index d52725668..f27502f4e 100644 --- a/apps/cs/src/pages/apiman/ui/apiman-form.tsx +++ b/apps/cs/src/pages/apiman/ui/apiman-form.tsx @@ -140,9 +140,9 @@ function ApimanForm({ form, authorizationNo, handleSave, handleDelete }: ApimanF )} /> - + - HTTP 메소드 + 요청 메소드 - + GET diff --git a/apps/cs/src/pages/apiman/ui/apiman-page.tsx b/apps/cs/src/pages/apiman/ui/apiman-page.tsx index d43d01168..fb0b82511 100644 --- a/apps/cs/src/pages/apiman/ui/apiman-page.tsx +++ b/apps/cs/src/pages/apiman/ui/apiman-page.tsx @@ -22,8 +22,10 @@ import { } from "@common/components"; import { ApimanApi, type Api, type ApiParams } from "../api/apiman-api"; +import { type ApiRoleByApi } from "../../apirole/api/apirole-api"; import { ENDPOINT } from "@/shared/config"; import { columns } from "../model/apiman-table-columns"; +import { ApiRoleDialog } from "./api-role-dialog"; function ApimanPage() { const router = useRouter(); @@ -34,9 +36,18 @@ function ApimanPage() { page: 0, size: 10, }); + + // API 역할 다이얼로그 상태 관리 + const [apiRoleDialog, setApiRoleDialog] = useState<{ + isOpen: boolean; + api: ApiRoleByApi | null; + }>({ + isOpen: false, + api: null, + }); const { data: apiList, refetch, isLoading } = useQuery({ - queryKey: [ENDPOINT.CMS_SERVICE.API, apiQuery], + queryKey: [ENDPOINT.USER_SERVICE.AUTHRTS, apiQuery], queryFn: () => ApimanApi.getApiList(apiQuery), enabled: false, }); @@ -53,6 +64,14 @@ function ApimanPage() { pageSize: apiQuery.size ?? 10, }, }, + meta: { + openRoleDialog: (api: ApiRoleByApi) => { + setApiRoleDialog({ + isOpen: true, + api, + }); + }, + }, onPaginationChange: (updater) => { const newPagination = typeof updater === "function" @@ -77,48 +96,46 @@ function ApimanPage() { setApiQuery(prev => ({ ...prev, page: 0 })); refetch(); }; - - const handleReset = () => { - setApiQuery({ - authorizationName: "", - httpMethodCode: "", - useYn: "", - page: 0, - size: 10, - }); - refetch(); - }; + const handleRowClick = (api: Api) => { router.push(`/apiman/register/${api.authorizationNo}`); }; + // API 역할 저장 핸들러 + const handleSaveRoles = async (roles: ApiRoleByApi[]) => { + try { + // 실제 구현에서는 API 호출로 대체 + console.log(`API ${apiRoleDialog.api?.authorizationNo}에 권한 저장:`, roles); + // 성공 시 다이얼로그 닫기 + setApiRoleDialog({ isOpen: false, api: null }); + // 목록 새로고침 + refetch(); + } catch (error) { + console.error("권한 저장 중 오류 발생:", error); + } + }; + return ( -
-
-
-
- - setApiQuery( prev => ({ ...prev, authorizationName: e.target.value }))} - placeholder="API 명 입력" - /> +
+ {/* API 역할 다이얼로그 */} + setApiRoleDialog({ isOpen: false, api: null })} + onSave={handleSaveRoles} + /> + +
+ -
- -
- - - - setApiQuery( prev => ({ ...prev, httpMethodCode: value === "ALL" ? "" : value }))} > - - + + 전체 @@ -131,78 +148,63 @@ function ApimanPage() { HEAD -
- -
- - -
- -
- - -
+ + setApiQuery( prev => ({ ...prev, authorizationName: e.target.value }))} + placeholder="API 명 입력" + /> +
+ + -
-
-
- 총 {apiList?.totalElements || 0}건 -
- -
+
+
+

모듈API 목록

+ +
- - {apiList && apiList.totalElements > 0 && ( -
- setApiQuery({ ...apiQuery, page })} - /> - -
- )} + +
+
+ 총 {apiList?.totalElements ?? 0} 건 +
+ + setApiQuery(prev => ({ ...prev, page: page - 1 })) + } + /> + +
); diff --git a/apps/cs/src/pages/apiman/ui/create-page.tsx b/apps/cs/src/pages/apiman/ui/create-page.tsx index 2e93b6143..69f774765 100644 --- a/apps/cs/src/pages/apiman/ui/create-page.tsx +++ b/apps/cs/src/pages/apiman/ui/create-page.tsx @@ -18,7 +18,7 @@ const apiFormSchema = z.object({ urlPatternValue: z.string().min(1, "URL 패턴을 입력해주세요"), httpMethodCode: z.string().min(1, "HTTP 메소드를 선택해주세요"), sortSeq: z.number().min(0, "정렬 순서는 0 이상이어야 합니다"), - useYn: z.string(), + useYn: z.string(), }); function CreatePage() { @@ -56,7 +56,7 @@ function CreatePage() { urlPatternValue: "", httpMethodCode: "", sortSeq: 0, - useYn: "Y" + useYn: "Y" } }); diff --git a/apps/cs/src/pages/apiman/ui/modify-page.tsx b/apps/cs/src/pages/apiman/ui/modify-page.tsx index 242aeccf8..a6185e8d0 100644 --- a/apps/cs/src/pages/apiman/ui/modify-page.tsx +++ b/apps/cs/src/pages/apiman/ui/modify-page.tsx @@ -18,7 +18,7 @@ const apiFormSchema = z.object({ urlPatternValue: z.string().min(1, "URL 패턴을 입력해주세요"), httpMethodCode: z.string().min(1, "HTTP 메소드를 선택해주세요"), sortSeq: z.number(), - useYn: z.string(), + useYn: z.string(), }); function ModifyPage({ authorizationNo }: { authorizationNo: number }) { @@ -91,7 +91,10 @@ function ModifyPage({ authorizationNo }: { authorizationNo: number }) { { + await updateApi(data); + return; + }} handleDelete={deleteApi} />
diff --git a/apps/cs/src/pages/apirole/api/apirole-api.ts b/apps/cs/src/pages/apirole/api/apirole-api.ts new file mode 100644 index 000000000..c525c9a6a --- /dev/null +++ b/apps/cs/src/pages/apirole/api/apirole-api.ts @@ -0,0 +1,77 @@ +import type { Page } from "@/shared/types"; +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; + +export interface ApiRole { + roleId?: string; + /** @format int64 */ + authorizationNo?: number; + authorizationName?: string; + urlPatternValue?: string; + httpMethodCode?: string; + /** @format int32 */ + sortSeq?: number; + createdAt?: boolean; +} +/** API 에서 역할 추가 (API에 대한 역할) */ +export interface ApiRoleByApi { + roleId?: string; + roleName?: string; + roleContent?: string; + useYn?: string; + /** @format int64 */ + authorizationNo?: number; + mode?:string; +} + +export interface ApiRoleParams { + authorizationNo?: number; // API 일련번호 검색 + roleId?: string; // API 역할 검색 + page?: number; // 페이지 번호 + size?: number; // 페이지 크기 +} + +export interface ApiRoleFormData { + authorizationNo: number | null; // API 일련번호 (신규 등록 시 null) + authorizationName: string; // API 명 + urlPatternValue: string; // URL 패턴 + httpMethodCode: string; // HTTP 메소드 + sortSeq: number; // 정렬 순서 + useYn: string; // 사용 여부 +} + +export const ApiRoleApi = { + getApiRoleList: async (params: ApiRoleParams) => { + const { data } = await client.get>( + `${ENDPOINT.USER_SERVICE.ROLES_AUTHRTS}`, + { params } + ); + return data; + }, + getApiRoleByApiList: async (params: ApiRoleParams) => { + const { data } = await client.get>( + `${ENDPOINT.USER_SERVICE.ROLES_AUTHRTS}/api`, + { params } + ); + return data; + }, + getApiRole: async (authorizationNo: number) => { + const { data } = await client.get(`${ENDPOINT.USER_SERVICE.ROLES_AUTHRTS}/${authorizationNo}`); + return data; + }, + + createApiRole: async (apiData: ApiRoleFormData) => { + const { data } = await client.post(ENDPOINT.USER_SERVICE.ROLES_AUTHRTS, apiData); + return data; + }, + + updateApiRole: async (authorizationNo: number, apiData: ApiRoleFormData) => { + const { data } = await client.put(`${ENDPOINT.USER_SERVICE.ROLES_AUTHRTS}/${authorizationNo}`, apiData); + return data; + }, + + deleteApiRole: async (authorizationNo: number) => { + const { data } = await client.delete(`${ENDPOINT.USER_SERVICE.ROLES_AUTHRTS}/${authorizationNo}`); + return data; + }, +}; diff --git a/apps/cs/src/pages/group/ui/group-page.tsx b/apps/cs/src/pages/group/ui/group-page.tsx index a9b198f3d..99459c185 100644 --- a/apps/cs/src/pages/group/ui/group-page.tsx +++ b/apps/cs/src/pages/group/ui/group-page.tsx @@ -103,13 +103,13 @@ function GroupPage() {

그룹 목록

- +
- 총 {groupList?.totalElements ?? 0} 개 + 총 {groupList?.totalElements ?? 0} 건