auto commit

This commit is contained in:
bangdk 2024-11-06 16:01:13 +09:00
parent 19f82aca2b
commit 304318af4e
11 changed files with 181 additions and 136 deletions

View File

@ -126,22 +126,27 @@ const AccountsPage = () => {
// Table columns
const columns: ColumnDef<User>[] = [
{
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: "액션",

View File

@ -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<z.infer<typeof formSchema>>({
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 (
<div key={`${category}-${type}`} className="flex items-center space-x-2">
<Checkbox
id={`${category}-${type}`}
checked={isChecked}
defaultChecked={defaultChecked}
onCheckedChange={(checked) =>
handlePermissionChange(category, type, checked as boolean)
}
/>
<label
htmlFor={`${category}-${type}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{typeName}
</label>
</div>
);
};
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
@ -148,33 +186,8 @@ export const RoleForm = ({
<CardContent className="pt-4">
<div className="font-medium mb-2">{categoryName}</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{Object.entries(PERMISSION_TYPES).map(
([type, typeName]) => (
<div
key={`${category}-${type}`}
className="flex items-center space-x-2"
>
<Checkbox
id={`${category}-${type}`}
checked={form
.watch(`permissions.${category}`, [])
.includes(type)}
onCheckedChange={(checked) =>
handlePermissionChange(
category,
type,
checked as boolean
)
}
/>
<label
htmlFor={`${category}-${type}`}
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{typeName}
</label>
</div>
)
{Object.entries(PERMISSION_TYPES).map(([type, typeName]) =>
renderPermissionCheckbox(category, type, typeName)
)}
</div>
</CardContent>
@ -205,12 +218,21 @@ export const RoleForm = ({
/>
<div className="flex justify-end space-x-2">
<Button type="button" variant="outline" onClick={onCancel}>
<Button
type="button"
variant="outline"
onClick={onCancel}
className="w-24"
>
</Button>
<Button type="submit">{initialData ? "수정" : "생성"}</Button>
<Button type="submit" className="w-24">
{initialData ? "수정" : "생성"}
</Button>
</div>
</form>
</Form>
);
};
export default RoleForm;

View File

@ -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 = () => {
</div>
{/* 중첩된 Dialog를 제거하고 하나의 Dialog만 사용 */}
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent className="max-w-2xl">
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{editingRole ? "권한 그룹 수정" : "새 권한 그룹"}

View File

@ -191,7 +191,14 @@ export function EquipmentForm({
// 사양 정보 상태 추가
const [specifications, setSpecifications] = React.useState<
Record<string, string>
>(initialData?.specifications || defaultSpecifications);
>(
Object.fromEntries(
Object.entries(initialData?.specifications || {}).map(([k, v]) => [
k,
String(v),
])
) || defaultSpecifications
);
// 사양 정보 필드 추가/제거 핸들러 수정
const handleSpecificationChange = (key: string, value: string) => {

View File

@ -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: "최근 정비일",

View File

@ -60,6 +60,15 @@ const MaintenancePage = () => {
// Table columns
const columns: ColumnDef<MaintenanceLog>[] = [
{
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: "활성 상태",

View File

@ -144,10 +144,10 @@ const PartsPage = () => {
</Badge>
),
},
{
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={() => {

View File

@ -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<typeof formSchema>) => 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({
<SelectValue placeholder="부서를 선택하세요" />
</SelectTrigger>
<SelectContent>
{department?.map((dept: any) => (
{department?.map((dept: { id: string; name: string }) => (
<SelectItem key={dept.id} value={dept.name}>
{dept.name}
</SelectItem>

View File

@ -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 }) => (
<div className="flex flex-wrap gap-1">
{row.original.certifications?.map((cert: any, index: number) => (
{row.original.certifications?.map((cert: { name: string }, index: number) => (
<Badge key={index} variant="secondary">
{cert.name}
</Badge>
@ -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={() => {

View File

@ -8,6 +8,7 @@ export interface Equipment {
manufacturer: string;
type: "HVAC" | "Boiler" | "Compressor" | "Motor" | "Pump" | "Other";
specifications: Record<string, string | number | boolean>;
defaultSpecifications: Record<string, string | number | boolean>;
installationDate: string;
lastMaintenance: string | null;
isActive: boolean;

View File

@ -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[];
}