momo 영역에서 미사용. FITO 레거시 잔존 코드 정리: - sidebar.tsx: __sys_code 가상 메뉴 항목 제거 - admin-panel/page.tsx: code 탭/메뉴/CodeManagement 함수 삭제 - admin-panel/code-form/: 폼 페이지 디렉토리 통째로 삭제 - api/admin/codes/: list/detail/save 라우트 통째로 삭제 /api/common/code-list (조회 전용) 는 product/part-change 등이 드롭다운 로드용으로 쓰고 있어 보존.
This commit is contained in:
@@ -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<Record<string, string>>({});
|
||||
const [parents, setParents] = useState<ParentCode[]>([]);
|
||||
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<Record<string, unknown>>;
|
||||
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 (
|
||||
<div className="p-5 bg-gray-50 min-h-screen">
|
||||
<h2 className="text-base font-bold text-gray-800 mb-4 flex items-center gap-1">
|
||||
<span className="text-blue-500">●</span> 공통코드 관리
|
||||
</h2>
|
||||
<div className="bg-white border rounded p-4">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th className="bg-[#5c6d97] text-white font-medium text-center py-2.5 w-[130px] border border-white">분류선택</th>
|
||||
<td className="border border-gray-200 p-1.5">
|
||||
<div className="relative">
|
||||
<input
|
||||
type="text"
|
||||
value={parentOpen ? parentSearch : (selectedParent?.CODE_NAME || "")}
|
||||
placeholder="선택 (검색 가능)"
|
||||
onFocus={() => { 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 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { set("parent_code_id", ""); setParentSearch(""); }}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-700 text-sm leading-none"
|
||||
aria-label="선택 해제"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
{parentOpen && (
|
||||
<div className="absolute z-50 left-0 right-0 top-full mt-1 max-h-60 overflow-y-auto bg-white border border-gray-300 rounded shadow-lg">
|
||||
<button
|
||||
type="button"
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
set("parent_code_id", "");
|
||||
setParentSearch("");
|
||||
setParentOpen(false);
|
||||
}}
|
||||
className="w-full text-left px-2 py-1.5 text-sm text-gray-500 italic hover:bg-gray-50 border-b"
|
||||
>
|
||||
(최상위 — 분류 없음)
|
||||
</button>
|
||||
{filteredParents.length === 0 ? (
|
||||
<div className="p-2 text-xs text-gray-400 text-center">결과 없음</div>
|
||||
) : (
|
||||
filteredParents.map((p) => (
|
||||
<button
|
||||
key={p.CODE_ID}
|
||||
type="button"
|
||||
onMouseDown={(e) => {
|
||||
e.preventDefault();
|
||||
set("parent_code_id", p.CODE_ID);
|
||||
setParentSearch("");
|
||||
setParentOpen(false);
|
||||
}}
|
||||
className="w-full text-left px-2 py-1.5 text-sm hover:bg-blue-50 flex items-center gap-2"
|
||||
>
|
||||
<span className="text-[11px] text-gray-400 font-mono shrink-0 w-[70px]">{p.CODE_ID}</span>
|
||||
<span className="truncate">{p.CODE_NAME}</span>
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="bg-[#5c6d97] text-white font-medium text-center py-2.5 border border-white">
|
||||
코드명 <span className="text-red-300">*</span>
|
||||
</th>
|
||||
<td className="border border-gray-200 p-1.5">
|
||||
<Input value={form.code_name || ""} onChange={(e) => set("code_name", e.target.value)} />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className="bg-[#5c6d97] text-white font-medium text-center py-2.5 border border-white">활성화여부</th>
|
||||
<td className="border border-gray-200 p-1.5">
|
||||
<select
|
||||
value={form.status || ""}
|
||||
onChange={(e) => set("status", e.target.value)}
|
||||
className="h-9 w-full rounded border border-gray-300 bg-white px-2 text-sm"
|
||||
>
|
||||
<option value="">선택</option>
|
||||
<option value="active">활성</option>
|
||||
<option value="inActive">비활성</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="flex justify-center gap-2 mt-5">
|
||||
<Button onClick={handleSave} disabled={loading}>{loading ? "저장 중..." : "저장"}</Button>
|
||||
<Button variant="secondary" onClick={() => window.close()}>닫기</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<Suspense fallback={<div className="p-8 text-center text-gray-400">로딩 중...</div>}>
|
||||
<CodeForm />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -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<string, AdminTab> = {
|
||||
"권한 관리": "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() {
|
||||
{/* 우측 콘텐츠 */}
|
||||
<main className="flex-1 overflow-auto p-4">
|
||||
{activeTab === "user" && <UserManagement />}
|
||||
{activeTab === "code" && <CodeManagement />}
|
||||
{activeTab === "menu" && <MenuManagement />}
|
||||
{activeTab === "auth" && <AuthManagement />}
|
||||
{activeTab === "dept" && <DeptManagement />}
|
||||
@@ -248,7 +245,7 @@ export default function AdminPanelPage() {
|
||||
{activeTab === "spec-data-category" && <SpecDataCategoryManagement />}
|
||||
{activeTab === "car-option" && <CarOptionManagement />}
|
||||
{/* 기타 탭은 공통 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) && (
|
||||
<PlaceholderContent title={groups.flatMap(g => g.items).find(i => LABEL_TO_TAB[i.label] === activeTab)?.label || activeTab} />
|
||||
)}
|
||||
</main>
|
||||
@@ -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<CodeRow[]>([]);
|
||||
const [expanded, setExpanded] = useState<Set<string>>(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<string, unknown>[];
|
||||
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<string, CodeRow[]>();
|
||||
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 (
|
||||
<div>
|
||||
<h2 className="text-lg font-bold text-gray-800 mb-4">공통코드관리</h2>
|
||||
<SearchForm onSearch={fetchData}>
|
||||
<SearchField label="코드명">
|
||||
<Input value={searchCode} onChange={(e) => setSearchCode(e.target.value)} className="w-[150px]" />
|
||||
</SearchField>
|
||||
<SearchField label="상태">
|
||||
<select value={searchStatus} onChange={(e) => setSearchStatus(e.target.value)}
|
||||
className="h-9 w-[120px] rounded border border-gray-300 bg-white px-3 text-sm">
|
||||
<option value="active">활성화</option>
|
||||
<option value="inActive">비활성화</option>
|
||||
<option value="">전체</option>
|
||||
</select>
|
||||
</SearchField>
|
||||
</SearchForm>
|
||||
<div className="flex justify-end gap-2 mb-3">
|
||||
{!isSearching && (
|
||||
<>
|
||||
<Button size="sm" variant="secondary" onClick={expandAll}>전체 펼치기</Button>
|
||||
<Button size="sm" variant="secondary" onClick={collapseAll}>전체 접기</Button>
|
||||
</>
|
||||
)}
|
||||
<Button size="sm" onClick={() => window.open("/admin-panel/code-form", "codeFormPopup", "width=500,height=600")}>코드등록</Button>
|
||||
<Button size="sm" variant="secondary" onClick={fetchData}>조회</Button>
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 mb-2">
|
||||
총 {data.length}건{isSearching ? " (검색 결과 - 평면 리스트)" : ` · 트리 모드 (최상위 ${(childrenMap.get("") || []).length}개)`}
|
||||
</div>
|
||||
<div className="border border-gray-200 rounded bg-white overflow-auto" style={{ maxHeight: "calc(100vh - 310px)" }}>
|
||||
<table className="w-full text-sm">
|
||||
<thead className="sticky top-0 bg-gray-100 z-10">
|
||||
<tr className="border-b">
|
||||
<th className="p-2 text-left w-[140px] font-medium text-gray-700">ID</th>
|
||||
<th className="p-2 text-left font-medium text-gray-700">코드명</th>
|
||||
<th className="p-2 text-left w-[120px] font-medium text-gray-700">CODE NO</th>
|
||||
<th className="p-2 text-center w-[100px] font-medium text-gray-700">등록자</th>
|
||||
<th className="p-2 text-center w-[110px] font-medium text-gray-700">등록일</th>
|
||||
<th className="p-2 text-center w-[80px] font-medium text-gray-700">활성화</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{visibleRows.map(({ row, depth, hasChildren }) => {
|
||||
const isExp = expanded.has(row.CODE_ID);
|
||||
return (
|
||||
<tr
|
||||
key={row.OBJID}
|
||||
className="border-t hover:bg-gray-50 cursor-pointer"
|
||||
onClick={() => openCodeDetail(row.CODE_ID)}
|
||||
>
|
||||
<td className="p-1.5 font-mono text-xs text-blue-600">{row.CODE_ID}</td>
|
||||
<td className="p-1.5">
|
||||
<div className="flex items-center" style={{ paddingLeft: `${depth * 18}px` }}>
|
||||
{!isSearching && hasChildren ? (
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); toggleExpand(row.CODE_ID); }}
|
||||
className="w-5 h-5 flex items-center justify-center mr-1 text-gray-400 hover:text-gray-700 text-[10px]"
|
||||
>
|
||||
{isExp ? "▼" : "▶"}
|
||||
</button>
|
||||
) : (
|
||||
<span className="w-5 h-5 mr-1 inline-flex items-center justify-center text-gray-300">·</span>
|
||||
)}
|
||||
<span className={cn(depth === 0 && !isSearching ? "font-bold text-gray-900" : "text-gray-700")}>
|
||||
{row.CODE_NAME}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-1.5 text-gray-600">{row.EXT_VAL || "-"}</td>
|
||||
<td className="p-1.5 text-center text-gray-600">{row.WRITER_NAME || "-"}</td>
|
||||
<td className="p-1.5 text-center text-gray-600">{row.REGDATE}</td>
|
||||
<td className="p-1.5 text-center">
|
||||
{row.STATUS === "active" ? (
|
||||
<span className="text-xs text-green-600">활성</span>
|
||||
) : (
|
||||
<span className="text-xs text-red-500">비활성</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
{visibleRows.length === 0 && (
|
||||
<tr><td colSpan={6} className="p-8 text-center text-gray-400">데이터가 없습니다.</td></tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 메뉴 관리 (트리구조 CRUD)
|
||||
// ==========================================
|
||||
|
||||
@@ -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(); }
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user