feat: 권한등록 작업

This commit is contained in:
2025-06-09 13:16:44 +09:00
parent 026ad44efa
commit 116a022a20
8 changed files with 277 additions and 8 deletions
@@ -0,0 +1,2 @@
import { CreatePage } from "@/pages/role";
export default CreatePage;
+14 -1
View File
@@ -1,9 +1,10 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
RoleService,
RoleList,
RoleParams,
RoleService,
RoleFormData,
} from "../model/role-interface";
export class RoleApiService implements RoleService {
@@ -13,6 +14,18 @@ export class RoleApiService implements RoleService {
});
return response.data;
}
public async createRole(data: RoleFormData): Promise<void> {
await client.post(ENDPOINT.USER_SERVICE.ROLES, data);
}
public async checkDuplicateRoleId(roleId: string): Promise<boolean> {
const response = await client.get(
`${ENDPOINT.USER_SERVICE.ROLES}/check/dup`,
{ params: { roleId } },
);
return response.data as boolean;
}
}
export const RoleApi = new RoleApiService();
+1
View File
@@ -1 +1,2 @@
export { RolePage } from "./ui/role-page";
export { CreatePage } from "./ui/create-page";
@@ -1,3 +1,4 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export interface RoleParams extends PageableParams {
@@ -15,8 +16,19 @@ export interface Role {
mngrYn: string;
}
export const roleFormSchema = z.object({
roleId: z.string(),
roleName: z.string(),
roleContent: z.string(),
useYn: z.string(),
mngrYn: z.string(),
});
export type RoleList = PageableList<Role>;
export type RoleFormData = z.infer<typeof roleFormSchema>;
export interface RoleService {
getRoles: (params: RoleParams) => Promise<RoleList>;
createRole: (data: RoleFormData) => Promise<void>;
checkDuplicateRoleId: (roleId: string) => Promise<boolean>;
}
+41
View File
@@ -0,0 +1,41 @@
"use client";
import { useRouter } from "next/navigation";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation } from "@tanstack/react-query";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import { RoleForm } from "./role-form";
import { RoleApi } from "../api/role-service";
import { roleFormSchema, type RoleFormData } from "../model/role-interface";
function CreatePage() {
const router = useRouter();
const alert = useAlertStore((state) => state.setMessage);
const { mutateAsync: createRole } = useMutation({
mutationFn: (data: RoleFormData) => RoleApi.createRole(data),
onSuccess: () => {
alert("등록되었습니다.", router.back);
},
});
const form = useForm<RoleFormData>({
resolver: zodResolver(roleFormSchema),
defaultValues: {
mngrYn: "Y",
useYn: "Y",
},
});
return (
<div className="card card-border !mt-6">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">권한 등록</h3>
</div>
<RoleForm form={form} handleSave={createRole} />
</div>
);
}
export { CreatePage };
+200
View File
@@ -0,0 +1,200 @@
import type { UseFormReturn } from "react-hook-form";
import {
Button,
Input,
Label,
RadioGroup,
RadioGroupItem,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from "@common/components";
import { FormWrapper } from "@/widgets/form-wrapper";
import { FormField, FormItem, FormControl } from "@/shared/ui/form";
import { RoleApi } from "../api/role-service";
import type { RoleFormData } from "../model/role-interface";
import { useMutation } from "@tanstack/react-query";
import { useAlertStore } from "@/shared/lib";
import { useState } from "react";
type RoleFormProps = {
roleId?: string;
form: UseFormReturn<RoleFormData>;
handleSave: (data: RoleFormData) => Promise<void>;
};
function RoleForm({ form, roleId, ...props }: RoleFormProps) {
const alert = useAlertStore((state) => state.setMessage);
const [isValidRoleId, setIsValidRoleId] = useState(false);
const { mutateAsync: checkDuplicateRoleId } = useMutation({
mutationFn: (roleId: string) => RoleApi.checkDuplicateRoleId(roleId),
onSuccess: (duplicate) => {
if (!duplicate) {
alert("사용가능한 권한ID입니다.");
setIsValidRoleId(true);
return;
}
alert("중복된 권한ID입니다.");
},
});
return (
<FormWrapper form={form} isModifyMode={!!roleId} {...props}>
<div className="card">
<Table variant="secondary">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>권한ID</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="roleId"
render={({ field }) => (
<div className="flex items-center gap-2">
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
onChange={(e) => {
field.onChange(e);
setIsValidRoleId(false);
}}
className="w-[20rem] !text-[1.3rem]"
placeholder="권한ID를 입력하세요"
/>
</FormControl>
</FormItem>
<Button
color="green"
className="px-6"
onClick={(e) => {
e.preventDefault();
checkDuplicateRoleId(field.value);
}}
disabled={!field.value || isValidRoleId}
>
{isValidRoleId && <i className="diveicon di-check" />}
중복확인
</Button>
</div>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>권한명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="roleName"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="권한명을 입력하세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>권한내용</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="roleContent"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="권한내용을 입력하세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>관리권한여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="mngrYn"
onValueChange={field.onChange}
>
<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>
<TableRow>
<TableHead>사용여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="useYn"
onValueChange={field.onChange}
>
<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>
</FormWrapper>
);
}
export { RoleForm };
+6 -1
View File
@@ -1,6 +1,7 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
@@ -14,6 +15,8 @@ import { columns } from "../model/table-columns";
import type { Role, RoleParams } from "../model/role-interface";
function RolePage() {
const router = useRouter();
const [roleQuery, setRoleQuery] = useState<RoleParams>({
keyword: "",
});
@@ -76,7 +79,9 @@ function RolePage() {
<div className="flex w-full items-center justify-between">
<h3 className="text-2xl font-medium text-label">권한 목록</h3>
<div className="flex gap-2">
<Button onClick={() => {}}>등록</Button>
<Button onClick={() => router.push("/role/register")}>
등록
</Button>
</div>
</div>
</div>
@@ -43,12 +43,7 @@ function FormWrapper<T extends FieldValues>({
<form onSubmit={form.handleSubmit(onSubmit)}>
{children}
<div className="flex justify-center gap-4 pb-12 pt-10">
<Button
size="lg"
color="white"
type="button"
onClick={() => router.back()}
>
<Button size="lg" color="white" type="button" onClick={router.back}>
목록
</Button>
<Button size="lg" type="submit">