From 9749234c30cf56c7a3134c034fc0eac903fee616 Mon Sep 17 00:00:00 2001 From: kjs Date: Thu, 2 Apr 2026 17:22:21 +0900 Subject: [PATCH] Refactor password reset logic and update UI components for better user experience - Removed the minimum password length validation from the backend, simplifying the password reset process. - Updated the password encryption method to utilize `EncryptUtil`, enhancing security and maintainability. - Adjusted the `UserPasswordResetModal` component to reset alert states upon closing, improving user feedback. - Modified the z-index values in the `Popover` and `Select` components for better layering and visibility in the UI. These changes streamline the password reset functionality and enhance the overall user interface, ensuring a more intuitive experience for administrators. --- .../src/controllers/adminController.ts | 24 +--------- .../admin/UserPasswordResetModal.tsx | 1 + .../components/layout/AdminPageRenderer.tsx | 48 +++++++++++++++++++ frontend/components/ui/popover.tsx | 2 +- frontend/components/ui/select.tsx | 2 +- frontend/package.json | 4 +- 6 files changed, 55 insertions(+), 26 deletions(-) diff --git a/backend-node/src/controllers/adminController.ts b/backend-node/src/controllers/adminController.ts index 1501a2c0..7af6b403 100644 --- a/backend-node/src/controllers/adminController.ts +++ b/backend-node/src/controllers/adminController.ts @@ -3728,17 +3728,6 @@ export const resetUserPassword = async ( return; } - // 비밀번호 길이 검증 (최소 4자) - if (newPassword.length < 4) { - res.status(400).json({ - success: false, - result: false, - message: "비밀번호는 최소 4자 이상이어야 합니다.", - msg: "비밀번호는 최소 4자 이상이어야 합니다.", - }); - return; - } - try { // 1. Raw Query로 사용자 존재 여부 확인 const currentUser = await queryOne( @@ -3756,19 +3745,10 @@ export const resetUserPassword = async ( return; } - // 2. 비밀번호 암호화 (기존 Java 로직과 동일) + // 2. 비밀번호 암호화 (EncryptUtil 사용) let encryptedPassword: string; try { - // EncryptUtil과 동일한 암호화 사용 - const crypto = require("crypto"); - const keyName = "ILJIAESSECRETKEY"; - const algorithm = "aes-128-ecb"; - - // AES-128-ECB 암호화 - const cipher = crypto.createCipher(algorithm, keyName); - let encrypted = cipher.update(newPassword, "utf8", "hex"); - encrypted += cipher.final("hex"); - encryptedPassword = encrypted.toUpperCase(); + encryptedPassword = EncryptUtil.encrypt(newPassword); } catch (encryptError) { logger.error("비밀번호 암호화 중 오류 발생", { error: encryptError, diff --git a/frontend/components/admin/UserPasswordResetModal.tsx b/frontend/components/admin/UserPasswordResetModal.tsx index 72bd53d1..9dc1967b 100644 --- a/frontend/components/admin/UserPasswordResetModal.tsx +++ b/frontend/components/admin/UserPasswordResetModal.tsx @@ -114,6 +114,7 @@ export function UserPasswordResetModal({ isOpen, onClose, userId, userName, onSu setShowPassword(false); setShowConfirmPassword(false); setIsLoading(false); + setAlertState({ isOpen: false, type: "info", title: "", message: "" }); onClose(); }, [onClose]); diff --git a/frontend/components/layout/AdminPageRenderer.tsx b/frontend/components/layout/AdminPageRenderer.tsx index 2284622a..7b6b8dcd 100644 --- a/frontend/components/layout/AdminPageRenderer.tsx +++ b/frontend/components/layout/AdminPageRenderer.tsx @@ -117,6 +117,30 @@ const ADMIN_PAGE_REGISTRY: Record> = { "/COMPANY_7/design/my-work": dynamic(() => import("@/app/(main)/COMPANY_7/design/my-work/page"), { ssr: false, loading: LoadingFallback }), "/COMPANY_7/design/design-request": dynamic(() => import("@/app/(main)/COMPANY_7/design/design-request/page"), { ssr: false, loading: LoadingFallback }), "/COMPANY_7/design/task-management": dynamic(() => import("@/app/(main)/COMPANY_7/design/task-management/page"), { ssr: false, loading: LoadingFallback }), + // === COMPANY_10 (큐앤씨) === + "/COMPANY_10/master-data/item-info": dynamic(() => import("@/app/(main)/COMPANY_10/master-data/item-info/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/master-data/department": dynamic(() => import("@/app/(main)/COMPANY_10/master-data/department/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/order": dynamic(() => import("@/app/(main)/COMPANY_10/sales/order/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/customer": dynamic(() => import("@/app/(main)/COMPANY_10/sales/customer/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/sales-item": dynamic(() => import("@/app/(main)/COMPANY_10/sales/sales-item/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/shipping-order": dynamic(() => import("@/app/(main)/COMPANY_10/sales/shipping-order/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/shipping-plan": dynamic(() => import("@/app/(main)/COMPANY_10/sales/shipping-plan/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/sales/claim": dynamic(() => import("@/app/(main)/COMPANY_10/sales/claim/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/production/process-info": dynamic(() => import("@/app/(main)/COMPANY_10/production/process-info/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/production/work-instruction": dynamic(() => import("@/app/(main)/COMPANY_10/production/work-instruction/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/production/plan-management": dynamic(() => import("@/app/(main)/COMPANY_10/production/plan-management/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/equipment/info": dynamic(() => import("@/app/(main)/COMPANY_10/equipment/info/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/logistics/material-status": dynamic(() => import("@/app/(main)/COMPANY_10/logistics/material-status/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/logistics/outbound": dynamic(() => import("@/app/(main)/COMPANY_10/logistics/outbound/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/logistics/receiving": dynamic(() => import("@/app/(main)/COMPANY_10/logistics/receiving/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/logistics/packaging": dynamic(() => import("@/app/(main)/COMPANY_10/logistics/packaging/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/outsourcing/subcontractor": dynamic(() => import("@/app/(main)/COMPANY_10/outsourcing/subcontractor/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/outsourcing/subcontractor-item": dynamic(() => import("@/app/(main)/COMPANY_10/outsourcing/subcontractor-item/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/design/project": dynamic(() => import("@/app/(main)/COMPANY_10/design/project/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/design/change-management": dynamic(() => import("@/app/(main)/COMPANY_10/design/change-management/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/design/my-work": dynamic(() => import("@/app/(main)/COMPANY_10/design/my-work/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/design/design-request": dynamic(() => import("@/app/(main)/COMPANY_10/design/design-request/page"), { ssr: false, loading: LoadingFallback }), + "/COMPANY_10/design/task-management": dynamic(() => import("@/app/(main)/COMPANY_10/design/task-management/page"), { ssr: false, loading: LoadingFallback }), // === COMPANY_9 (제일그라스) === "/COMPANY_9/sales/order": dynamic(() => import("@/app/(main)/COMPANY_9/sales/order/page"), { ssr: false, loading: LoadingFallback }), @@ -222,6 +246,30 @@ const DYNAMIC_ADMIN_IMPORTS: Record Promise> = { "/COMPANY_7/design/my-work": () => import("@/app/(main)/COMPANY_7/design/my-work/page"), "/COMPANY_7/design/design-request": () => import("@/app/(main)/COMPANY_7/design/design-request/page"), "/COMPANY_7/design/task-management": () => import("@/app/(main)/COMPANY_7/design/task-management/page"), + // COMPANY_10 (큐앤씨) + "/COMPANY_10/master-data/item-info": () => import("@/app/(main)/COMPANY_10/master-data/item-info/page"), + "/COMPANY_10/master-data/department": () => import("@/app/(main)/COMPANY_10/master-data/department/page"), + "/COMPANY_10/sales/order": () => import("@/app/(main)/COMPANY_10/sales/order/page"), + "/COMPANY_10/sales/customer": () => import("@/app/(main)/COMPANY_10/sales/customer/page"), + "/COMPANY_10/sales/sales-item": () => import("@/app/(main)/COMPANY_10/sales/sales-item/page"), + "/COMPANY_10/sales/shipping-order": () => import("@/app/(main)/COMPANY_10/sales/shipping-order/page"), + "/COMPANY_10/sales/shipping-plan": () => import("@/app/(main)/COMPANY_10/sales/shipping-plan/page"), + "/COMPANY_10/sales/claim": () => import("@/app/(main)/COMPANY_10/sales/claim/page"), + "/COMPANY_10/production/process-info": () => import("@/app/(main)/COMPANY_10/production/process-info/page"), + "/COMPANY_10/production/work-instruction": () => import("@/app/(main)/COMPANY_10/production/work-instruction/page"), + "/COMPANY_10/production/plan-management": () => import("@/app/(main)/COMPANY_10/production/plan-management/page"), + "/COMPANY_10/equipment/info": () => import("@/app/(main)/COMPANY_10/equipment/info/page"), + "/COMPANY_10/logistics/material-status": () => import("@/app/(main)/COMPANY_10/logistics/material-status/page"), + "/COMPANY_10/logistics/outbound": () => import("@/app/(main)/COMPANY_10/logistics/outbound/page"), + "/COMPANY_10/logistics/receiving": () => import("@/app/(main)/COMPANY_10/logistics/receiving/page"), + "/COMPANY_10/logistics/packaging": () => import("@/app/(main)/COMPANY_10/logistics/packaging/page"), + "/COMPANY_10/outsourcing/subcontractor": () => import("@/app/(main)/COMPANY_10/outsourcing/subcontractor/page"), + "/COMPANY_10/outsourcing/subcontractor-item": () => import("@/app/(main)/COMPANY_10/outsourcing/subcontractor-item/page"), + "/COMPANY_10/design/project": () => import("@/app/(main)/COMPANY_10/design/project/page"), + "/COMPANY_10/design/change-management": () => import("@/app/(main)/COMPANY_10/design/change-management/page"), + "/COMPANY_10/design/my-work": () => import("@/app/(main)/COMPANY_10/design/my-work/page"), + "/COMPANY_10/design/design-request": () => import("@/app/(main)/COMPANY_10/design/design-request/page"), + "/COMPANY_10/design/task-management": () => import("@/app/(main)/COMPANY_10/design/task-management/page"), // COMPANY_9 (제일그라스) "/COMPANY_9/sales/order": () => import("@/app/(main)/COMPANY_9/sales/order/page"), // COMPANY_29 diff --git a/frontend/components/ui/popover.tsx b/frontend/components/ui/popover.tsx index 29d4a2f1..c9138ac2 100644 --- a/frontend/components/ui/popover.tsx +++ b/frontend/components/ui/popover.tsx @@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef< align={align} sideOffset={sideOffset} className={cn( - "bg-popover text-popover-foreground z-[10002] w-72 rounded-md border p-4 shadow-md outline-none", + "bg-popover text-popover-foreground z-[2000] w-72 rounded-md border p-4 shadow-md outline-none", className, )} {...props} diff --git a/frontend/components/ui/select.tsx b/frontend/components/ui/select.tsx index 0b34a6b2..1ebfdfed 100644 --- a/frontend/components/ui/select.tsx +++ b/frontend/components/ui/select.tsx @@ -62,7 +62,7 @@ function SelectContent({