거지같아아
This commit is contained in:
parent
68101a1811
commit
241c1f94e4
190
plm-api/src/services/oemmng.service.js
Normal file
190
plm-api/src/services/oemmng.service.js
Normal file
@ -0,0 +1,190 @@
|
||||
// src/services/oemmng.service.js
|
||||
const {
|
||||
User,
|
||||
Company,
|
||||
Branch,
|
||||
Department,
|
||||
UserRole,
|
||||
Role,
|
||||
} = require("../models");
|
||||
const { Op } = require("sequelize");
|
||||
const alertService = require("./alert.service");
|
||||
|
||||
class OemMngService {
|
||||
async findAll(currentUser, page = 1, limit = 10) {
|
||||
try {
|
||||
// Initialize the where clause
|
||||
let where = {
|
||||
role: { [Op.ne]: "super_admin" }, // Exclude super_admin users
|
||||
};
|
||||
|
||||
// company_admin은 자신의 회사 유저만 조회 가능
|
||||
if (currentUser.role === "company_admin") {
|
||||
where.companyId = currentUser.companyId;
|
||||
}
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 전체 개수 조회
|
||||
const count = await User.count({ where });
|
||||
|
||||
const users = await User.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: Company,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Branch,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Department,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Role,
|
||||
through: { attributes: [] },
|
||||
attributes: ["id", "name", "description"],
|
||||
},
|
||||
],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset,
|
||||
limit,
|
||||
distinct: true,
|
||||
});
|
||||
|
||||
// 인덱스 추가
|
||||
const usersWithIndex = users.map((user, index) => {
|
||||
const userJson = user.toJSON();
|
||||
userJson.index = offset + index + 1;
|
||||
return userJson;
|
||||
});
|
||||
|
||||
return {
|
||||
total: count,
|
||||
totalPages: Math.ceil(count / limit),
|
||||
currentPage: page,
|
||||
users: usersWithIndex,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error in findAll:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async findById(id) {
|
||||
return await User.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: Company,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Branch,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Department,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async createUser(userData, currentUser) {
|
||||
const { roleId, ...userFields } = userData;
|
||||
|
||||
const user = await User.create(userFields);
|
||||
|
||||
if (roleId) {
|
||||
const role = await Role.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
companyId: user.companyId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!role) {
|
||||
throw new Error("Role not found or does not belong to the company");
|
||||
}
|
||||
|
||||
await UserRole.create({
|
||||
userId: user.id,
|
||||
roleId: role.id,
|
||||
});
|
||||
}
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${user.name}이(가) ${currentUser.name}에 의해 생성되었습니다.`,
|
||||
companyId: user.companyId,
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async updateUser(id, updateData, currentUser) {
|
||||
const { roleId, ...userFields } = updateData;
|
||||
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("User not found");
|
||||
|
||||
// company_admin은 특정 필드를 수정할 수 없음 (예: role을 super_admin으로 변경 불가)
|
||||
if (currentUser.role === "company_admin") {
|
||||
if (updateData.role && updateData.role === "super_admin") {
|
||||
throw new Error("super_admin 역할을 부여할 수 없습니다");
|
||||
}
|
||||
updateData.companyId = currentUser.companyId;
|
||||
}
|
||||
|
||||
const updatedUser = await user.update(userFields);
|
||||
|
||||
if (roleId) {
|
||||
const role = await Role.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
companyId: user.companyId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!role) {
|
||||
throw new Error("Role not found or does not belong to the company");
|
||||
}
|
||||
|
||||
await UserRole.upsert({
|
||||
userId: user.id,
|
||||
roleId: role.id,
|
||||
});
|
||||
}
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${user.name}이(가) ${currentUser.name}에 의해 수정되었습니다.`,
|
||||
companyId: user.companyId,
|
||||
});
|
||||
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
async deleteUser(id, currentUser) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("User not found");
|
||||
|
||||
const userName = user.name;
|
||||
const companyId = user.companyId;
|
||||
|
||||
await user.destroy();
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${userName}이(가) ${currentUser.name}에 의해 삭제되었습니다.`,
|
||||
companyId: companyId,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new UserService();
|
166
plm-app/src/app/(admin)/common/oemmng/components/OemMngForm.tsx
Normal file
166
plm-app/src/app/(admin)/common/oemmng/components/OemMngForm.tsx
Normal file
@ -0,0 +1,166 @@
|
||||
import React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { api } from "@/lib/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { oemMng, Role } from "@/types/common/oemmng/oemmng";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { AxiosError } from "axios";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
|
||||
import { createValidationSchema } from "@/lib/utils"; // createValidationSchema 함수 임포트
|
||||
|
||||
const fields = [
|
||||
{ name: 'oem_code', required: true, type: 'string' },
|
||||
{ name: 'oem_name', required: true, type: 'string' },
|
||||
{ name: 'oem_no', required: true, type: 'string' },
|
||||
{ name: 'isActive', required: false, type: 'boolean' },
|
||||
];
|
||||
|
||||
const formSchema = createValidationSchema(fields);
|
||||
|
||||
type FormSchema = z.infer<typeof formSchema>;
|
||||
|
||||
interface OemFormProps {
|
||||
initialData?: Partial<oemMng>;
|
||||
onSubmit: (data: Partial<User>) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const OemMngForm: React.FC<OemFormProps> = ({
|
||||
initialData,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { token, user } = useAuthStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
// Initialize the form with react-hook-form and zod resolver
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
oem_code: initialData?.oem_code || "",
|
||||
oem_name: initialData?.oem_name || "",
|
||||
oem_no: initialData?.oem_no || "",
|
||||
isActive: initialData?.isActive || false,
|
||||
},
|
||||
});
|
||||
|
||||
// Reset form when initialData changes
|
||||
React.useEffect(() => {
|
||||
if (initialData) {
|
||||
form.reset({
|
||||
oem_code: initialData.oem_code || "",
|
||||
oem_name: initialData.oem_name || "",
|
||||
oem_no: initialData.oem_no || "",
|
||||
isActive: initialData.isActive || false,
|
||||
});
|
||||
}
|
||||
}, [initialData, form]);
|
||||
|
||||
const handleSubmit = async (data: FormSchema) => {
|
||||
try {
|
||||
await onSubmit(data);
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
toast({
|
||||
title: "에러",
|
||||
description:
|
||||
(err.response?.data as { message: string })?.message ||
|
||||
"에러가 발생했습니다.",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="oem_code"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>OEM_CODE</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="oem_name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>OEM_NAME</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="oem_no"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>고객사번호</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isActive"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>활성화 여부</FormLabel>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end space-x-2">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
취소
|
||||
</Button>
|
||||
<Button type="submit">저장</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
@ -1,369 +0,0 @@
|
||||
import React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import * as z from "zod";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
// import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { api } from "@/lib/api";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { User, Role } from "@/types/user";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { AxiosError } from "axios";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
|
||||
// Define the form schema with Zod
|
||||
const formSchema = z.object({
|
||||
username: z.string().min(3, "아이디는 3자 이상이어야 합니다"),
|
||||
password: z
|
||||
.string()
|
||||
.optional()
|
||||
.refine((val) => !val || val.length >= 8, {
|
||||
message: "비밀번호는 8자 이상이어야 합니다",
|
||||
}),
|
||||
name: z.string().min(2, "이름은 2자 이상이어야 합니다"),
|
||||
email: z.string().email("올바른 이메일 형식이 아닙니다"),
|
||||
phone: z.string().regex(/^[0-9-]+$/, "올바른 전화번호 형식이 아닙니다"),
|
||||
role: z.enum(["company_admin", "branch_admin", "user"], {
|
||||
required_error: "역할을 선택해주세요",
|
||||
}),
|
||||
roleId: z.string().uuid("올바른 권한 그룹을 선택해주세요"),
|
||||
isActive: z.boolean(),
|
||||
branchId: z.string().uuid("올바른 지점을 선택해주세요"),
|
||||
departmentId: z.string().uuid("올바른 부서를 선택해주세요"),
|
||||
});
|
||||
|
||||
type FormSchema = z.infer<typeof formSchema>;
|
||||
|
||||
interface UserFormProps {
|
||||
initialData?: Partial<User>;
|
||||
onSubmit: (data: Partial<User>) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export const UserForm: React.FC<UserFormProps> = ({
|
||||
initialData,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { token, user } = useAuthStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
// Initialize the form with react-hook-form and zod resolver
|
||||
const form = useForm<FormSchema>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
username: initialData?.username || "",
|
||||
password: initialData?.password || "",
|
||||
name: initialData?.name || "",
|
||||
email: initialData?.email || "",
|
||||
phone: initialData?.phone || "",
|
||||
role:
|
||||
(initialData?.role as "company_admin" | "branch_admin" | "user") ||
|
||||
"user",
|
||||
roleId: initialData?.Roles?.[0]?.id || "",
|
||||
isActive: initialData?.isActive || false,
|
||||
branchId: initialData?.branchId || "",
|
||||
departmentId: initialData?.departmentId || "",
|
||||
},
|
||||
});
|
||||
|
||||
// Reset form when initialData changes
|
||||
React.useEffect(() => {
|
||||
if (initialData) {
|
||||
form.reset({
|
||||
username: initialData.username || "",
|
||||
password: initialData.password || "",
|
||||
name: initialData.name || "",
|
||||
email: initialData.email || "",
|
||||
phone: initialData.phone || "",
|
||||
role:
|
||||
(initialData.role as "company_admin" | "branch_admin" | "user") ||
|
||||
"user",
|
||||
roleId: initialData.Roles?.[0]?.id || "",
|
||||
isActive: initialData.isActive || false,
|
||||
branchId: initialData.branchId || "",
|
||||
departmentId: initialData.departmentId || "",
|
||||
});
|
||||
}
|
||||
}, [initialData, form]);
|
||||
|
||||
// Fetch available roles
|
||||
const userRoles = [
|
||||
{ value: "company_admin", label: "기업 관리자" },
|
||||
{ value: "branch_admin", label: "지점 관리자" },
|
||||
{ value: "user", label: "일반 유저" },
|
||||
];
|
||||
|
||||
// Fetch branches
|
||||
const { data: branches } = useQuery({
|
||||
queryKey: ["branches"],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<{ id: string; name: string }[]>(
|
||||
"/api/v1/admin/branches"
|
||||
);
|
||||
return data;
|
||||
},
|
||||
enabled: !!token,
|
||||
});
|
||||
|
||||
// Fetch departments
|
||||
const { data: departments } = useQuery({
|
||||
queryKey: ["departments", user?.companyId],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<{ id: string; name: string }[]>(
|
||||
`/api/v1/admin/departments/${user?.companyId}`
|
||||
);
|
||||
return data;
|
||||
},
|
||||
enabled: !!token && !!user?.companyId,
|
||||
});
|
||||
|
||||
// Fetch roles
|
||||
const { data: roles } = useQuery<Role[]>({
|
||||
queryKey: ["roles", user?.companyId],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get<Role[]>(
|
||||
`/api/v1/admin/roles/${user?.companyId}`
|
||||
);
|
||||
return data;
|
||||
},
|
||||
enabled: !!token && !!user?.companyId,
|
||||
});
|
||||
|
||||
const handleSubmit = async (data: FormSchema) => {
|
||||
try {
|
||||
await onSubmit(data);
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
toast({
|
||||
title: "에러",
|
||||
description:
|
||||
(err.response?.data as { message: string })?.message ||
|
||||
"에러가 발생했습니다.",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="username"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>아이디</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{!initialData && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="password"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>비밀번호</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="password" {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>이름</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="email"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>이메일</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="email" {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="phone"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>전화번호</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} value={field.value || ""} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="role"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>역할</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
value={field.value || "user"}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="역할을 선택하세요" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{userRoles.map((role) => (
|
||||
<SelectItem key={role.value} value={role.value}>
|
||||
{role.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="roleId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>권한 그룹</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="권한 그룹을 선택하세요" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{roles?.map((role) => (
|
||||
<SelectItem key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="isActive"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex items-center space-x-2">
|
||||
<FormControl>
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>활성화 여부</FormLabel>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="branchId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>지점</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="지점을 선택하세요" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{branches?.map((branch) => (
|
||||
<SelectItem key={branch.id} value={branch.id}>
|
||||
{branch.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="departmentId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>부서</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="부서를 선택하세요" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{departments?.map((dept) => (
|
||||
<SelectItem key={dept.id} value={dept.id}>
|
||||
{dept.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end space-x-2">
|
||||
<Button type="button" variant="outline" onClick={onCancel}>
|
||||
취소
|
||||
</Button>
|
||||
<Button type="submit">저장</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
@ -19,9 +19,9 @@ import { Plus, Edit, Trash2 } from "lucide-react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import { useAuthStore } from "@/stores/auth";
|
||||
import { AxiosError } from "axios";
|
||||
import { UserForm } from "./components/UserForm";
|
||||
import { OemMngForm } from "./components/OemMngForm";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { User, PaginatedResponse } from "@/types/user";
|
||||
import { oemMng, PaginatedResponse } from "@/types/common/oemmng/oemmng";
|
||||
|
||||
const AccountsPage = () => {
|
||||
const { token, user } = useAuthStore();
|
||||
@ -159,46 +159,6 @@ const AccountsPage = () => {
|
||||
header: "업체/고객사코드드",
|
||||
cell: ({ row }) => row.original.Department?.name || "-",
|
||||
},
|
||||
{
|
||||
accessorKey: "email",
|
||||
header: "이메일",
|
||||
},
|
||||
// {
|
||||
// accessorKey: "phone",
|
||||
// header: "전화번호",
|
||||
// },
|
||||
{
|
||||
accessorKey: "role",
|
||||
header: "역할",
|
||||
cell: ({ row }) => {
|
||||
const role = row.original.role;
|
||||
const roleMap: Record<string, string> = {
|
||||
super_admin: "슈퍼 관리자",
|
||||
company_admin: "기업 관리자",
|
||||
branch_admin: "지점 관리자",
|
||||
user: "일반 고객사",
|
||||
};
|
||||
return roleMap[role] || role;
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "roles",
|
||||
header: "권한 그룹",
|
||||
cell: ({ row }) => {
|
||||
const roles = row.original.Roles;
|
||||
return (roles ?? []).map((role) => role.name).join(", ");
|
||||
},
|
||||
},
|
||||
// {
|
||||
// accessorKey: "Company.name",
|
||||
// header: "회사",
|
||||
// cell: ({ row }) => row.original.Company?.name || "-",
|
||||
// },
|
||||
{
|
||||
accessorKey: "Branch.name",
|
||||
header: "지점",
|
||||
cell: ({ row }) => row.original.Branch?.name || "-",
|
||||
},
|
||||
{
|
||||
accessorKey: "isActive",
|
||||
header: "활성화",
|
||||
@ -309,7 +269,7 @@ const AccountsPage = () => {
|
||||
: "새로운 고객사를 생성합니다."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<UserForm
|
||||
<OemMngForm
|
||||
initialData={editingUser || undefined}
|
||||
onSubmit={(data) => {
|
||||
if (editingUser) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { z, ZodTypeAny } from 'zod';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
@ -8,3 +9,37 @@ export function cn(...inputs: ClassValue[]) {
|
||||
export function formatNumber(num: number): string {
|
||||
return new Intl.NumberFormat("ko-KR").format(num);
|
||||
}
|
||||
|
||||
interface Field {
|
||||
name: string;
|
||||
required: boolean;
|
||||
type: 'string' | 'number' | 'email' | 'phone' | 'boolean';
|
||||
}
|
||||
|
||||
export const createValidationSchema = (fields: Field[]) => {
|
||||
const schema: Record<string, ZodTypeAny> = {};
|
||||
|
||||
fields.forEach((field) => {
|
||||
switch (field.type) {
|
||||
case 'string':
|
||||
schema[field.name] = z.string().min(field.required ? 1 : 0, `${field.name}은 필수입니다.`);
|
||||
break;
|
||||
case 'number':
|
||||
schema[field.name] = z.number().min(field.required ? 1 : 0, `${field.name}은 필수입니다.`);
|
||||
break;
|
||||
case 'email':
|
||||
schema[field.name] = z.string().email(`${field.name}은 유효한 이메일 형식이어야 합니다.`);
|
||||
break;
|
||||
case 'phone':
|
||||
schema[field.name] = z.string().regex(/^[0-9-]+$/, `${field.name}은 올바른 전화번호 형식이어야 합니다.`);
|
||||
break;
|
||||
case 'boolean':
|
||||
schema[field.name] = z.boolean();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported field type: ${field.type}`);
|
||||
}
|
||||
});
|
||||
|
||||
return z.object(schema);
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
// src/types/menu.ts
|
||||
|
||||
|
||||
export interface oemMng {
|
||||
id: string;
|
||||
@ -14,3 +14,10 @@ export interface oemMngResponse {
|
||||
success: boolean;
|
||||
data: oemMng[];
|
||||
}
|
||||
|
||||
export interface PaginatedResponse {
|
||||
total: number;
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
oemMngs: oemMng[];
|
||||
}
|
Loading…
Reference in New Issue
Block a user