feat(items): 제조사 필드/메뉴 제거 + 공급업체 검색 + 원가/단가 천단위 콤마
Deploy momo-erp / deploy (push) Failing after 1m31s

- 품목 폼/리스트/모바일 카드에서 제조사 컬럼·셀렉트 제거 (dead code 정리)
- 공급업체 셀렉트 → SearchableSelect (결과내 검색 가능)
- 단가/원가 인풋: type=number → text + 천단위 콤마 표시, 소수점 제거(반올림)
- 운영 menu_info: '제조사 관리' (9000204) status='inactive'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chpark
2026-05-12 10:54:32 +09:00
parent 52084d6075
commit 9705a04328
2 changed files with 22 additions and 48 deletions
Binary file not shown.
+22 -48
View File
@@ -3,14 +3,13 @@
import { useEffect, useState, useRef, FormEvent } from "react";
import { Plus, Search, Pencil, Trash2, Upload } from "lucide-react";
import Swal from "sweetalert2";
import { SearchableSelect } from "@/components/ui/searchable-select";
interface Item {
OBJID: string;
ITEM_CODE: string;
ITEM_NAME: string;
ITEM_DETAIL: string;
MAKER_OBJID: string;
MAKER_NAME: string;
UNIT: string;
UNIT_PRICE: number;
COST_PRICE: number;
@@ -27,7 +26,6 @@ interface Item {
}
interface Vendor { OBJID: string; VENDOR_NAME: string }
interface Maker { OBJID: string; MAKER_NAME: string }
interface ItemAttributes {
expiry_date?: string; // 소비기한 (유통기한)
@@ -42,7 +40,6 @@ const fmt = (n: number) => Number(n || 0).toLocaleString("ko-KR");
export default function AdminItemsPage() {
const [items, setItems] = useState<Item[]>([]);
const [makers, setMakers] = useState<Maker[]>([]);
const [vendors, setVendors] = useState<Vendor[]>([]);
const [keyword, setKeyword] = useState("");
const [filterStatus, setFilterStatus] = useState("");
@@ -60,15 +57,6 @@ export default function AdminItemsPage() {
setItems((await res.json()).RESULTLIST ?? []);
};
const loadMakers = async () => {
const res = await fetch("/api/m/makers/list", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
setMakers((await res.json()).RESULTLIST ?? []);
};
const loadVendors = async () => {
const res = await fetch("/api/m/vendors/list", {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}),
@@ -78,7 +66,6 @@ export default function AdminItemsPage() {
useEffect(() => {
loadItems();
loadMakers();
loadVendors();
}, []); // eslint-disable-line
@@ -106,7 +93,6 @@ export default function AdminItemsPage() {
actionType: isNew ? "regist" : "update",
itemName: editing.ITEM_NAME,
itemDetail: editing.ITEM_DETAIL,
makerObjid: editing.MAKER_OBJID,
unit: editing.UNIT || "EA",
unitPrice: editing.UNIT_PRICE,
costPrice: editing.COST_PRICE,
@@ -236,7 +222,7 @@ export default function AdminItemsPage() {
{it.REQUIRES_DELIVERY === "Y" && <span className="px-1 py-0.5 rounded bg-orange-100 text-orange-700 text-[9px] font-bold"></span>}
{it.MAX_ORDER_QTY != null && Number(it.MAX_ORDER_QTY) > 0 && <span className="px-1 py-0.5 rounded bg-sky-100 text-sky-700 text-[9px] font-bold">{it.MAX_ORDER_QTY}</span>}
</div>
<div className="text-[10px] text-slate-400 font-mono mt-0.5">{it.ITEM_CODE} · {it.MAKER_NAME || "제조사 없음"}</div>
<div className="text-[10px] text-slate-400 font-mono mt-0.5">{it.ITEM_CODE}</div>
<div className="flex items-center justify-between mt-1.5 text-[11px]">
<div>
<span className="text-slate-400"></span> <b className="tabular-nums">{fmt(it.UNIT_PRICE)}</b>
@@ -261,7 +247,6 @@ export default function AdminItemsPage() {
<th className="px-3 py-3 w-14"></th>
<th className="text-left px-3 py-3"></th>
<th className="text-left px-3 py-3"></th>
<th className="text-left px-3 py-3"></th>
<th className="text-center px-3 py-3"></th>
<th className="text-right px-3 py-3"></th>
<th className="text-right px-3 py-3"></th>
@@ -273,7 +258,7 @@ export default function AdminItemsPage() {
<tbody>
{items.length === 0 ? (
<tr>
<td colSpan={10} className="text-center py-12 text-slate-400">
<td colSpan={9} className="text-center py-12 text-slate-400">
. .
</td>
</tr>
@@ -291,7 +276,6 @@ export default function AdminItemsPage() {
</td>
<td className="px-3 py-2 font-mono text-xs text-slate-500">{it.ITEM_CODE}</td>
<td className="px-3 py-2 font-semibold text-slate-800">{it.ITEM_NAME}</td>
<td className="px-3 py-2 text-slate-600 text-xs">{it.MAKER_NAME || "-"}</td>
<td className="px-3 py-2 text-center">
{it.IS_TAX_FREE === "Y" ? (
<span className="px-1.5 py-0.5 rounded bg-violet-100 text-violet-700 text-[10px] font-bold"></span>
@@ -361,29 +345,13 @@ export default function AdminItemsPage() {
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm focus:border-emerald-500 outline-none"
/>
</Field>
<Field label="제조사">
<select
value={editing.MAKER_OBJID ?? ""}
onChange={(e) => setEditing({ ...editing, MAKER_OBJID: e.target.value })}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm bg-white focus:border-emerald-500 outline-none"
>
<option value=""></option>
{makers.map((m) => (
<option key={m.OBJID} value={m.OBJID}>{m.MAKER_NAME}</option>
))}
</select>
</Field>
<Field label="공급업체">
<select
<SearchableSelect
options={vendors.map((v) => ({ value: v.OBJID, label: v.VENDOR_NAME }))}
value={editing.VENDOR_OBJID ?? ""}
onChange={(e) => setEditing({ ...editing, VENDOR_OBJID: e.target.value })}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm bg-white focus:border-emerald-500 outline-none"
>
<option value=""></option>
{vendors.map((v) => (
<option key={v.OBJID} value={v.OBJID}>{v.VENDOR_NAME}</option>
))}
</select>
onChange={(v) => setEditing({ ...editing, VENDOR_OBJID: v })}
placeholder="공급업체 검색"
/>
</Field>
<Field label="단위">
<select
@@ -420,18 +388,24 @@ export default function AdminItemsPage() {
</Field>
<Field label="단가 (VAT포함)">
<input
type="number" min={0}
value={editing.UNIT_PRICE ?? 0}
onChange={(e) => setEditing({ ...editing, UNIT_PRICE: Number(e.target.value) })}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm focus:border-emerald-500 outline-none"
type="text" inputMode="numeric"
value={editing.UNIT_PRICE == null ? "" : Math.round(Number(editing.UNIT_PRICE)).toLocaleString("ko-KR")}
onChange={(e) => {
const n = Number(e.target.value.replace(/[^0-9]/g, ""));
setEditing({ ...editing, UNIT_PRICE: Number.isFinite(n) ? n : 0 });
}}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm tabular-nums text-right focus:border-emerald-500 outline-none"
/>
</Field>
<Field label="원가">
<input
type="number" min={0}
value={editing.COST_PRICE ?? 0}
onChange={(e) => setEditing({ ...editing, COST_PRICE: Number(e.target.value) })}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm focus:border-emerald-500 outline-none"
type="text" inputMode="numeric"
value={editing.COST_PRICE == null ? "" : Math.round(Number(editing.COST_PRICE)).toLocaleString("ko-KR")}
onChange={(e) => {
const n = Number(e.target.value.replace(/[^0-9]/g, ""));
setEditing({ ...editing, COST_PRICE: Number.isFinite(n) ? n : 0 });
}}
className="w-full h-10 px-3 rounded-lg border border-slate-200 text-sm tabular-nums text-right focus:border-emerald-500 outline-none"
/>
</Field>
<Field label="상태">