api 관리 프로그램 추가, 조회시 ApiQuery 값이 변경되면 바로 refetch함
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
import { ApimanPage } from "@/pages/apiman";
|
||||
|
||||
export default function Page() {
|
||||
return <ApimanPage />;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ModifyPage } from "@/pages/apiman/ui/modify-page";
|
||||
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: { id: string };
|
||||
}) {
|
||||
const authorizationNo = parseInt(params.id);
|
||||
return <ModifyPage authorizationNo={authorizationNo} />;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
"use client";
|
||||
|
||||
import { CreatePage } from "@/pages/apiman/ui/create-page";
|
||||
export default CreatePage;
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { Page } from "@/shared/types";
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
export interface Api {
|
||||
authorizationNo: number; // API 일련번호
|
||||
authorizationName: string; // API 명
|
||||
urlPatternValue: string; // URL 패턴
|
||||
httpMethodCode: string; // 하이퍼텍스트 전송 프로토콜 메쏘드 값 (예: GET, POST)
|
||||
sortSeq: number; // 정렬 순서
|
||||
useYn: string; // 사용 여부 (Y/N)
|
||||
regDt: string; // 등록 일시
|
||||
rgtr: string; // 등록자 UUID
|
||||
mdfcnDt: string | null; // 갱신 일시
|
||||
mdfr: string | null; // 갱신자 UUID
|
||||
}
|
||||
|
||||
export interface ApiParams {
|
||||
authorizationName?: string; // API 명 검색
|
||||
httpMethodCode?: string; // HTTP 메소드 검색
|
||||
useYn?: string; // 사용 여부 검색
|
||||
page?: number; // 페이지 번호
|
||||
size?: number; // 페이지 크기
|
||||
}
|
||||
|
||||
export interface ApiFormData {
|
||||
authorizationNo: number | null; // API 일련번호 (신규 등록 시 null)
|
||||
authorizationName: string; // API 명
|
||||
urlPatternValue: string; // URL 패턴
|
||||
httpMethodCode: string; // HTTP 메소드
|
||||
sortSeq: number; // 정렬 순서
|
||||
useYn: string; // 사용 여부
|
||||
}
|
||||
|
||||
export const ApimanApi = {
|
||||
getApiList: async (params: ApiParams) => {
|
||||
const { data } = await client.get<Page<Api>>(
|
||||
`${ENDPOINT.USER_SERVICE.AUTHRTS}`,
|
||||
{ params }
|
||||
);
|
||||
return data;
|
||||
},
|
||||
|
||||
getApi: async (authorizationNo: number) => {
|
||||
const { data } = await client.get<Api>(`${ENDPOINT.USER_SERVICE.AUTHRTS}/${authorizationNo}`);
|
||||
return data;
|
||||
},
|
||||
|
||||
createApi: async (apiData: ApiFormData) => {
|
||||
const { data } = await client.post<Api>(ENDPOINT.USER_SERVICE.AUTHRTS, apiData);
|
||||
return data;
|
||||
},
|
||||
|
||||
updateApi: async (authorizationNo: number, apiData: ApiFormData) => {
|
||||
const { data } = await client.put<Api>(`${ENDPOINT.USER_SERVICE.AUTHRTS}/${authorizationNo}`, apiData);
|
||||
return data;
|
||||
},
|
||||
|
||||
deleteApi: async (authorizationNo: number) => {
|
||||
const { data } = await client.delete<void>(`${ENDPOINT.USER_SERVICE.AUTHRTS}/${authorizationNo}`);
|
||||
return data;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
export { ApimanPage } from "./ui/apiman-page";
|
||||
export { ApimanForm } from "./ui/apiman-form";
|
||||
export { CreatePage } from "./ui/create-page";
|
||||
export { ModifyPage } from "./ui/modify-page";
|
||||
export { ApimanApi } from "./api/apiman-api";
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { Badge } from "@common/components";
|
||||
import { Api } from "../api/apiman-api";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const columns: ColumnDef<Api>[] = [
|
||||
{
|
||||
accessorKey: "authorizationNo",
|
||||
header: "번호",
|
||||
cell: ({ row }) => <div>{row.original.authorizationNo}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "authorizationName",
|
||||
header: "API 명",
|
||||
cell: ({ row }) => <div>{row.original.authorizationName}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "urlPatternValue",
|
||||
header: "URL 패턴",
|
||||
cell: ({ row }) => (
|
||||
<div className="max-w-md truncate text-left" title={row.original.urlPatternValue}>
|
||||
{row.original.urlPatternValue}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "httpMethodCode",
|
||||
header: "HTTP 메소드",
|
||||
cell: ({ row }) => {
|
||||
const method = row.original.httpMethodCode;
|
||||
let color = "bg-gray-500";
|
||||
|
||||
switch (method) {
|
||||
case "GET":
|
||||
color = "bg-blue-500";
|
||||
break;
|
||||
case "POST":
|
||||
color = "bg-green-500";
|
||||
break;
|
||||
case "PUT":
|
||||
color = "bg-orange-500";
|
||||
break;
|
||||
case "DELETE":
|
||||
color = "bg-red-500";
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge className={`${color} text-white`}>
|
||||
{method}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "sortSeq",
|
||||
header: "정렬 순서",
|
||||
cell: ({ row }) => <div className="text-center">{row.original.sortSeq}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "useYn",
|
||||
header: "사용 여부",
|
||||
cell: ({ row }) => {
|
||||
const useYn = row.original.useYn;
|
||||
return (
|
||||
<Badge className={useYn === "Y" ? "bg-green-100 text-green-800" : "bg-red-100 text-red-800"}>
|
||||
{useYn === "Y" ? "사용" : "미사용"}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "mdfrNm",
|
||||
header: "수정자",
|
||||
},
|
||||
{
|
||||
accessorKey: "mdfcnDt",
|
||||
header: "수정일시",
|
||||
cell: ({ row }) => dayjs(row.original.mdfcnDt).format("YYYY-MM-DD"),
|
||||
},
|
||||
|
||||
];
|
||||
@@ -0,0 +1,256 @@
|
||||
// apps/cs/src/pages/apiman/ui/apiman-form.tsx
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { type UseFormReturn } from "react-hook-form";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Label,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@common/components";
|
||||
import { FormItem, FormControl, FormField, Form } from "@/shared/ui/form";
|
||||
import type { ApiFormData } from "../api/apiman-api";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
|
||||
interface ApimanFormProps {
|
||||
form: UseFormReturn<ApiFormData>;
|
||||
authorizationNo?: number;
|
||||
handleSave: (data: ApiFormData) => Promise<void>;
|
||||
handleDelete?: () => Promise<void>;
|
||||
}
|
||||
|
||||
function ApimanForm({ form, authorizationNo, handleSave, handleDelete }: ApimanFormProps) {
|
||||
const isModifyMode = !!authorizationNo;
|
||||
const router = useRouter();
|
||||
const { confirm } = useConfirm();
|
||||
const [onConfirm, setOnConfirm] = useState(false);
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
const onSubmit = async (data: ApiFormData) => {
|
||||
const confirmMessage = isModifyMode
|
||||
? "수정하시겠습니까?"
|
||||
: "저장하시겠습니까?";
|
||||
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm(confirmMessage)) {
|
||||
await handleSave(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onDelete = async () => {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm("삭제하시겠습니까?")) {
|
||||
handleDelete?.();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="80%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
{isModifyMode && (
|
||||
<TableRow>
|
||||
<TableHead>API 일련번호</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="authorizationNo"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value?.toString() ?? ""}
|
||||
className="w-[12rem] !text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
<TableRow>
|
||||
<TableHead>API 명</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="authorizationName"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="API 명을 입력하세요"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>URL 패턴</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="urlPatternValue"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
placeholder="URL 패턴을 입력하세요 (예: /api/users/{id})"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>HTTP 메소드</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="httpMethodCode"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select
|
||||
value={field.value || undefined}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<SelectTrigger className="!text-[1.3rem]">
|
||||
<SelectValue placeholder="HTTP 메소드를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="GET">GET</SelectItem>
|
||||
<SelectItem value="POST">POST</SelectItem>
|
||||
<SelectItem value="PUT">PUT</SelectItem>
|
||||
<SelectItem value="DELETE">DELETE</SelectItem>
|
||||
<SelectItem value="PATCH">PATCH</SelectItem>
|
||||
<SelectItem value="OPTIONS">OPTIONS</SelectItem>
|
||||
<SelectItem value="HEAD">TRACE</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>정렬 순서</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sortSeq"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="number"
|
||||
{...field}
|
||||
value={field.value?.toString() ?? ""}
|
||||
className="w-[12rem] !text-[1.3rem]"
|
||||
onChange={(e) => field.onChange(parseInt(e.target.value) || 0)}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableCell className="border">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="useYn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
{...field}
|
||||
name="useYn"
|
||||
onValueChange={field.onChange}
|
||||
value={field.value}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="Y" id="useY" />
|
||||
<Label htmlFor="useY">예</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="N" id="useN" />
|
||||
<Label htmlFor="useN">아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex justify-center gap-4 pb-12 pt-10">
|
||||
<Button
|
||||
size="lg"
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
목록
|
||||
</Button>
|
||||
<Button size="lg" type="submit">
|
||||
저장
|
||||
</Button>
|
||||
{isModifyMode && (
|
||||
<Button size="lg" variant="destructive" type="button" onClick={onDelete}>
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<ConfirmDialog open={onConfirm} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { ApimanForm };
|
||||
@@ -0,0 +1,211 @@
|
||||
"use client";
|
||||
import "@common/assets/styles/grid.css";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import {
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
useReactTable
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
Input,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Pagination
|
||||
} from "@common/components";
|
||||
|
||||
import { ApimanApi, type Api, type ApiParams } from "../api/apiman-api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "../model/apiman-table-columns";
|
||||
|
||||
function ApimanPage() {
|
||||
const router = useRouter();
|
||||
const [apiQuery, setApiQuery] = useState<ApiParams>({
|
||||
authorizationName: "",
|
||||
httpMethodCode: "",
|
||||
useYn: "",
|
||||
page: 0,
|
||||
size: 10,
|
||||
});
|
||||
|
||||
const { data: apiList, refetch, isLoading } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.API, apiQuery],
|
||||
queryFn: () => ApimanApi.getApiList(apiQuery),
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
const table = useReactTable({
|
||||
data: apiList?.content ?? [],
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
pageCount: apiList?.totalPages ?? -1,
|
||||
state: {
|
||||
pagination: {
|
||||
pageIndex: apiQuery.page ?? 0,
|
||||
pageSize: apiQuery.size ?? 10,
|
||||
},
|
||||
},
|
||||
onPaginationChange: (updater) => {
|
||||
const newPagination =
|
||||
typeof updater === "function"
|
||||
? updater({ pageIndex: apiQuery.page ?? 0, pageSize: apiQuery.size ?? 10 })
|
||||
: updater;
|
||||
|
||||
setApiQuery((prev) => ({
|
||||
...prev,
|
||||
page: newPagination.pageIndex,
|
||||
size: newPagination.pageSize,
|
||||
}));
|
||||
},
|
||||
manualPagination: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [apiQuery, refetch]);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
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}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-6">
|
||||
<div className="bg-white p-6 rounded-lg shadow mb-6">
|
||||
<form onSubmit={handleSubmit} className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<Label htmlFor="authorizationName">API 명</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
|
||||
id="authorizationName"
|
||||
value={apiQuery.authorizationName || ""}
|
||||
onChange={(e) => setApiQuery( prev => ({ ...prev, authorizationName: e.target.value }))}
|
||||
placeholder="API 명 입력"
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
|
||||
<Label htmlFor="httpMethodCode">HTTP 메소드</Label>
|
||||
<Select
|
||||
value={apiQuery.httpMethodCode || undefined}
|
||||
onValueChange={(value) => setApiQuery( prev => ({ ...prev, httpMethodCode: value === "ALL" ? "" : value }))}
|
||||
>
|
||||
<SelectTrigger id="httpMethodCode">
|
||||
<SelectValue placeholder="HTTP 메소드 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ALL">전체</SelectItem>
|
||||
<SelectItem value="GET">GET</SelectItem>
|
||||
<SelectItem value="POST">POST</SelectItem>
|
||||
<SelectItem value="PUT">PUT</SelectItem>
|
||||
<SelectItem value="DELETE">DELETE</SelectItem>
|
||||
<SelectItem value="OPTIONS">OPTIONS</SelectItem>
|
||||
<SelectItem value="PATCH">PATCH</SelectItem>
|
||||
<SelectItem value="HEAD">HEAD</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="useYn">사용 여부</Label>
|
||||
<Select
|
||||
value={apiQuery.useYn || undefined}
|
||||
onValueChange={(value) => setApiQuery({ ...apiQuery, useYn: value === "ALL" ? "" : value })}
|
||||
>
|
||||
<SelectTrigger id="useYn">
|
||||
<SelectValue placeholder="사용 여부 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ALL">전체</SelectItem>
|
||||
<SelectItem value="Y">사용</SelectItem>
|
||||
<SelectItem value="N">미사용</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-3 flex justify-end gap-2">
|
||||
<Button type="button" variant="outline" onClick={handleReset}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button type="submit">
|
||||
검색
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="p-4 flex justify-between items-center">
|
||||
<div>
|
||||
총 <span className="font-bold">{apiList?.totalElements || 0}</span>건
|
||||
</div>
|
||||
<Button onClick={() => router.push("/apiman/register")}>
|
||||
API 등록
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
table={table}
|
||||
isLoading={isLoading}
|
||||
onRowClick={handleRowClick}
|
||||
/>
|
||||
|
||||
{apiList && apiList.totalElements > 0 && (
|
||||
<div className="p-4 flex justify-center items-center gap-4">
|
||||
<Pagination
|
||||
currentPage={apiQuery.page ?? 0}
|
||||
totalPages={apiList.totalPages}
|
||||
onPageChange={(page) => setApiQuery({ ...apiQuery, page })}
|
||||
/>
|
||||
<Select
|
||||
value={String(apiQuery.size)}
|
||||
onValueChange={(value) =>
|
||||
setApiQuery(prev => ({ ...prev, page: 0, size: Number(value) }))
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="h-8 w-[70px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{[2,10, 20, 30, 40, 50,100].map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={String(pageSize)}>
|
||||
{pageSize}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ApimanPage };
|
||||
@@ -0,0 +1,76 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { ApimanApi, type ApiFormData } from "../api/apiman-api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { ApimanForm } from "./apiman-form";
|
||||
import { z } from "zod";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
// API 스키마 정의
|
||||
const apiFormSchema = z.object({
|
||||
authorizationNo: z.number().nullable(),
|
||||
authorizationName: z.string().min(1, "API 명을 입력해주세요"),
|
||||
urlPatternValue: z.string().min(1, "URL 패턴을 입력해주세요"),
|
||||
httpMethodCode: z.string().min(1, "HTTP 메소드를 선택해주세요"),
|
||||
sortSeq: z.number().min(0, "정렬 순서는 0 이상이어야 합니다"),
|
||||
useYn: z.string(),
|
||||
});
|
||||
|
||||
function CreatePage() {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { mutateAsync: createApi } = useMutation({
|
||||
mutationFn: (data: ApiFormData) => ApimanApi.createApi(data),
|
||||
onSuccess: () => {
|
||||
alert("등록되었습니다.", () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.AUTHRTS],
|
||||
});
|
||||
router.push("/apiman");
|
||||
});
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
const axiosError = error as AxiosError<{ message: string }>;
|
||||
if (axiosError.response?.data) {
|
||||
alert(axiosError.response.data.message);
|
||||
} else if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
} else {
|
||||
alert("알 수 없는 오류가 발생했습니다.");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<ApiFormData>({
|
||||
resolver: zodResolver(apiFormSchema),
|
||||
defaultValues: {
|
||||
authorizationNo: null,
|
||||
authorizationName: "",
|
||||
urlPatternValue: "",
|
||||
httpMethodCode: "",
|
||||
sortSeq: 0,
|
||||
useYn: "Y"
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">API 등록</h3>
|
||||
</div>
|
||||
<ApimanForm
|
||||
form={form}
|
||||
handleSave={createApi}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { CreatePage };
|
||||
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ApimanApi, type ApiFormData } from "../api/apiman-api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { ApimanForm } from "./apiman-form";
|
||||
import { z } from "zod";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
// API 스키마 정의
|
||||
const apiFormSchema = z.object({
|
||||
authorizationNo: z.number().nullable(),
|
||||
authorizationName: z.string().min(1, "API 명을 입력해주세요"),
|
||||
urlPatternValue: z.string().min(1, "URL 패턴을 입력해주세요"),
|
||||
httpMethodCode: z.string().min(1, "HTTP 메소드를 선택해주세요"),
|
||||
sortSeq: z.number(),
|
||||
useYn: z.string(),
|
||||
});
|
||||
|
||||
function ModifyPage({ authorizationNo }: { authorizationNo: number }) {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { data: apiDetail } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.AUTHRTS, authorizationNo],
|
||||
queryFn: () => ApimanApi.getApi(authorizationNo),
|
||||
select: (data) => {
|
||||
const keys = Object.keys(apiFormSchema.shape);
|
||||
const filteredData = Object.fromEntries(
|
||||
Object.entries(data).filter(([key]) => keys.includes(key)),
|
||||
);
|
||||
return filteredData;
|
||||
},
|
||||
enabled: !!authorizationNo,
|
||||
});
|
||||
|
||||
const { mutateAsync: updateApi } = useMutation({
|
||||
mutationFn: (data: ApiFormData) => ApimanApi.updateApi(authorizationNo, data),
|
||||
onSuccess: () => {
|
||||
alert("수정되었습니다.", () => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.AUTHRTS],
|
||||
});
|
||||
router.back();
|
||||
});
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
const axiosError = error as AxiosError<{ message: string }>;
|
||||
if (axiosError.response?.data) {
|
||||
alert(axiosError.response.data.message);
|
||||
} else if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
} else {
|
||||
alert("알 수 없는 오류가 발생했습니다.");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteApi } = useMutation({
|
||||
mutationFn: () => ApimanApi.deleteApi(authorizationNo),
|
||||
onSuccess: () => {
|
||||
alert("삭제되었습니다.", router.back);
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
const axiosError = error as AxiosError<{ message: string }>;
|
||||
if (axiosError.response?.data) {
|
||||
alert(axiosError.response.data.message);
|
||||
} else if (error instanceof Error) {
|
||||
alert(error.message);
|
||||
} else {
|
||||
alert("알 수 없는 오류가 발생했습니다.");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<ApiFormData>({
|
||||
resolver: zodResolver(apiFormSchema),
|
||||
values: apiDetail as ApiFormData,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">API 수정</h3>
|
||||
</div>
|
||||
<ApimanForm
|
||||
form={form}
|
||||
authorizationNo={authorizationNo}
|
||||
handleSave={updateApi}
|
||||
handleDelete={deleteApi}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { ModifyPage };
|
||||
@@ -80,7 +80,7 @@ function GroupPage() {
|
||||
|
||||
useEffect(() => {
|
||||
refetch();
|
||||
}, [groupQuery.page, groupQuery.size]); // 페이지나 사이즈 변경 시 자동 조회
|
||||
}, [groupQuery, refetch]); //[groupQuery.page, groupQuery.size]); // 페이지나 사이즈 변경 시 자동 조회
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
|
||||
@@ -10,6 +10,8 @@ export const ENDPOINT = {
|
||||
LOGIN: "/user-service/login",
|
||||
USERS: "/user-service/api/v1/users",
|
||||
SILENT_REFRESH: "/user-service/api/v1/users/token/refresh",
|
||||
AUTHRTS: "/user-service/api/v1/authorizations",
|
||||
|
||||
},
|
||||
CMS_SERVICE: {
|
||||
SITES: "/cms-service/api/v1/sites",
|
||||
|
||||
Reference in New Issue
Block a user