diff --git a/frontend/app/(main)/COMPANY_16/master-data/item-info/page.tsx b/frontend/app/(main)/COMPANY_16/master-data/item-info/page.tsx
index 954c54c1..5fa81eae 100644
--- a/frontend/app/(main)/COMPANY_16/master-data/item-info/page.tsx
+++ b/frontend/app/(main)/COMPANY_16/master-data/item-info/page.tsx
@@ -33,9 +33,11 @@ import { DynamicSearchFilter, FilterValue } from "@/components/common/DynamicSea
import { exportToExcel } from "@/lib/utils/excelExport";
import {
Plus, Trash2, Save, Loader2, FileSpreadsheet, Download,
- Pencil, Copy, Settings2,
+ Pencil, Copy, Settings2, Check, ChevronsUpDown,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { EDataTable, EDataTableColumn } from "@/components/common/EDataTable";
import { cn } from "@/lib/utils";
import { apiClient } from "@/lib/api/client";
@@ -44,6 +46,43 @@ import { useTableSettings } from "@/hooks/useTableSettings";
import { TableSettingsModal } from "@/components/common/TableSettingsModal";
import { toast } from "sonner";
+// 검색 가능한 카테고리 콤보박스
+function CategoryCombobox({ options, value, onChange, placeholder }: {
+ options: { code: string; label: string }[];
+ value: string;
+ onChange: (v: string) => void;
+ placeholder: string;
+}) {
+ const [open, setOpen] = useState(false);
+ const selected = options.find((o) => o.code === value);
+ return (
+
+
+
+
+
+
+
+
+ 검색 결과가 없어요
+
+ {options.map((opt) => (
+ { onChange(opt.code); setOpen(false); }}>
+
+ {opt.label}
+
+ ))}
+
+
+
+
+
+ );
+}
+
const TABLE_NAME = "item_info";
const GRID_COLUMNS = [
@@ -55,8 +94,8 @@ const GRID_COLUMNS = [
{ key: "unit", label: "단위" },
{ key: "material", label: "재질" },
{ key: "status", label: "상태" },
- { key: "selling_price", label: "판매가격", align: "right" as const },
- { key: "standard_price", label: "기준단가", align: "right" as const },
+ { key: "selling_price", label: "판매가격", align: "right" as const, formatNumber: true },
+ { key: "standard_price", label: "기준단가", align: "right" as const, formatNumber: true },
{ key: "weight", label: "중량", align: "right" as const },
{ key: "inventory_unit", label: "재고단위" },
{ key: "user_type01", label: "대분류" },
@@ -163,6 +202,14 @@ export default function ItemInfoPage() {
const raw = res.data?.data?.data || res.data?.data?.rows || [];
const resolve = (col: string, code: string) => {
if (!code) return "";
+ // 쉼표 구분 다중값 지원
+ if (code.includes(",")) {
+ return code.split(",").map((c) => {
+ const trimmed = c.trim();
+ if (!trimmed || trimmed === "s") return "";
+ return categoryOptions[col]?.find((o) => o.code === trimmed)?.label || trimmed;
+ }).filter(Boolean).join(", ");
+ }
return categoryOptions[col]?.find((o) => o.code === code)?.label || code;
};
const data = raw.map((r: any) => {
@@ -361,6 +408,7 @@ export default function ItemInfoPage() {
key: col.key,
label: col.label,
align: col.align as "left" | "center" | "right" | undefined,
+ formatNumber: (col as any).formatNumber,
}))}
data={ts.groupData(items)}
loading={loading}
@@ -394,21 +442,12 @@ export default function ItemInfoPage() {
{field.required && *}
{field.type === "category" ? (
-
+ onChange={(v) => setFormData((prev) => ({ ...prev, [field.key]: v }))}
+ placeholder={`${field.label} 선택`}
+ />
) : field.type === "textarea" ? (
+ ) : ["selling_price", "standard_price"].includes(field.key) ? (
+ {
+ const raw = e.target.value.replace(/[^\d.-]/g, "");
+ setFormData((prev) => ({ ...prev, [field.key]: raw }));
+ }}
+ placeholder={field.placeholder || field.label}
+ className="h-9 text-right"
+ />
) : (