diff --git a/src/app/admin-panel/code-form/page.tsx b/src/app/admin-panel/code-form/page.tsx deleted file mode 100644 index 94641db..0000000 --- a/src/app/admin-panel/code-form/page.tsx +++ /dev/null @@ -1,200 +0,0 @@ -"use client"; - -import { useState, useEffect, Suspense } from "react"; -import { useSearchParams } from "next/navigation"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import Swal from "sweetalert2"; - -interface ParentCode { CODE_ID: string; CODE_NAME: string } - -function CodeForm() { - const searchParams = useSearchParams(); - const codeId = searchParams.get("codeId"); - const isNew = !codeId; - const [form, setForm] = useState>({}); - const [parents, setParents] = useState([]); - const [loading, setLoading] = useState(false); - const [parentOpen, setParentOpen] = useState(false); - const [parentSearch, setParentSearch] = useState(""); - const set = (k: string, v: string) => setForm((p) => ({ ...p, [k]: v })); - - const selectedParent = parents.find((p) => p.CODE_ID === (form.parent_code_id || "")); - const filteredParents = parentSearch - ? parents.filter( - (p) => - p.CODE_NAME.toLowerCase().includes(parentSearch.toLowerCase()) || - p.CODE_ID.toLowerCase().includes(parentSearch.toLowerCase()) - ) - : parents; - - // 분류(최상위) 코드 로드 - useEffect(() => { - fetch("/api/admin/codes", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ parentOnly: true }), - }) - .then((r) => r.json()) - .then((d) => { - const rows = (d.RESULTLIST || []) as Array>; - setParents(rows.map((r) => ({ CODE_ID: String(r.CODE_ID || ""), CODE_NAME: String(r.CODE_NAME || "") }))); - }) - .catch(() => {}); - }, []); - - // 수정 시 상세 로드 - useEffect(() => { - if (codeId) { - fetch("/api/admin/codes/detail", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ codeId }), - }) - .then((r) => r.json()) - .then((d) => { - if (d.success) setForm(d.data); - }) - .catch(() => {}); - } - }, [codeId]); - - const handleSave = async () => { - if (!form.code_name) { - Swal.fire("알림", "코드명을 입력하세요.", "warning"); - return; - } - setLoading(true); - try { - const res = await fetch("/api/admin/codes/save", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ ...form, actionType: isNew ? "regist" : "update" }), - }); - const data = await res.json(); - if (data.success) { - await Swal.fire({ icon: "success", title: data.message || "저장되었습니다.", timer: 1500, showConfirmButton: false }); - if (window.opener) { - try { window.opener.location.reload(); } catch {} - } - if (isNew) window.close(); - } else { - Swal.fire("오류", data.message || "저장 실패", "error"); - } - } catch { - Swal.fire("오류", "서버 오류", "error"); - } finally { - setLoading(false); - } - }; - - return ( -
-

- 공통코드 관리 -

-
- - - - - - - - - - - - - - - -
분류선택 -
- { setParentOpen(true); setParentSearch(""); }} - onBlur={() => setTimeout(() => setParentOpen(false), 150)} - onChange={(e) => setParentSearch(e.target.value)} - className="h-9 w-full rounded border border-gray-300 bg-white pl-2 pr-8 text-sm outline-none focus:ring-1 focus:ring-blue-400" - /> - {selectedParent && !parentOpen && ( - - )} - {parentOpen && ( -
- - {filteredParents.length === 0 ? ( -
결과 없음
- ) : ( - filteredParents.map((p) => ( - - )) - )} -
- )} -
-
- 코드명 * - - set("code_name", e.target.value)} /> -
활성화여부 - -
-
-
- - -
-
- ); -} - -export default function Page() { - return ( - 로딩 중...}> - - - ); -} diff --git a/src/app/admin-panel/page.tsx b/src/app/admin-panel/page.tsx index e036ed0..2812799 100644 --- a/src/app/admin-panel/page.tsx +++ b/src/app/admin-panel/page.tsx @@ -14,7 +14,7 @@ import { } from "lucide-react"; // admin/adminMainFS.do 대응 - 관리자 팝업 페이지 -type AdminTab = "menu" | "auth" | "user" | "dept" | "code" | "supply" | "template" | "exchange" | "log-file" | "log-login" | "log-mail" | "ref-customer" | "ref-material" | "ref-car" | "ref-car-grade" | "ref-product-group" | "ref-product" | "spec-data-category" | "car-option"; +type AdminTab = "menu" | "auth" | "user" | "dept" | "supply" | "template" | "exchange" | "log-file" | "log-login" | "log-mail" | "ref-customer" | "ref-material" | "ref-car" | "ref-car-grade" | "ref-product-group" | "ref-product" | "spec-data-category" | "car-option"; const ADMIN_MENUS = [ { @@ -32,7 +32,6 @@ const ADMIN_MENUS = [ { label: "기준정보관리", icon: Database, items: [ - { key: "code" as AdminTab, label: "공통코드관리" }, { key: "supply" as AdminTab, label: "공급업체관리" }, { key: "template" as AdminTab, label: "템플릿 관리" }, { key: "exchange" as AdminTab, label: "환율관리" }, @@ -67,7 +66,6 @@ const LABEL_TO_TAB: Record = { "권한 관리": "auth", "부서 관리": "dept", "사용자 관리": "user", - "공통코드관리": "code", "공급업체관리": "supply", "템플릿 관리": "template", "환율관리": "exchange", @@ -99,7 +97,7 @@ interface SidebarGroup { } const VALID_TABS: AdminTab[] = [ - "menu","auth","user","dept","code","supply","template","exchange", + "menu","auth","user","dept","supply","template","exchange", "log-file","log-login","log-mail", "ref-customer","ref-material","ref-car","ref-car-grade", "ref-product-group","ref-product","spec-data-category","car-option", @@ -230,7 +228,6 @@ export default function AdminPanelPage() { {/* 우측 콘텐츠 */}
{activeTab === "user" && } - {activeTab === "code" && } {activeTab === "menu" && } {activeTab === "auth" && } {activeTab === "dept" && } @@ -248,7 +245,7 @@ export default function AdminPanelPage() { {activeTab === "spec-data-category" && } {activeTab === "car-option" && } {/* 기타 탭은 공통 Placeholder (DB 테이블 없음) */} - {!["user","code","menu","auth","dept","supply","log-login","log-file","log-mail","template","exchange","ref-customer","ref-material","ref-car","ref-product-group","ref-product","spec-data-category","car-option"].includes(activeTab) && ( + {!["user","menu","auth","dept","supply","log-login","log-file","log-mail","template","exchange","ref-customer","ref-material","ref-car","ref-product-group","ref-product","spec-data-category","car-option"].includes(activeTab) && ( g.items).find(i => LABEL_TO_TAB[i.label] === activeTab)?.label || activeTab} /> )}
@@ -352,189 +349,6 @@ function UserManagement() { ); } -// ========================================== -// 공통코드 관리 (admin/codeCategory/codeCategoryMngList.jsp) -// ========================================== -interface CodeRow { - OBJID: string; - CODE_ID: string; - CODE_NAME: string; - EXT_VAL: string; - PARENT_CODE_ID: string; - WRITER_NAME: string; - REGDATE: string; - STATUS: string; -} - -function CodeManagement() { - const [searchCode, setSearchCode] = useState(""); - const [searchStatus, setSearchStatus] = useState("active"); - const [data, setData] = useState([]); - const [expanded, setExpanded] = useState>(new Set()); - - const fetchData = useCallback(async () => { - const res = await fetch("/api/admin/codes", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ code_name: searchCode, status: searchStatus }), - }); - if (res.ok) { - const json = await res.json(); - const rows = (json.RESULTLIST || []) as Record[]; - const mapped: CodeRow[] = rows.map((r) => ({ - OBJID: String(r.OBJID || ""), - CODE_ID: String(r.CODE_ID || ""), - CODE_NAME: String(r.CODE_NAME || ""), - EXT_VAL: String(r.EXT_VAL || ""), - PARENT_CODE_ID: String(r.PARENT_CODE_ID || ""), - WRITER_NAME: String(r.WRITER_NAME || ""), - REGDATE: String(r.REGDATE || ""), - STATUS: String(r.STATUS || ""), - })); - setData(mapped); - } - }, [searchCode, searchStatus]); - - useEffect(() => { fetchData(); }, [fetchData]); - - // 검색어가 있으면 평면 리스트, 없으면 트리 모드 - const isSearching = searchCode.trim().length > 0; - - // code_id → 자식 리스트 매핑 - const childrenMap = useMemo(() => { - const m = new Map(); - data.forEach((r) => { - const key = r.PARENT_CODE_ID || ""; - if (!m.has(key)) m.set(key, []); - m.get(key)!.push(r); - }); - return m; - }, [data]); - - // 표시 행 (트리 평탄화 또는 검색 평면) - const visibleRows = useMemo(() => { - if (isSearching) { - return data.map((r) => ({ row: r, depth: 0, hasChildren: false })); - } - const out: { row: CodeRow; depth: number; hasChildren: boolean }[] = []; - const walk = (parentId: string, depth: number) => { - const kids = childrenMap.get(parentId) || []; - kids.forEach((k) => { - const hasChildren = (childrenMap.get(k.CODE_ID) || []).length > 0; - out.push({ row: k, depth, hasChildren }); - if (expanded.has(k.CODE_ID)) walk(k.CODE_ID, depth + 1); - }); - }; - walk("", 0); - return out; - }, [data, childrenMap, expanded, isSearching]); - - const toggleExpand = (codeId: string) => { - setExpanded((prev) => { - const next = new Set(prev); - if (next.has(codeId)) next.delete(codeId); else next.add(codeId); - return next; - }); - }; - - const expandAll = () => setExpanded(new Set(data.map((r) => r.CODE_ID))); - const collapseAll = () => setExpanded(new Set()); - - const openCodeDetail = (codeId: string) => { - window.open(`/admin-panel/code-form?codeId=${codeId}`, "codeFormPopup", "width=500,height=600"); - }; - - return ( -
-

