diff --git a/fems-app/src/app/(admin)/users/accounts/page.tsx b/fems-app/src/app/(admin)/users/accounts/page.tsx index 9423dd7..5780d4b 100644 --- a/fems-app/src/app/(admin)/users/accounts/page.tsx +++ b/fems-app/src/app/(admin)/users/accounts/page.tsx @@ -126,22 +126,27 @@ const AccountsPage = () => { // Table columns const columns: ColumnDef[] = [ - { - accessorKey: "username", - header: "아이디", - }, + // { + // accessorKey: "username", + // header: "아이디", + // }, { accessorKey: "name", header: "이름", }, { - accessorKey: "email", - header: "이메일", - }, - { - accessorKey: "phone", - header: "전화번호", + accessorKey: "Department.name", + header: "부서", + cell: ({ row }) => row.original.Department?.name || "-", }, + // { + // accessorKey: "email", + // header: "이메일", + // }, + // { + // accessorKey: "phone", + // header: "전화번호", + // }, { accessorKey: "role", header: "역할", @@ -164,6 +169,16 @@ const AccountsPage = () => { 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: "활성화", @@ -176,21 +191,6 @@ const AccountsPage = () => { /> ), }, - { - accessorKey: "Company.name", - header: "회사", - cell: ({ row }) => row.original.Company?.name || "-", - }, - { - accessorKey: "Branch.name", - header: "지점", - cell: ({ row }) => row.original.Branch?.name || "-", - }, - { - accessorKey: "Department.name", - header: "부서", - cell: ({ row }) => row.original.Department?.name || "-", - }, { id: "actions", header: "액션", diff --git a/fems-app/src/app/(admin)/users/roles/components/RoleForm.tsx b/fems-app/src/app/(admin)/users/roles/components/RoleForm.tsx index 9ac15cf..9126b00 100644 --- a/fems-app/src/app/(admin)/users/roles/components/RoleForm.tsx +++ b/fems-app/src/app/(admin)/users/roles/components/RoleForm.tsx @@ -1,5 +1,5 @@ // src/app/(admin)/users/roles/components/RoleForm.tsx -import React from "react"; +import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import * as z from "zod"; @@ -17,18 +17,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; - -interface Permission { - [key: string]: string[]; -} - -interface Role { - id: string; - name: string; - description: string; - permissions: Permission; - isActive: boolean; -} +import { Role } from "@/types/user"; const formSchema = z.object({ name: z @@ -66,38 +55,55 @@ export const RoleForm = ({ onSubmit, onCancel, }: RoleFormProps) => { + const [mounted, setMounted] = useState(false); + + // 초기값 설정을 별도 상수로 분리 + const defaultPermissions = { + company: [], + user: [], + energy: [], + equipment: [], + report: [], + }; + const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { name: initialData?.name || "", description: initialData?.description || "", - permissions: initialData?.permissions || { - company: [], - user: [], - energy: [], - equipment: [], - report: [], - }, + permissions: initialData?.permissions || defaultPermissions, isActive: initialData?.isActive ?? true, }, }); - // handlePermissionChange 함수 수정 + useEffect(() => { + setMounted(true); + + // initialData가 있을 경우, 폼 값을 초기화 + if (initialData) { + Object.entries(initialData.permissions).forEach( + ([category, permissions]) => { + form.setValue(`permissions.${category}`, permissions, { + shouldValidate: true, + }); + } + ); + } + }, [initialData, form]); + + // permissions 값을 watch로 관찰 + const permissions = form.watch("permissions"); + const handlePermissionChange = ( category: string, type: string, checked: boolean ) => { - const currentPermissions = form.getValues().permissions[category] || []; - let newPermissions; + const currentPermissions = permissions[category] || []; + const newPermissions = checked + ? [...currentPermissions, type] + : currentPermissions.filter((p) => p !== type); - if (checked) { - newPermissions = [...currentPermissions, type]; - } else { - newPermissions = currentPermissions.filter((p) => p !== type); - } - - // form.setValue 대신 setValue를 사용하고 shouldValidate를 true로 설정 form.setValue(`permissions.${category}`, newPermissions, { shouldValidate: true, shouldDirty: true, @@ -105,6 +111,38 @@ export const RoleForm = ({ }); }; + const renderPermissionCheckbox = ( + category: string, + type: string, + typeName: string + ) => { + if (!mounted && !initialData) { + return null; + } + + const isChecked = permissions[category]?.includes(type); + const defaultChecked = initialData?.permissions[category]?.includes(type); + + return ( +
+ + handlePermissionChange(category, type, checked as boolean) + } + /> + +
+ ); + }; + return (
@@ -148,33 +186,8 @@ export const RoleForm = ({
{categoryName}
- {Object.entries(PERMISSION_TYPES).map( - ([type, typeName]) => ( -
- - handlePermissionChange( - category, - type, - checked as boolean - ) - } - /> - -
- ) + {Object.entries(PERMISSION_TYPES).map(([type, typeName]) => + renderPermissionCheckbox(category, type, typeName) )}
@@ -205,12 +218,21 @@ export const RoleForm = ({ />
- - +
); }; + +export default RoleForm; diff --git a/fems-app/src/app/(admin)/users/roles/page.tsx b/fems-app/src/app/(admin)/users/roles/page.tsx index 8bb3963..db47488 100644 --- a/fems-app/src/app/(admin)/users/roles/page.tsx +++ b/fems-app/src/app/(admin)/users/roles/page.tsx @@ -19,18 +19,7 @@ import { RoleForm } from "./components/RoleForm"; import { api } from "@/lib/api"; import { AxiosError } from "axios"; import { useAuthStore } from "@/stores/auth"; - -interface Permission { - [key: string]: string[]; -} - -interface Role { - id: string; - name: string; - description: string; - permissions: Permission; - isActive: boolean; -} +import { Role } from "@/types/user"; const RolesPage = () => { const { token, user } = useAuthStore(); @@ -234,7 +223,7 @@ const RolesPage = () => { {/* 중첩된 Dialog를 제거하고 하나의 Dialog만 사용 */} - + {editingRole ? "권한 그룹 수정" : "새 권한 그룹"} diff --git a/fems-app/src/app/(equipment)/inventory/[mode]/components/EquipmentForm.tsx b/fems-app/src/app/(equipment)/inventory/[mode]/components/EquipmentForm.tsx index fad4fdf..fd79571 100644 --- a/fems-app/src/app/(equipment)/inventory/[mode]/components/EquipmentForm.tsx +++ b/fems-app/src/app/(equipment)/inventory/[mode]/components/EquipmentForm.tsx @@ -191,7 +191,14 @@ export function EquipmentForm({ // 사양 정보 상태 추가 const [specifications, setSpecifications] = React.useState< Record - >(initialData?.specifications || defaultSpecifications); + >( + Object.fromEntries( + Object.entries(initialData?.specifications || {}).map(([k, v]) => [ + k, + String(v), + ]) + ) || defaultSpecifications + ); // 사양 정보 필드 추가/제거 핸들러 수정 const handleSpecificationChange = (key: string, value: string) => { diff --git a/fems-app/src/app/(equipment)/inventory/page.tsx b/fems-app/src/app/(equipment)/inventory/page.tsx index 6bfb2e2..ac20d7e 100644 --- a/fems-app/src/app/(equipment)/inventory/page.tsx +++ b/fems-app/src/app/(equipment)/inventory/page.tsx @@ -100,10 +100,10 @@ const InventoryPage = () => { accessorKey: "model", header: "모델명", }, - { - accessorKey: "manufacturer", - header: "제조사", - }, + // { + // accessorKey: "manufacturer", + // header: "제조사", + // }, { accessorKey: "type", header: "유형", @@ -119,12 +119,12 @@ const InventoryPage = () => { return typeMap[row.original.type] || row.original.type; }, }, - { - accessorKey: "installationDate", - header: "설치일", - cell: ({ row }) => - new Date(row.original.installationDate).toLocaleDateString(), - }, + // { + // accessorKey: "installationDate", + // header: "설치일", + // cell: ({ row }) => + // new Date(row.original.installationDate).toLocaleDateString(), + // }, { accessorKey: "lastMaintenance", header: "최근 정비일", diff --git a/fems-app/src/app/(equipment)/maintenance/page.tsx b/fems-app/src/app/(equipment)/maintenance/page.tsx index db297a6..34637f5 100644 --- a/fems-app/src/app/(equipment)/maintenance/page.tsx +++ b/fems-app/src/app/(equipment)/maintenance/page.tsx @@ -60,6 +60,15 @@ const MaintenancePage = () => { // Table columns const columns: ColumnDef[] = [ + { + accessorKey: "equipment.name", + header: "설비명", + cell: ({ row }) => row.original.Equipment?.name || "-", + }, + { + accessorKey: "description", + header: "정비 내용", + }, { accessorKey: "type", header: "정비 유형", @@ -100,15 +109,6 @@ const MaintenancePage = () => { ? new Date(row.original.completionDate).toLocaleDateString() : "-", }, - { - accessorKey: "equipment.name", - header: "설비명", - cell: ({ row }) => row.original.Equipment?.name || "-", - }, - { - accessorKey: "description", - header: "정비 내용", - }, // { // accessorKey: "isActive", // header: "활성 상태", diff --git a/fems-app/src/app/(equipment)/parts/page.tsx b/fems-app/src/app/(equipment)/parts/page.tsx index abb324e..79a500d 100644 --- a/fems-app/src/app/(equipment)/parts/page.tsx +++ b/fems-app/src/app/(equipment)/parts/page.tsx @@ -144,10 +144,10 @@ const PartsPage = () => { ), }, - { - accessorKey: "manufacturer", - header: "제조사", - }, + // { + // accessorKey: "manufacturer", + // header: "제조사", + // }, { accessorKey: "stockQuantity", header: "재고수량", @@ -276,9 +276,16 @@ const PartsPage = () => { initialData={editingPart || undefined} onSubmit={(data) => { if (editingPart) { - updateMutation.mutate({ id: editingPart.id, ...data }); + updateMutation.mutate({ + id: editingPart.id, + ...data, + specifications: JSON.stringify(data.specifications || {}), + }); } else { - createMutation.mutate(data); + createMutation.mutate({ + ...data, + specifications: JSON.stringify(data.specifications || {}), + }); } }} onCancel={() => { diff --git a/fems-app/src/app/(equipment)/personnel/components/PersonnelForm.tsx b/fems-app/src/app/(equipment)/personnel/components/PersonnelForm.tsx index e1680e8..925b8e6 100644 --- a/fems-app/src/app/(equipment)/personnel/components/PersonnelForm.tsx +++ b/fems-app/src/app/(equipment)/personnel/components/PersonnelForm.tsx @@ -7,7 +7,7 @@ import { useForm, useFieldArray } from "react-hook-form"; import * as z from "zod"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { Award, Plus, X } from "lucide-react"; +import { Award, X } from "lucide-react"; import { Form, FormControl, @@ -64,7 +64,7 @@ const formSchema = z.object({ interface PersonnelFormProps { initialData?: Personnel; - onSubmit: (data: any) => void; + onSubmit: (data: z.infer) => void; onCancel: () => void; } @@ -113,11 +113,7 @@ export function PersonnelForm({ }); // useFieldArray 설정 - const { - fields: certFields, - append: appendCert, - remove: removeCert, - } = useFieldArray({ + useFieldArray({ control: form.control, name: "certifications", }); @@ -200,7 +196,7 @@ export function PersonnelForm({ - {department?.map((dept: any) => ( + {department?.map((dept: { id: string; name: string }) => ( {dept.name} diff --git a/fems-app/src/app/(equipment)/personnel/page.tsx b/fems-app/src/app/(equipment)/personnel/page.tsx index 5ea95a1..0925c48 100644 --- a/fems-app/src/app/(equipment)/personnel/page.tsx +++ b/fems-app/src/app/(equipment)/personnel/page.tsx @@ -16,7 +16,7 @@ import { DialogDescription, } from "@/components/ui/dialog"; import { useToast } from "@/hooks/use-toast"; -import { Plus, Edit, Trash2, Wrench, Search } from "lucide-react"; +import { Plus, Edit, Trash2, Search } from "lucide-react"; import { ColumnDef } from "@tanstack/react-table"; import { useAuthStore } from "@/stores/auth"; import { AxiosError } from "axios"; @@ -157,7 +157,7 @@ const PersonnelPage = () => { header: "자격증", cell: ({ row }) => (
- {row.original.certifications?.map((cert: any, index: number) => ( + {row.original.certifications?.map((cert: { name: string }, index: number) => ( {cert.name} @@ -308,9 +308,26 @@ const PersonnelPage = () => { initialData={editingPersonnel || undefined} onSubmit={(data) => { if (editingPersonnel) { - updateMutation.mutate({ id: editingPersonnel.id, ...data }); + updateMutation.mutate({ + id: editingPersonnel.id, + ...data, + certifications: data.certifications?.map(cert => ({ + name: cert.name, + date: cert.issueDate, + expiry: cert.expiryDate || "", + expiryDate: cert.expiryDate || "", + })), + }); } else { - createMutation.mutate(data); + createMutation.mutate({ + ...data, + certifications: data.certifications?.map(cert => ({ + name: cert.name, + date: cert.issueDate, + expiry: cert.expiryDate || "", + expiryDate: cert.expiryDate || "", + })), + }); } }} onCancel={() => { diff --git a/fems-app/src/types/equipment.ts b/fems-app/src/types/equipment.ts index 8435b15..9b4b8c7 100644 --- a/fems-app/src/types/equipment.ts +++ b/fems-app/src/types/equipment.ts @@ -8,6 +8,7 @@ export interface Equipment { manufacturer: string; type: "HVAC" | "Boiler" | "Compressor" | "Motor" | "Pump" | "Other"; specifications: Record; + defaultSpecifications: Record; installationDate: string; lastMaintenance: string | null; isActive: boolean; diff --git a/fems-app/src/types/user.ts b/fems-app/src/types/user.ts index 89b37d6..91886de 100644 --- a/fems-app/src/types/user.ts +++ b/fems-app/src/types/user.ts @@ -34,5 +34,11 @@ export interface User { export interface Role { id: string; name: string; - description?: string; + description: string; + permissions: Permission; + isActive: boolean; +} + +export interface Permission { + [key: string]: string[]; }