고객사 하는중중
This commit is contained in:
parent
7b90ebd0d1
commit
68101a1811
369
plm-app/src/app/(admin)/common/oemmng/components/UserForm.tsx
Normal file
369
plm-app/src/app/(admin)/common/oemmng/components/UserForm.tsx
Normal file
@ -0,0 +1,369 @@
|
||||
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>
|
||||
);
|
||||
};
|
332
plm-app/src/app/(admin)/common/oemmng/page.tsx
Normal file
332
plm-app/src/app/(admin)/common/oemmng/page.tsx
Normal file
@ -0,0 +1,332 @@
|
||||
// src/app/(admin)/users/accounts/page.tsx
|
||||
"use client";
|
||||
|
||||
import React, { useState, useCallback } from "react";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { DataTable } from "@/components/ui/data-table";
|
||||
import { api } from "@/lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from "@/components/ui/dialog";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
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 { Switch } from "@/components/ui/switch";
|
||||
import { User, PaginatedResponse } from "@/types/user";
|
||||
|
||||
const AccountsPage = () => {
|
||||
const { token, user } = useAuthStore();
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [editingUser, setEditingUser] = React.useState<User | null>(null);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Fetch users with pagination
|
||||
const { data, isLoading } = useQuery<PaginatedResponse>({
|
||||
queryKey: ["users", page, pageSize],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get("/api/v1/admin/users", {
|
||||
params: {
|
||||
page,
|
||||
limit: pageSize,
|
||||
},
|
||||
});
|
||||
return {
|
||||
...data,
|
||||
users: data.users.map((user: User) => ({
|
||||
...user,
|
||||
Roles: user.Roles || [],
|
||||
})),
|
||||
};
|
||||
},
|
||||
enabled: !!token,
|
||||
});
|
||||
|
||||
const handlePageSizeChange = useCallback((newPageSize: number) => {
|
||||
setPageSize(newPageSize);
|
||||
setPage(1);
|
||||
}, []);
|
||||
|
||||
// Create user mutation
|
||||
const createMutation = useMutation({
|
||||
mutationFn: async (newUser: Partial<User>) => {
|
||||
// Include companyId in the user data
|
||||
const userWithCompanyId = {
|
||||
...newUser,
|
||||
companyId: user?.companyId,
|
||||
};
|
||||
|
||||
const { data } = await api.post<User>(
|
||||
"/api/v1/admin/users",
|
||||
userWithCompanyId
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
setIsOpen(false);
|
||||
toast({
|
||||
title: "고객사 생성",
|
||||
description: "새로운 고객사가 생성되었습니다.",
|
||||
});
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
toast({
|
||||
title: "고객사 생성 실패",
|
||||
description: (error.response?.data as { message: string }).message,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Update user mutation
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: async (userData: Partial<User>) => {
|
||||
const { data } = await api.put<User>(
|
||||
`/api/v1/admin/users/${userData.id}`,
|
||||
userData
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
setIsOpen(false);
|
||||
setEditingUser(null);
|
||||
toast({
|
||||
title: "고객사 수정",
|
||||
description: "고객사 정보가 수정되었습니다.",
|
||||
});
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
toast({
|
||||
title: "고객사 수정 실패",
|
||||
description: (error.response?.data as { message: string }).message,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Delete user mutation
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
await api.delete(`/api/v1/admin/users/${id}`);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
toast({
|
||||
title: "고객사 삭제",
|
||||
description: "고객사가 삭제되었습니다.",
|
||||
});
|
||||
},
|
||||
onError: (error: AxiosError) => {
|
||||
toast({
|
||||
title: "고객사 삭제 실패",
|
||||
description: (error.response?.data as { message: string }).message,
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
// Table columns
|
||||
const columns: ColumnDef<User>[] = [
|
||||
{
|
||||
id: "index",
|
||||
header: "No",
|
||||
cell: ({ row }) => row.original.index,
|
||||
size: 60,
|
||||
},
|
||||
// {
|
||||
// accessorKey: "username",
|
||||
// header: "아이디",
|
||||
// },
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "업체명/고객사사",
|
||||
},
|
||||
{
|
||||
accessorKey: "Department.name",
|
||||
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: "활성화",
|
||||
cell: ({ row }) => (
|
||||
<Switch
|
||||
checked={row.original.isActive}
|
||||
onCheckedChange={(value) => {
|
||||
updateMutation.mutate({ id: row.original.id, isActive: value });
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "액션",
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setEditingUser(row.original);
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
if (confirm("정말 삭제하시겠습니까?")) {
|
||||
deleteMutation.mutate(row.original.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (isLoading) return <div>Loading...</div>;
|
||||
|
||||
return (
|
||||
<div className="container mx-auto py-6">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-3xl font-bold">고객사 관리</h1>
|
||||
<p className="text-muted-foreground">
|
||||
고객사를 관리하고 권한을 설정합니다.
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={() => setIsOpen(true)}>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
고객사 추가
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Users Table */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>고객사 목록</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{data?.users && data.users.length > 0 ? (
|
||||
<>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={data.users}
|
||||
pagination={{
|
||||
pageIndex: page - 1,
|
||||
pageSize,
|
||||
pageCount: data.totalPages,
|
||||
rowCount: data.total,
|
||||
onPageChange: (newPage) => setPage(newPage + 1),
|
||||
onPageSizeChange: handlePageSizeChange,
|
||||
}}
|
||||
/>
|
||||
<div className="text-sm text-muted-foreground mt-2">
|
||||
총 {data.total}명의 고객사
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
등록된 고객사가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* User Create/Edit Dialog */}
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
setIsOpen(open);
|
||||
if (!open) setEditingUser(null);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editingUser ? "고객사 수정" : "새 고객사"}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{editingUser
|
||||
? "기존 고객사 정보를 수정합니다."
|
||||
: "새로운 고객사를 생성합니다."}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<UserForm
|
||||
initialData={editingUser || undefined}
|
||||
onSubmit={(data) => {
|
||||
if (editingUser) {
|
||||
updateMutation.mutate({ id: editingUser.id, ...data });
|
||||
} else {
|
||||
createMutation.mutate(data);
|
||||
}
|
||||
}}
|
||||
onCancel={() => {
|
||||
setIsOpen(false);
|
||||
setEditingUser(null);
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountsPage;
|
16
plm-app/src/types/common/oemmng/oemmng.ts
Normal file
16
plm-app/src/types/common/oemmng/oemmng.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// src/types/menu.ts
|
||||
|
||||
export interface oemMng {
|
||||
id: string;
|
||||
oem_code?: string | null;
|
||||
oem_name?: string | null;
|
||||
writer?: string | null;
|
||||
regdate?: Date | null;
|
||||
status?: string | null;
|
||||
oem_no?: string | null;
|
||||
}
|
||||
|
||||
export interface oemMngResponse {
|
||||
success: boolean;
|
||||
data: oemMng[];
|
||||
}
|
Loading…
Reference in New Issue
Block a user