공통코드관리

- - - setSearchCode(e.target.value)} className="w-[150px]" /> - - - - - -
- {!isSearching && ( - <> - - - - )} - - -
-
- 총 {data.length}건{isSearching ? " (검색 결과 - 평면 리스트)" : ` · 트리 모드 (최상위 ${(childrenMap.get("") || []).length}개)`} -
-
- - - - - - - - - - - - - {visibleRows.map(({ row, depth, hasChildren }) => { - const isExp = expanded.has(row.CODE_ID); - return ( - openCodeDetail(row.CODE_ID)} - > - - - - - - - - ); - })} - {visibleRows.length === 0 && ( - - )} - -
ID코드명CODE NO등록자등록일활성화
{row.CODE_ID} -
- {!isSearching && hasChildren ? ( - - ) : ( - · - )} - - {row.CODE_NAME} - -
-
{row.EXT_VAL || "-"}{row.WRITER_NAME || "-"}{row.REGDATE} - {row.STATUS === "active" ? ( - 활성 - ) : ( - 비활성 - )} -
데이터가 없습니다.
-
-
- ); -} - // ========================================== // 메뉴 관리 (트리구조 CRUD) // ========================================== diff --git a/src/app/api/admin/codes/detail/route.ts b/src/app/api/admin/codes/detail/route.ts deleted file mode 100644 index b8ddd77..0000000 --- a/src/app/api/admin/codes/detail/route.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { pool } from "@/lib/db"; -import { getSession } from "@/lib/session"; - -export async function POST(request: NextRequest) { - const user = await getSession(); - if (!user) return NextResponse.json({ success: false }, { status: 401 }); - const body = await request.json(); - const client = await pool.connect(); - try { - const result = await client.query( - `SELECT objid::text AS "objid", code_id AS "code_id", code_name AS "code_name", - ext_val AS "ext_val", parent_code_id AS "parent_code_id", - COALESCE(status, 'active') AS "status" - FROM comm_code WHERE code_id = $1`, - [body.codeId || ""] - ); - return NextResponse.json({ success: true, data: result.rows[0] || null }); - } catch (e) { - console.error("Code detail:", e); - return NextResponse.json({ success: false, message: "조회 중 오류가 발생했습니다." }); - } finally { client.release(); } -} diff --git a/src/app/api/admin/codes/route.ts b/src/app/api/admin/codes/route.ts deleted file mode 100644 index 6154008..0000000 --- a/src/app/api/admin/codes/route.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { queryRows } from "@/lib/db"; -import { getSession } from "@/lib/session"; - -// 공통코드관리 목록 조회 -// body.parentOnly === true → 최상위 코드만 (code-form 분류선택 드롭다운용) -// 그 외 → 전체 코드 (트리 렌더링용) -export async function POST(request: NextRequest) { - const user = await getSession(); - if (!user) return NextResponse.json({ success: false }, { status: 401 }); - - const body = await request.json(); - const conditions: string[] = []; - const params: unknown[] = []; - let idx = 1; - - if (body.parentOnly === true) { - conditions.push("(CC.parent_code_id IS NULL OR CC.parent_code_id = '')"); - } - - if (body.code_name) { - conditions.push(`CC.code_name LIKE '%' || $${idx++} || '%'`); - params.push(body.code_name); - } - if (body.status) { - conditions.push(`COALESCE(CC.status, 'active') = $${idx++}`); - params.push(body.status); - } - - const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""; - - const sql = ` - SELECT CC.objid::text AS "OBJID", - CC.code_id AS "CODE_ID", - CC.code_name AS "CODE_NAME", - CC.ext_val AS "EXT_VAL", - COALESCE(CC.parent_code_id, '') AS "PARENT_CODE_ID", - COALESCE((SELECT user_name FROM user_info WHERE user_id = CC.writer LIMIT 1), CC.writer) AS "WRITER_NAME", - TO_CHAR(CC.regdate, 'YYYY-MM-DD') AS "REGDATE", - COALESCE(CC.status, 'active') AS "STATUS" - FROM comm_code CC - ${where} - ORDER BY - COALESCE(CC.parent_code_id, '') ASC, - CC.code_id ASC - `; - - const rows = await queryRows(sql, params); - return NextResponse.json({ RESULTLIST: rows, TOTAL_CNT: rows.length }); -} diff --git a/src/app/api/admin/codes/save/route.ts b/src/app/api/admin/codes/save/route.ts deleted file mode 100644 index 9bf3362..0000000 --- a/src/app/api/admin/codes/save/route.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NextRequest, NextResponse } from "next/server"; -import { pool } from "@/lib/db"; -import { getSession } from "@/lib/session"; - -export async function POST(request: NextRequest) { - const user = await getSession(); - if (!user) return NextResponse.json({ success: false }, { status: 401 }); - const body = await request.json(); - const isNew = body.actionType === "regist" || !body.objid; - const objId = isNew ? Date.now() : Number(body.objid); - const status = body.status || "active"; - const client = await pool.connect(); - try { - await client.query( - `INSERT INTO comm_code (objid, code_id, code_name, ext_val, parent_code_id, writer, regdate, status) - VALUES ($1::numeric, $2, $3, $4, $5, $6, now(), $7) - ON CONFLICT (objid) DO UPDATE SET - code_name=EXCLUDED.code_name, - ext_val=EXCLUDED.ext_val, - parent_code_id=EXCLUDED.parent_code_id, - status=EXCLUDED.status`, - [ - objId, - body.code_id || "", - body.code_name || "", - body.ext_val || "", - body.parent_code_id || "", - user.userId, - status, - ] - ); - return NextResponse.json({ success: true, message: isNew ? "등록되었습니다." : "수정되었습니다." }); - } catch (e) { - console.error("Code save:", e); - return NextResponse.json({ success: false, message: "저장 중 오류가 발생했습니다." }); - } finally { - client.release(); - } -} diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx index 20fa8a8..0494b0a 100644 --- a/src/components/layout/sidebar.tsx +++ b/src/components/layout/sidebar.tsx @@ -47,7 +47,6 @@ const ADMIN_SYSTEM_MENU: MenuItem = { { objid: "__sys_auth", menuNameKor: "권한 관리", menuNameEng: "Auth", menuUrl: "/admin-panel?tab=auth", parentObjId: "__sys__", menuOrder: "2", level: "2" }, { objid: "__sys_dept", menuNameKor: "부서 관리", menuNameEng: "Dept", menuUrl: "/admin-panel?tab=dept", parentObjId: "__sys__", menuOrder: "3", level: "2" }, { objid: "__sys_menu", menuNameKor: "메뉴 관리", menuNameEng: "Menu", menuUrl: "/admin-panel?tab=menu", parentObjId: "__sys__", menuOrder: "4", level: "2" }, - { objid: "__sys_code", menuNameKor: "공통코드 관리", menuNameEng: "Code", menuUrl: "/admin-panel?tab=code", parentObjId: "__sys__", menuOrder: "5", level: "2" }, { objid: "__sys_supply", menuNameKor: "공급업체 관리", menuNameEng: "Vendors", menuUrl: "/admin-panel?tab=supply", parentObjId: "__sys__", menuOrder: "6", level: "2" }, { objid: "__sys_login", menuNameKor: "로그인 로그", menuNameEng: "Login Log", menuUrl: "/admin-panel?tab=log-login", parentObjId: "__sys__", menuOrder: "7", level: "2" }, { objid: "__sys_file", menuNameKor: "파일 로그", menuNameEng: "File Log", menuUrl: "/admin-panel?tab=log-file", parentObjId: "__sys__", menuOrder: "8", level: "2" },