fix: 프로젝트 전체 TypeScript 타입에러 408개 수정 (tsc --noEmit 0 errors)
- 공통 템플릿 타입 수정 (IntegratedDetailTemplate, UniversalListPage) - 페이지(app/[locale]) 타입 호환성 수정 (80개) - 재고/자재 모듈 타입 수정 (StockStatus, ReceivingManagement) - 생산 모듈 타입 수정 (WorkOrders, WorkerScreen, WorkResults) - 주문/출고 모듈 타입 수정 (ShipmentManagement, Orders) - 견적/단가 모듈 타입 수정 (Quotes, Pricing) - 건설 모듈 타입 수정 (49개, 17개 하위 모듈) - HR 모듈 타입 수정 (CardManagement, VacationManagement 등) - 설정 모듈 타입 수정 (PermissionManagement, AccountManagement 등) - 게시판 모듈 타입 수정 (BoardManagement, BoardList 등) - 회계 모듈 타입 수정 (VendorManagement, BadDebtCollection 등) - 기타 모듈 타입 수정 (CEODashboard, clients, vehicle 등) - 유틸/훅/API 타입 수정 (hooks, contexts, lib) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,7 +155,7 @@ export function InspectionCreate({ id }: Props) {
|
||||
}, [inspector, inspectionItems]);
|
||||
|
||||
// 검사 저장
|
||||
const handleSubmit = useCallback(() => {
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
@@ -211,7 +211,7 @@ export function InspectionCreate({ id }: Props) {
|
||||
>
|
||||
<p className="font-medium text-sm">{target.orderNo}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{target.supplier} · {target.qty} {target.unit}
|
||||
{target.supplier} · {target.orderQty ?? target.receivingQty ?? '-'} {target.unit}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
|
||||
@@ -44,7 +44,7 @@ export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete
|
||||
// 폼 데이터
|
||||
const [receivingLot, setReceivingLot] = useState(() => generateLotNo());
|
||||
const [supplierLot, setSupplierLot] = useState('');
|
||||
const [receivingQty, setReceivingQty] = useState<string>(detail.orderQty.toString());
|
||||
const [receivingQty, setReceivingQty] = useState<string>((detail.orderQty ?? 0).toString());
|
||||
const [receivingLocation, setReceivingLocation] = useState('');
|
||||
const [remark, setRemark] = useState('');
|
||||
|
||||
|
||||
@@ -314,13 +314,12 @@ export function StockStatusDetail({ id }: StockStatusDetailProps) {
|
||||
<IntegratedDetailTemplate
|
||||
config={stockStatusConfig}
|
||||
mode={initialMode as 'view' | 'edit'}
|
||||
initialData={detail || {}}
|
||||
initialData={(detail || undefined) as Record<string, unknown> | undefined}
|
||||
itemId={id}
|
||||
isLoading={isLoading}
|
||||
isSaving={isSaving}
|
||||
renderView={() => renderViewContent()}
|
||||
renderForm={() => renderFormContent()}
|
||||
onSave={handleSave}
|
||||
onSubmit={async () => { await handleSave(); return { success: true }; }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -214,13 +214,12 @@ export function StockStatusList() {
|
||||
{
|
||||
key: 'useStatus',
|
||||
label: '상태',
|
||||
type: 'select',
|
||||
type: 'single',
|
||||
options: [
|
||||
{ value: 'all', label: '전체' },
|
||||
{ value: 'active', label: '사용' },
|
||||
{ value: 'inactive', label: '미사용' },
|
||||
],
|
||||
defaultValue: 'all',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -41,55 +41,79 @@ function generateLocation(type: string, seed: number): string {
|
||||
const rawMaterialItems: StockItem[] = [
|
||||
{
|
||||
id: 'rm-1',
|
||||
stockNumber: 'STK-RM-001',
|
||||
itemCode: 'SCR-FABRIC-WHT-03T',
|
||||
itemName: '스크린원단-백색-0.3T',
|
||||
itemType: 'raw_material',
|
||||
specification: '-',
|
||||
unit: 'm²',
|
||||
calculatedQty: 500,
|
||||
actualQty: 500,
|
||||
stockQty: 500,
|
||||
safetyStock: 100,
|
||||
lotCount: 3,
|
||||
lotDaysElapsed: 21,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-01',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'rm-2',
|
||||
stockNumber: 'STK-RM-002',
|
||||
itemCode: 'SCR-FABRIC-GRY-03T',
|
||||
itemName: '스크린원단-회색-0.3T',
|
||||
itemType: 'raw_material',
|
||||
specification: '-',
|
||||
unit: 'm²',
|
||||
calculatedQty: 350,
|
||||
actualQty: 350,
|
||||
stockQty: 350,
|
||||
safetyStock: 80,
|
||||
lotCount: 2,
|
||||
lotDaysElapsed: 15,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-02',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'rm-3',
|
||||
stockNumber: 'STK-RM-003',
|
||||
itemCode: 'SCR-FABRIC-BLK-03T',
|
||||
itemName: '스크린원단-흑색-0.3T',
|
||||
itemType: 'raw_material',
|
||||
specification: '-',
|
||||
unit: 'm²',
|
||||
calculatedQty: 280,
|
||||
actualQty: 280,
|
||||
stockQty: 280,
|
||||
safetyStock: 70,
|
||||
lotCount: 2,
|
||||
lotDaysElapsed: 18,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-03',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'rm-4',
|
||||
stockNumber: 'STK-RM-004',
|
||||
itemCode: 'SCR-FABRIC-BEI-03T',
|
||||
itemName: '스크린원단-베이지-0.3T',
|
||||
itemType: 'raw_material',
|
||||
specification: '-',
|
||||
unit: 'm²',
|
||||
calculatedQty: 420,
|
||||
actualQty: 420,
|
||||
stockQty: 420,
|
||||
safetyStock: 90,
|
||||
lotCount: 4,
|
||||
lotDaysElapsed: 12,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-04',
|
||||
hasStock: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -105,16 +129,22 @@ const bentPartItems: StockItem[] = Array.from({ length: 41 }, (_, i) => {
|
||||
|
||||
return {
|
||||
id: `bp-${i + 1}`,
|
||||
stockNumber: `STK-BP-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `BENT-${type.toUpperCase().slice(0, 3)}-${variant}-${String(i + 1).padStart(2, '0')}`,
|
||||
itemName: `${type}-${variant}형-${i + 1}`,
|
||||
itemType: 'bent_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 1, 5),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 45),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: generateLocation('bent_part', seed + 4),
|
||||
hasStock: true,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -132,16 +162,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-sqp-${i + 1}`,
|
||||
stockNumber: `STK-PP-SQP-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `SQP-${size.replace('×', '')}-${length.slice(0, 2)}`,
|
||||
itemName: `각파이프 ${size} L:${length}`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 2, 5),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 40),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'I-05',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
// 앵글류 (15개)
|
||||
@@ -156,16 +192,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-ang-${i + 1}`,
|
||||
stockNumber: `STK-PP-ANG-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `ANG-${size.replace('×', '')}-${length.slice(0, 2)}`,
|
||||
itemName: `앵글 ${size} L:${length}`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 2, 4),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 35),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'I-04',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
// 전동개폐기류 (10개)
|
||||
@@ -182,16 +224,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-motor-${i + 1}`,
|
||||
stockNumber: `STK-PP-MOT-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `MOTOR-${voltage}${weight}${type === '무선' ? '-W' : ''}`,
|
||||
itemName: `전동개폐기-${voltage}${weight}${type}`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 1, 3),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 30),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'I-01',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
// 볼트/너트류 (15개)
|
||||
@@ -206,16 +254,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-bolt-${i + 1}`,
|
||||
stockNumber: `STK-PP-BLT-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `BOLT-${size}-${length}`,
|
||||
itemName: `볼트 ${size}×${length}mm`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 3, 6),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 25),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'J-01',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
// 베어링류 (10개)
|
||||
@@ -228,16 +282,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-bearing-${i + 1}`,
|
||||
stockNumber: `STK-PP-BRG-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `BEARING-${type}`,
|
||||
itemName: `베어링 ${type}`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 2, 4),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 20),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'J-02',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
// 스프링류 (10개)
|
||||
@@ -252,16 +312,22 @@ const purchasedPartItems: StockItem[] = [
|
||||
|
||||
return {
|
||||
id: `pp-spring-${i + 1}`,
|
||||
stockNumber: `STK-PP-SPR-${String(i + 1).padStart(3, '0')}`,
|
||||
itemCode: `SPRING-${type.toUpperCase().slice(0, 2)}-${size}`,
|
||||
itemName: `스프링-${type}-${size}`,
|
||||
itemType: 'purchased_part' as const,
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: stockQty,
|
||||
actualQty: stockQty,
|
||||
stockQty,
|
||||
safetyStock,
|
||||
lotCount: seededInt(seed + 2, 2, 5),
|
||||
lotDaysElapsed: seededInt(seed + 3, 0, 30),
|
||||
status: getStockStatus(stockQty, safetyStock),
|
||||
useStatus: 'active' as const,
|
||||
location: 'J-03',
|
||||
hasStock: true,
|
||||
};
|
||||
}),
|
||||
];
|
||||
@@ -270,94 +336,136 @@ const purchasedPartItems: StockItem[] = [
|
||||
const subMaterialItems: StockItem[] = [
|
||||
{
|
||||
id: 'sm-1',
|
||||
stockNumber: 'STK-SM-001',
|
||||
itemCode: 'SEW-WHT',
|
||||
itemName: '미싱실-백색',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'M',
|
||||
calculatedQty: 5000,
|
||||
actualQty: 5000,
|
||||
stockQty: 5000,
|
||||
safetyStock: 1000,
|
||||
lotCount: 3,
|
||||
lotDaysElapsed: 28,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-04',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-2',
|
||||
stockNumber: 'STK-SM-002',
|
||||
itemCode: 'ALU-BAR',
|
||||
itemName: '하단바-알루미늄',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 120,
|
||||
actualQty: 120,
|
||||
stockQty: 120,
|
||||
safetyStock: 30,
|
||||
lotCount: 1,
|
||||
lotDaysElapsed: 5,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-03',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-3',
|
||||
stockNumber: 'STK-SM-003',
|
||||
itemCode: 'END-CAP-STD',
|
||||
itemName: '앤드락-표준',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 800,
|
||||
actualQty: 800,
|
||||
stockQty: 800,
|
||||
safetyStock: 200,
|
||||
lotCount: 2,
|
||||
lotDaysElapsed: 12,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'A-02',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-4',
|
||||
stockNumber: 'STK-SM-004',
|
||||
itemCode: 'SILICON-TRANS',
|
||||
itemName: '실리콘-투명',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 200,
|
||||
actualQty: 200,
|
||||
stockQty: 200,
|
||||
safetyStock: 50,
|
||||
lotCount: 5,
|
||||
lotDaysElapsed: 37,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-03',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-5',
|
||||
stockNumber: 'STK-SM-005',
|
||||
itemCode: 'TAPE-DBL-25',
|
||||
itemName: '양면테이프-25mm',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 150,
|
||||
actualQty: 150,
|
||||
stockQty: 150,
|
||||
safetyStock: 40,
|
||||
lotCount: 2,
|
||||
lotDaysElapsed: 10,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-02',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-6',
|
||||
stockNumber: 'STK-SM-006',
|
||||
itemCode: 'RIVET-STL-4',
|
||||
itemName: '리벳-스틸-4mm',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 3000,
|
||||
actualQty: 3000,
|
||||
stockQty: 3000,
|
||||
safetyStock: 500,
|
||||
lotCount: 4,
|
||||
lotDaysElapsed: 8,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-01',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'sm-7',
|
||||
stockNumber: 'STK-SM-007',
|
||||
itemCode: 'WASHER-M8',
|
||||
itemName: '와셔-M8',
|
||||
itemType: 'sub_material',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 2500,
|
||||
actualQty: 2500,
|
||||
stockQty: 2500,
|
||||
safetyStock: 400,
|
||||
lotCount: 3,
|
||||
lotDaysElapsed: 15,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-04',
|
||||
hasStock: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -365,29 +473,41 @@ const subMaterialItems: StockItem[] = [
|
||||
const consumableItems: StockItem[] = [
|
||||
{
|
||||
id: 'cs-1',
|
||||
stockNumber: 'STK-CS-001',
|
||||
itemCode: 'PKG-BOX-L',
|
||||
itemName: '포장박스-대형',
|
||||
itemType: 'consumable',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 200,
|
||||
actualQty: 200,
|
||||
stockQty: 200,
|
||||
safetyStock: 50,
|
||||
lotCount: 2,
|
||||
lotDaysElapsed: 8,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-01',
|
||||
hasStock: true,
|
||||
},
|
||||
{
|
||||
id: 'cs-2',
|
||||
stockNumber: 'STK-CS-002',
|
||||
itemCode: 'PKG-BOX-M',
|
||||
itemName: '포장박스-중형',
|
||||
itemType: 'consumable',
|
||||
specification: '-',
|
||||
unit: 'EA',
|
||||
calculatedQty: 350,
|
||||
actualQty: 350,
|
||||
stockQty: 350,
|
||||
safetyStock: 80,
|
||||
lotCount: 3,
|
||||
lotDaysElapsed: 5,
|
||||
status: 'normal',
|
||||
useStatus: 'active',
|
||||
location: 'B-02',
|
||||
hasStock: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -474,6 +594,7 @@ export function generateStockDetail(item: StockItem): StockDetail {
|
||||
lotCount: item.lotCount,
|
||||
lastReceiptDate: lots[lots.length - 1]?.receiptDate || '2025-12-23',
|
||||
status: item.status,
|
||||
hasStock: item.hasStock,
|
||||
lots,
|
||||
};
|
||||
}
|
||||
@@ -503,12 +624,14 @@ const calculateStats = (): StockStats => {
|
||||
const normalCount = mockStockItems.filter(item => item.status === 'normal').length;
|
||||
const lowCount = mockStockItems.filter(item => item.status === 'low').length;
|
||||
const outCount = mockStockItems.filter(item => item.status === 'out').length;
|
||||
const noStockCount = mockStockItems.filter(item => !item.hasStock).length;
|
||||
|
||||
return {
|
||||
totalItems: mockStockItems.length,
|
||||
normalCount,
|
||||
lowCount,
|
||||
outCount,
|
||||
noStockCount,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ export const stockStatusConfig: DetailConfig = {
|
||||
icon: Package,
|
||||
basePath: '/material/stock-status',
|
||||
fields: [], // renderView/renderForm 사용으로 필드 정의 불필요
|
||||
gridColumns: 4,
|
||||
gridColumns: 3,
|
||||
actions: {
|
||||
showBack: true,
|
||||
showDelete: false,
|
||||
showEdit: true,
|
||||
backLabel: '목록',
|
||||
editLabel: '수정',
|
||||
saveLabel: '저장',
|
||||
submitLabel: '저장',
|
||||
cancelLabel: '취소',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,20 +5,31 @@
|
||||
*/
|
||||
|
||||
// 품목유형 (Item 모델의 MATERIAL_TYPES)
|
||||
export type ItemType = 'RM' | 'SM' | 'CS';
|
||||
// API에서는 'RM' | 'SM' | 'CS' 형태를 사용하지만, mock 데이터에서는 legacy 값도 지원
|
||||
export type ItemType = 'RM' | 'SM' | 'CS' | 'raw_material' | 'bent_part' | 'purchased_part' | 'sub_material' | 'consumable';
|
||||
|
||||
// 품목유형 라벨
|
||||
export const ITEM_TYPE_LABELS: Record<ItemType, string> = {
|
||||
export const ITEM_TYPE_LABELS: Partial<Record<ItemType, string>> = {
|
||||
RM: '원자재',
|
||||
SM: '부자재',
|
||||
CS: '소모품',
|
||||
raw_material: '원자재',
|
||||
bent_part: '절곡부품',
|
||||
purchased_part: '구매부품',
|
||||
sub_material: '부자재',
|
||||
consumable: '소모품',
|
||||
};
|
||||
|
||||
// 품목유형 스타일 (뱃지용)
|
||||
export const ITEM_TYPE_STYLES: Record<ItemType, string> = {
|
||||
export const ITEM_TYPE_STYLES: Partial<Record<ItemType, string>> = {
|
||||
RM: 'bg-blue-100 text-blue-800',
|
||||
SM: 'bg-green-100 text-green-800',
|
||||
CS: 'bg-orange-100 text-orange-800',
|
||||
raw_material: 'bg-blue-100 text-blue-800',
|
||||
bent_part: 'bg-purple-100 text-purple-800',
|
||||
purchased_part: 'bg-teal-100 text-teal-800',
|
||||
sub_material: 'bg-green-100 text-green-800',
|
||||
consumable: 'bg-orange-100 text-orange-800',
|
||||
};
|
||||
|
||||
// 재고 상태
|
||||
|
||||
Reference in New Issue
Block a user