From d47da1827eb3995d0c3f1b7609c63092d52e716c Mon Sep 17 00:00:00 2001 From: chpark Date: Mon, 30 Dec 2024 18:28:26 +0900 Subject: [PATCH] =?UTF-8?q?=EC=A0=9C=ED=92=88=20=EB=AA=A8=EB=8D=B8=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plm-api/src/models/Product.js | 74 ++++ plm-api/src/models/page.tsx | 687 ---------------------------------- 2 files changed, 74 insertions(+), 687 deletions(-) create mode 100644 plm-api/src/models/Product.js delete mode 100644 plm-api/src/models/page.tsx diff --git a/plm-api/src/models/Product.js b/plm-api/src/models/Product.js new file mode 100644 index 0000000..5483492 --- /dev/null +++ b/plm-api/src/models/Product.js @@ -0,0 +1,74 @@ +// models/ProductGroup.js + +const { Model, DataTypes } = require("sequelize"); + +class Product extends Model { + static init(sequelize) { + super.init( + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + }, + // 제품군 ID + product_group_id: { + type: DataTypes.UUID, + comment: "제품군 ID", + }, + product_code: { + type: DataTypes.STRING(128), + allowNull: true, + comment: "제품코드", + }, + product_name: { + type: DataTypes.STRING(128), + allowNull: true, + comment: "제품코드", + }, + product_desc: { + type: DataTypes.STRING(128), + allowNull: true, + comment: "제품 설명", + }, + + writer: { + type: DataTypes.STRING(32), + allowNull: true, + comment: "작성자", + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + }, + // 새로 추가할 필드들 + companyId: { + type: DataTypes.UUID, + comment: "회사 ID", + }, + }, + { + sequelize, + modelName: 'Product', + tableName: 'product_mng1', + timestamps: true, + indexes: [ + { + fields: ['id'], + }, + ], + } + ); + return this; + } + + static associate(models) { + // product가 Company에 속함 + // product가 product_group에 속함 + this.belongsTo(models.Company, { foreignKey: "companyId" }); + this.belongsTo(models.ProductGroup, { foreignKey: "product_group_id" }); + } + +} + +module.exports = Product; \ No newline at end of file diff --git a/plm-api/src/models/page.tsx b/plm-api/src/models/page.tsx deleted file mode 100644 index 9fef407..0000000 --- a/plm-api/src/models/page.tsx +++ /dev/null @@ -1,687 +0,0 @@ -"use client"; - -import React, { useState, useEffect } from "react"; -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { useForm } from "react-hook-form"; -import { api } from "@/lib/api"; -import { Button } from "@/components/ui/button"; -import { Separator } from "@/components/ui/separator"; -import { useToast } from "@/hooks/use-toast"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { Switch } from "@/components/ui/switch"; -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogDescription, - DialogFooter, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, -} from "@/components/ui/card"; -import { - Loader2, - Plus, - ChevronDown, - ChevronRight, - Trash2, - AlertCircle -} from "lucide-react"; -import { cn } from "@/lib/utils"; -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@/components/ui/alert-dialog"; - -interface DBMenuItem { - id: string; - menu_type: string; - parent_id: string | null; - menu_name_kor: string; - menu_name_eng: string; - seq: string; - menu_url: string; - menu_desc?: string; - isActive: boolean; - children?: DBMenuItem[]; - order?: string; -} - -interface DBApiResponse { - success: boolean; - data: { - success: boolean; - data: DBMenuItem[]; - }; -} - -interface MenuFormDialogProps { - isOpen: boolean; - onClose: () => void; - menuData?: DBMenuItem; - onSave: (data: DBMenuItem) => void; - menus: DBMenuItem[]; -} - -interface FormData { - menuNameKor: string; - menuNameEng: string; - menuUrl: string; - menuSeq: string; - menuType: string; - isActive: boolean; - parentId: string; -} - -const getFlatMenuList = (menus: DBMenuItem[], depth = 0): { id: string; label: string; isChild: boolean }[] => { - return menus.flatMap((menu) => [ - { - id: menu.id, - label: `${' '.repeat(depth)}${menu.menu_name_kor}`, - isChild: depth > 0, // 깊이에 따라 자식 여부 판단 - }, - ...(menu.children ? getFlatMenuList(menu.children, depth + 1) : []), - ]); -}; -const MenuFormDialog = ({ isOpen, onClose, menuData, onSave, menus }: MenuFormDialogProps) => { - const [searchText, setSearchText] = useState(""); - const form = useForm({ - defaultValues: { - menuNameKor: "", - menuNameEng: "", - menuUrl: "", - menuSeq: "", - menuType: "0", - isActive: true, // 기본 활성화 상태를 true로 설정 - parentId: "root", - }, - }); - - useEffect(() => { - if (menuData) { - form.reset({ - menuNameKor: menuData.menu_name_kor || "", - menuNameEng: menuData.menu_name_eng || "", - menuUrl: menuData.menu_url || "", - menuSeq: menuData.seq || "", - menuType: menuData.menu_type || "0", - isActive: menuData.isActive || false, - parentId: menuData.parent_id || "root", - }); - } else { - form.reset({ - menuNameKor: "", - menuNameEng: "", - menuUrl: "", - menuSeq: "", - menuType: "0", - isActive: true, // 새로 추가할 때 기본 활성화 - parentId: "root", - }); - } - }, [menuData, form]); - - const onSubmit = (data: FormData) => { - onSave({ - id: menuData?.id || "", - menu_type: data.menuType, - parent_id: data.parentId === "root" ? null : data.parentId, - menu_name_kor: data.menuNameKor, - menu_name_eng: data.menuNameEng, - seq: data.menuSeq, - menu_url: data.menuUrl, - isActive: data.isActive, - children: menuData?.children || [], - }); - }; - - - return ( - - - - {menuData ? "메뉴 수정" : "메뉴 추가"} - - {menuData ? "기존 메뉴의 정보를 수정합니다." : "새로운 메뉴를 시스템에 추가합니다."} - - -
- - ( - - 상위메뉴 선택 - setSearchText(e.target.value)} - className="mb-2" - /> - - 기본메뉴 - - {getFlatMenuList(menus) - .filter((menu) => - menu.label.toLowerCase().includes(searchText.toLowerCase()) - ) - .map((menu, index, array) => ( - - - {menu.isChild ? `└ ${menu.label.trim()}` : menu.label} - - {array[index + 1]?.isChild === false && ( - - )} - - ))} - - - - )} - /> - - ( - - 메뉴 이름 (한글) - - - - - )} - /> - - ( - - 메뉴 이름 (영문) - - - - - )} - /> - - ( - - URL - - - - - )} - /> - - ( - - 순서 - - - - - )} - /> - - ( - - 타입 - - - )} - /> - - ( - -
- 활성화 - - - -
-
- )} - /> - - - - - - - - -
-
- ); -}; - -interface MenusTableProps { - menus: DBMenuItem[]; - toggleActive: (id: string, value: boolean) => void; - openSections: { [key: string]: boolean }; - toggleSection: (id: string) => void; - onMenuClick: (menu: DBMenuItem) => void; - onDelete: (id: string) => void; -} - -const MenusTable = ({ - menus, - toggleActive, - openSections, - toggleSection, - onMenuClick, - onDelete -}: MenusTableProps) => { - const renderMenuRow = (menu: DBMenuItem, level: number = 0) => ( - - { - e.stopPropagation(); - toggleSection(menu.id); - }} - > - -
- {menu.children && menu.children.length > 0 ? ( - - ) : ( - - )} - { - e.stopPropagation(); // 부모 클릭 이벤트 방지 - onMenuClick(menu); - }} - > - {menu.menu_name_kor} - -
-
- {menu.menu_name_eng} - {menu.menu_url} - {menu.seq} - - - {menu.menu_type === "0" ? "관리자 메뉴" : "사용자 메뉴"} - - - -
- toggleActive(menu.id, value)} - /> - -
-
-
- {menu.children && - menu.children.length > 0 && - openSections[menu.id] && - menu.children.map((child) => renderMenuRow(child, level + 1))} -
- ); - - return ( - - - 메뉴 목록 - - -
- - - - 메뉴 이름 - 영문 이름 - URL - 순서 - 타입 - 활성화/삭제 - - - - {menus.map((menu) => renderMenuRow(menu))} - -
- {menus.length === 0 && ( -
- 등록된 메뉴가 없습니다. -
- )} -
-
-
- ); -}; - -const addMenuOrder = (menus: DBMenuItem[], parentOrder: string = ''): DBMenuItem[] => { - return menus.map((menu, index) => { - const order = parentOrder ? `${parentOrder}-${index + 1}` : `${index + 1}`; - return { - ...menu, - order, - children: menu.children ? addMenuOrder(menu.children, order) : [], - }; - }); -}; - -const MenusPage = () => { - const [isOpen, setIsOpen] = useState(false); - const [selectedMenu, setSelectedMenu] = useState(null); - const [openSections, setOpenSections] = useState<{ [key: string]: boolean }>({}); - const [deleteAlertOpen, setDeleteAlertOpen] = useState(false); - const [menuToDelete, setMenuToDelete] = useState(null); - const queryClient = useQueryClient(); - const { toast } = useToast(); - - const toggleSection = (id: string) => { - setOpenSections((prev) => ({ - ...prev, - [id]: !prev[id], - })); - }; - - const { data: dbMenuData, isLoading } = useQuery({ - queryKey: ["admin-menus"], - queryFn: async () => { - const response = await api.get("/api/v1/app/common/admin/menu"); - return response.data; - }, - // 추가 옵션 - refetchOnWindowFocus: true, - refetchOnMount: true, - refetchOnReconnect: true, - }); - - const saveMenuMutation = useMutation({ - mutationFn: async (menuData: Omit) => { - const response = await api.post("/api/v1/app/common/createmenu", menuData); - return response.data; - }, - onSuccess: () => { - // 더 구체적인 refetch 처리 - queryClient.invalidateQueries({ queryKey: ["admin-menus"] }); - queryClient.refetchQueries({ queryKey: ["admin-menus"] }); - toast({ - title: "메뉴 저장 성공", - description: "메뉴가 저장되었습니다.", - }); - setIsOpen(false); - }, - onError: (error) => { - toast({ - title: "메뉴 저장 실패", - description: "메뉴 저장 중 오류가 발생했습니다. 다시 시도해주세요.", - variant: "destructive", - }); - console.error("Menu save error:", error); - }, - }); - - const toggleActiveMutation = useMutation({ - mutationFn: async ({ id, isActive }: { id: string; isActive: boolean }) => { - const response = await api.post("/api/v1/app/common/createmenu", { - id, - isActive, - }); - return response.data; - }, - onSuccess: () => { - // 더 구체적인 refetch 처리 - queryClient.invalidateQueries({ queryKey: ["admin-menus"] }); - queryClient.refetchQueries({ queryKey: ["admin-menus"] }); - }, - onError: (error) => { - toast({ - title: "상태 변경 실패", - description: "메뉴 상태 변경 중 오류가 발생했습니다.", - variant: "destructive", - }); - console.error("Toggle active error:", error); - }, - }); - - const deleteMenuMutation = useMutation({ - mutationFn: async (id: string) => { - const response = await api.delete(`/api/v1/app/common/menu/${id}`); - return response.data; - }, - onSuccess: () => { - // 더 구체적인 refetch 처리 - queryClient.invalidateQueries({ queryKey: ["admin-menus"] }); - queryClient.refetchQueries({ queryKey: ["admin-menus"] }); - toast({ - title: "메뉴 삭제 성공", - description: "메뉴가 삭제되었습니다.", - }); - setMenuToDelete(null); - }, - onError: (error) => { - toast({ - title: "메뉴 삭제 실패", - description: "메뉴 삭제 중 오류가 발생했습니다.", - variant: "destructive", - }); - console.error("Delete menu error:", error); - }, - }); - - const handleMenuSave = (menu: DBMenuItem) => { - const { children, order, ...saveData } = menu; - saveMenuMutation.mutate(saveData); - }; - - const handleMenuClick = (menu: DBMenuItem) => { - setSelectedMenu(menu); - setIsOpen(true); - }; - - const handleAddClick = () => { - setSelectedMenu(null); - setIsOpen(true); - }; - - const handleDeleteClick = (id: string) => { - setMenuToDelete(id); - setDeleteAlertOpen(true); - }; - - const handleDeleteConfirm = () => { - if (menuToDelete) { - deleteMenuMutation.mutate(menuToDelete); - } - setDeleteAlertOpen(false); - }; - - const toggleActive = (id: string, value: boolean) => { - toggleActiveMutation.mutate({ id, isActive: value }); - }; - - if (isLoading) return
Loading...
; - - const orderedMenus = addMenuOrder(dbMenuData?.data?.data || []); - - return ( -
-
-
-

메뉴 관리

-

- 시스템의 메뉴 구조를 관리하고 설정합니다. -

-
- -
- - - - setIsOpen(false)} - menuData={selectedMenu || undefined} - onSave={handleMenuSave} - menus={orderedMenus} - /> - - - - - 메뉴 삭제 - - 정말로 이 메뉴를 삭제하시겠습니까? 이 작업은 되돌릴 수 없습니다. - - - - 취소 - - 삭제 - - - - -
- ); -}; - -export default MenusPage; \ No newline at end of file