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:
유병철
2026-01-30 10:07:58 +09:00
parent 8a5cbde5ef
commit a1f4c82cec
154 changed files with 832 additions and 536 deletions

View File

@@ -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>
))

View File

@@ -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('');

View File

@@ -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 }; }}
/>
);
}

View File

@@ -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',
},
];

View File

@@ -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,
};
};

View File

@@ -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: '취소',
},
};

View File

@@ -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',
};
// 재고 상태