diff --git a/frontend/app/(main)/COMPANY_10/mold/info/page.tsx b/frontend/app/(main)/COMPANY_10/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_10/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_10/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_10/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_10/outsourcing/subcontractor/page.tsx index d649ab56..46b78a03 100644 --- a/frontend/app/(main)/COMPANY_10/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_10/outsourcing/subcontractor/page.tsx @@ -141,10 +141,12 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1181,8 +1183,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1226,7 +1228,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_10/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_10/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_10/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_10/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_16/mold/info/page.tsx b/frontend/app/(main)/COMPANY_16/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_16/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_16/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_16/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_16/outsourcing/subcontractor/page.tsx index d649ab56..46b78a03 100644 --- a/frontend/app/(main)/COMPANY_16/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_16/outsourcing/subcontractor/page.tsx @@ -141,10 +141,12 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1181,8 +1183,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1226,7 +1228,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_16/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_16/purchase/supplier/page.tsx index 8f2755e5..a6453061 100644 --- a/frontend/app/(main)/COMPANY_16/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_16/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }, [itemSearchKeyword, priceItems]); @@ -2464,8 +2471,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2507,7 +2514,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx index e5821403..4e7bdbbf 100644 --- a/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = useCallback(async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2483,8 +2489,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2526,7 +2532,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_29/mold/info/page.tsx b/frontend/app/(main)/COMPANY_29/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_29/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_29/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_29/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_29/outsourcing/subcontractor/page.tsx index d649ab56..46b78a03 100644 --- a/frontend/app/(main)/COMPANY_29/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_29/outsourcing/subcontractor/page.tsx @@ -141,10 +141,12 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1181,8 +1183,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1226,7 +1228,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_29/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_29/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_29/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_29/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_30/mold/info/page.tsx b/frontend/app/(main)/COMPANY_30/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_30/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_30/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_30/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_30/outsourcing/subcontractor/page.tsx index d649ab56..46b78a03 100644 --- a/frontend/app/(main)/COMPANY_30/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_30/outsourcing/subcontractor/page.tsx @@ -141,10 +141,12 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1181,8 +1183,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1226,7 +1228,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_30/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_30/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_30/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_30/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_7/mold/info/page.tsx b/frontend/app/(main)/COMPANY_7/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_7/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_7/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_7/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_7/outsourcing/subcontractor/page.tsx index ed0e71d8..191608a8 100644 --- a/frontend/app/(main)/COMPANY_7/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_7/outsourcing/subcontractor/page.tsx @@ -141,11 +141,13 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - // item_info의 division 카테고리도 로드 (품목 검색 시 외주관리 코드 조회용) - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + // item_info의 division/unit/material 카테고리도 로드 (품목 검색 시 외주관리 코드 조회용) + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1182,8 +1184,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1227,7 +1229,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_7/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_7/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_7/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_7/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_8/mold/info/page.tsx b/frontend/app/(main)/COMPANY_8/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_8/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_8/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_8/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_8/outsourcing/subcontractor/page.tsx index 1118d571..1f9a3d0e 100644 --- a/frontend/app/(main)/COMPANY_8/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_8/outsourcing/subcontractor/page.tsx @@ -141,11 +141,13 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - // item_info의 division 카테고리도 로드 (품목 검색 시 외주관리 코드 조회용) - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + // item_info의 division/unit/material 카테고리도 로드 (품목 검색 시 외주관리 코드 조회용) + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1182,8 +1184,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1227,7 +1229,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_8/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_8/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_8/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_8/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_9/mold/info/page.tsx b/frontend/app/(main)/COMPANY_9/mold/info/page.tsx index dcd92eac..126c803d 100644 --- a/frontend/app/(main)/COMPANY_9/mold/info/page.tsx +++ b/frontend/app/(main)/COMPANY_9/mold/info/page.tsx @@ -26,6 +26,7 @@ import { import { cn } from "@/lib/utils"; import { apiClient } from "@/lib/api/client"; import { previewNumberingCode, allocateNumberingCode } from "@/lib/api/numberingRule"; +import { ImageUpload } from "@/components/common/ImageUpload"; import { useAuth } from "@/hooks/useAuth"; import { toast } from "sonner"; import { useConfirmDialog } from "@/components/common/ConfirmDialog"; @@ -102,8 +103,8 @@ export default function MoldInfoPage() { const [numberingRuleId, setNumberingRuleId] = useState(null); const [previewCode, setPreviewCode] = useState(null); const [saving, setSaving] = useState(false); + // moldImagePreview는 상세 표시용으로 유지 (ImageUpload는 모달에서만 사용) const [moldImagePreview, setMoldImagePreview] = useState(null); - const moldImageRef = React.useRef(null); const [serialModalOpen, setSerialModalOpen] = useState(false); const [serialForm, setSerialForm] = useState>({}); @@ -240,17 +241,7 @@ export default function MoldInfoPage() { setMoldModalOpen(true); }; - const handleMoldImageUpload = (e: React.ChangeEvent) => { - const file = e.target.files?.[0]; - if (!file) return; - const reader = new FileReader(); - reader.onloadend = () => { - const result = reader.result as string; - setMoldImagePreview(result); - setMoldForm((prev) => ({ ...prev, image_path: result })); - }; - reader.readAsDataURL(file); - }; + // 이미지 업로드는 ImageUpload 컴포넌트가 처리 → objid를 image_path에 저장 const handleSaveMold = async () => { // 등록 모드에서 채번이 없으면 수동 입력 필수 @@ -460,9 +451,10 @@ export default function MoldInfoPage() { )}> {mold.image_path ? ( {mold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -662,9 +654,10 @@ export default function MoldInfoPage() {
{selectedMold.image_path ? ( {selectedMold.mold_name} { (e.target as HTMLImageElement).style.display = "none"; }} /> ) : ( @@ -1143,39 +1136,13 @@ export default function MoldInfoPage() {
-
- {moldImagePreview ? ( - <> - 금형 이미지 -
- - -
- - ) : ( - - )} -
- setMoldForm((prev) => ({ ...prev, image_path: v }))} + tableName="mold_mng" + recordId={moldForm.id || ""} + columnName="image_path" + height="h-32" />
diff --git a/frontend/app/(main)/COMPANY_9/outsourcing/subcontractor/page.tsx b/frontend/app/(main)/COMPANY_9/outsourcing/subcontractor/page.tsx index d649ab56..46b78a03 100644 --- a/frontend/app/(main)/COMPANY_9/outsourcing/subcontractor/page.tsx +++ b/frontend/app/(main)/COMPANY_9/outsourcing/subcontractor/page.tsx @@ -141,10 +141,12 @@ export default function SubcontractorManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } - try { - const res = await apiClient.get(`/table-categories/item_info/division/values`); - if (res.data?.success) optMap["item_division"] = flatten(res.data.data || []); - } catch { /* skip */ } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -1181,8 +1183,8 @@ export default function SubcontractorManagementPage() { {item.item_number} {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -1226,7 +1228,7 @@ export default function SubcontractorManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_9/purchase/supplier/page.tsx b/frontend/app/(main)/COMPANY_9/purchase/supplier/page.tsx index 521f770e..eb9bd977 100644 --- a/frontend/app/(main)/COMPANY_9/purchase/supplier/page.tsx +++ b/frontend/app/(main)/COMPANY_9/purchase/supplier/page.tsx @@ -195,6 +195,12 @@ export default function SupplierManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -820,11 +826,12 @@ export default function SupplierManagementPage() { const allItems = res.data?.data?.data || res.data?.data?.rows || []; setItemTotalCount(allItems.length); const existingItemIds = new Set(priceItems.map((p: any) => p.item_id || p.item_number)); - const PURCHASE_CODES = ["s"]; // 구매관리 카테고리 코드 + const purchaseCode = categoryOptions["item_division"]?.find((o) => o.label === "구매관리")?.code; setItemSearchResults(allItems.filter((item: any) => { if (existingItemIds.has(item.item_number) || existingItemIds.has(item.id)) return false; - const divCodes = (item.division || "").split(",").map((c: string) => c.trim()); - return divCodes.some((code: string) => PURCHASE_CODES.includes(code)); + if (!purchaseCode) return true; + const div = item.division || ""; + return div.includes(purchaseCode); })); } catch { /* skip */ } finally { setItemSearchLoading(false); } }; @@ -2430,8 +2437,8 @@ export default function SupplierManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2473,7 +2480,7 @@ export default function SupplierManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx index c500c7ae..57897a8a 100644 --- a/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx @@ -195,6 +195,12 @@ export default function CustomerManagementPage() { if (res.data?.success) optMap[col] = flatten(res.data.data || []); } catch { /* skip */ } } + for (const col of ["division", "unit", "material"]) { + try { + const res = await apiClient.get(`/table-categories/item_info/${col}/values`); + if (res.data?.success) optMap[`item_${col}`] = flatten(res.data.data || []); + } catch { /* skip */ } + } setCategoryOptions(optMap); const priceOpts: Record = {}; @@ -811,7 +817,7 @@ export default function CustomerManagementPage() { const searchItems = async () => { setItemSearchLoading(true); try { - const salesCode = categoryOptions["division"]?.find((o) => o.label === "영업관리")?.code; + const salesCode = categoryOptions["item_division"]?.find((o) => o.label === "영업관리")?.code; const filters: any[] = salesCode ? [{ columnName: "division", operator: "contains", value: salesCode }] : []; @@ -2434,8 +2440,8 @@ export default function CustomerManagementPage() { {item.item_name} {item.size} - {item.material} - {item.unit} + {categoryOptions["item_material"]?.find((o) => o.code === item.material)?.label || item.material} + {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit} ))} @@ -2477,7 +2483,7 @@ export default function CustomerManagementPage() { {/* 품목 헤더 */}
{idx + 1}. {item.item_name || itemKey}
-
{itemKey} | {item.size || ""} | {item.unit || ""}
+
{itemKey} | {item.size || ""} | {categoryOptions["item_unit"]?.find((o) => o.code === item.unit)?.label || item.unit || ""}
diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index cbc58206..441d044c 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -28,6 +28,10 @@ const nextConfig = { source: "/api/:path*", destination: `${backendUrl}/api/:path*`, }, + { + source: "/uploads/:path*", + destination: `${backendUrl}/uploads/:path*`, + }, ]; },