feat(WEB): 수입검사 관리 대폭 개선, 캘린더 DayTimeView 추가 및 출고 기능 보완
- 수입검사: InspectionCreate/Detail/List 대폭 개선, OrderSelectModal/문서 컴포넌트 신규 추가 - 수입검사: actions/types/mockData/inspectionConfig 전면 리팩토링 - QMS: InspectionModalV2/ImportInspectionDocument 개선 - 캘린더: DayTimeView 신규 추가, CalendarHeader/ScheduleCalendar/utils 확장 - 출고: ShipmentDetail/List/actions 개선, ShipmentOrderDocument/ShippingSlip 수정 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,305 +1,476 @@
|
||||
import type {
|
||||
Inspection,
|
||||
ProductInspection,
|
||||
InspectionStats,
|
||||
InspectionItem,
|
||||
InspectionCalendarItem,
|
||||
OrderSelectItem,
|
||||
OrderSettingItem,
|
||||
InspectionStatus,
|
||||
InspectionRequestDocument,
|
||||
InspectionReportDocument,
|
||||
ReportInspectionItem,
|
||||
} from './types';
|
||||
import { getTodayString } from '@/utils/date';
|
||||
|
||||
// 검사 항목 템플릿 (조인트바 예시)
|
||||
export const inspectionItemsTemplate: InspectionItem[] = [
|
||||
{
|
||||
id: 'item-1',
|
||||
name: '가공상태',
|
||||
type: 'quality',
|
||||
spec: '결함 없을 것',
|
||||
},
|
||||
{
|
||||
id: 'item-2',
|
||||
name: '높이(H)',
|
||||
type: 'measurement',
|
||||
spec: '16.5 ± 1',
|
||||
unit: 'mm',
|
||||
},
|
||||
{
|
||||
id: 'item-3',
|
||||
name: '길이(L)',
|
||||
type: 'measurement',
|
||||
spec: '300 ± 4',
|
||||
unit: 'mm',
|
||||
},
|
||||
];
|
||||
// ===== 상태/색상 매핑 =====
|
||||
|
||||
// Mock 검사 데이터 (스크린샷 기반)
|
||||
export const mockInspections: Inspection[] = [
|
||||
{
|
||||
id: '1',
|
||||
inspectionNo: 'QC-251219-01',
|
||||
inspectionType: 'IQC',
|
||||
requestDate: '2025-12-19',
|
||||
itemName: 'EGI 철골판 1.5ST',
|
||||
lotNo: 'MAT-251219-01',
|
||||
processName: '입고 검사',
|
||||
quantity: 100,
|
||||
unit: 'EA',
|
||||
status: '대기',
|
||||
inspector: undefined,
|
||||
items: [],
|
||||
remarks: '',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
inspectionNo: 'QC-251219-02',
|
||||
inspectionType: 'PQC',
|
||||
requestDate: '2025-12-19',
|
||||
inspectionDate: '2025-12-19',
|
||||
itemName: '조인트바',
|
||||
lotNo: 'WO-251219-05',
|
||||
processName: '조립 공정',
|
||||
quantity: 50,
|
||||
unit: 'EA',
|
||||
status: '진행중',
|
||||
result: undefined,
|
||||
inspector: '홍길동',
|
||||
items: [
|
||||
{
|
||||
id: 'item-1',
|
||||
name: '가공상태',
|
||||
type: 'quality',
|
||||
spec: '결함 없을 것',
|
||||
result: '양호',
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-2',
|
||||
name: '높이(H)',
|
||||
type: 'measurement',
|
||||
spec: '16.5 ± 1',
|
||||
unit: 'mm',
|
||||
measuredValue: 16.6,
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-3',
|
||||
name: '길이(L)',
|
||||
type: 'measurement',
|
||||
spec: '300 ± 4',
|
||||
unit: 'mm',
|
||||
measuredValue: 301,
|
||||
judgment: '적합',
|
||||
},
|
||||
],
|
||||
remarks: '',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
inspectionNo: 'QC-251218-03',
|
||||
inspectionType: 'FQC',
|
||||
requestDate: '2025-12-18',
|
||||
inspectionDate: '2025-12-18',
|
||||
itemName: '방화샤터 완제품',
|
||||
lotNo: 'WO-251218-02',
|
||||
processName: '최종 검사',
|
||||
quantity: 10,
|
||||
unit: 'EA',
|
||||
status: '완료',
|
||||
result: '합격',
|
||||
inspector: '김철수',
|
||||
items: [
|
||||
{
|
||||
id: 'item-1',
|
||||
name: '가공상태',
|
||||
type: 'quality',
|
||||
spec: '결함 없을 것',
|
||||
result: '양호',
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-2',
|
||||
name: '높이(H)',
|
||||
type: 'measurement',
|
||||
spec: '16.5 ± 1',
|
||||
unit: 'mm',
|
||||
measuredValue: 16.6,
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-3',
|
||||
name: '길이(L)',
|
||||
type: 'measurement',
|
||||
spec: '300 ± 4',
|
||||
unit: 'mm',
|
||||
measuredValue: 301,
|
||||
judgment: '적합',
|
||||
},
|
||||
],
|
||||
remarks: '',
|
||||
opinion: '특이사항 없음. 후공정(포장) 인계 완료함.',
|
||||
attachments: [
|
||||
{
|
||||
id: 'att-1',
|
||||
fileName: '현장_검사_사진_01.jpg',
|
||||
fileUrl: '/uploads/inspection/현장_검사_사진_01.jpg',
|
||||
fileType: 'image/jpeg',
|
||||
uploadedAt: '2025-12-18T10:30:00',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
inspectionNo: 'QC-251218-04',
|
||||
inspectionType: 'PQC',
|
||||
requestDate: '2025-12-18',
|
||||
inspectionDate: '2025-12-18',
|
||||
itemName: '슬랫 성형품',
|
||||
lotNo: 'WO-251218-01',
|
||||
processName: '성형 공정',
|
||||
quantity: 200,
|
||||
unit: 'EA',
|
||||
status: '완료',
|
||||
result: '합격',
|
||||
inspector: '이영희',
|
||||
items: [
|
||||
{
|
||||
id: 'item-1',
|
||||
name: '가공상태',
|
||||
type: 'quality',
|
||||
spec: '결함 없을 것',
|
||||
result: '양호',
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-2',
|
||||
name: '높이(H)',
|
||||
type: 'measurement',
|
||||
spec: '16.5 ± 1',
|
||||
unit: 'mm',
|
||||
measuredValue: 16.4,
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-3',
|
||||
name: '길이(L)',
|
||||
type: 'measurement',
|
||||
spec: '300 ± 4',
|
||||
unit: 'mm',
|
||||
measuredValue: 299,
|
||||
judgment: '적합',
|
||||
},
|
||||
],
|
||||
remarks: '',
|
||||
opinion: '검사 완료. 이상 없음.',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
inspectionNo: 'QC-251218-05',
|
||||
inspectionType: 'IQC',
|
||||
requestDate: '2025-12-18',
|
||||
inspectionDate: '2025-12-18',
|
||||
itemName: '스테인레스 코일',
|
||||
lotNo: 'MAT-251218-03',
|
||||
processName: '입고 검사',
|
||||
quantity: 5,
|
||||
unit: 'ROLL',
|
||||
status: '완료',
|
||||
result: '합격',
|
||||
inspector: '박민수',
|
||||
items: [
|
||||
{
|
||||
id: 'item-1',
|
||||
name: '가공상태',
|
||||
type: 'quality',
|
||||
spec: '결함 없을 것',
|
||||
result: '양호',
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-2',
|
||||
name: '두께',
|
||||
type: 'measurement',
|
||||
spec: '1.2 ± 0.1',
|
||||
unit: 'mm',
|
||||
measuredValue: 1.19,
|
||||
judgment: '적합',
|
||||
},
|
||||
{
|
||||
id: 'item-3',
|
||||
name: '폭',
|
||||
type: 'measurement',
|
||||
spec: '1000 ± 5',
|
||||
unit: 'mm',
|
||||
measuredValue: 1001,
|
||||
judgment: '적합',
|
||||
},
|
||||
],
|
||||
remarks: '',
|
||||
opinion: '입고 검사 완료. 품질 적합.',
|
||||
},
|
||||
];
|
||||
|
||||
// 통계 데이터 계산
|
||||
export const calculateStats = (inspections: Inspection[]): InspectionStats => {
|
||||
const today = getTodayString();
|
||||
|
||||
const waitingCount = inspections.filter(i => i.status === '대기').length;
|
||||
const inProgressCount = inspections.filter(i => i.status === '진행중').length;
|
||||
const completedToday = inspections.filter(
|
||||
i => i.status === '완료' && i.inspectionDate === today
|
||||
).length;
|
||||
|
||||
const totalCompleted = inspections.filter(i => i.status === '완료').length;
|
||||
const defectCount = inspections.filter(i => i.result === '불합격').length;
|
||||
const defectRate = totalCompleted > 0 ? (defectCount / totalCompleted) * 100 : 0;
|
||||
|
||||
return {
|
||||
waitingCount,
|
||||
inProgressCount,
|
||||
completedCount: completedToday,
|
||||
defectRate: Math.round(defectRate * 10) / 10,
|
||||
};
|
||||
};
|
||||
|
||||
// 기본 통계 (mockData 기준)
|
||||
export const mockStats: InspectionStats = {
|
||||
waitingCount: 1,
|
||||
inProgressCount: 1,
|
||||
completedCount: 3,
|
||||
defectRate: 0.0,
|
||||
};
|
||||
|
||||
// 검사유형 라벨
|
||||
export const inspectionTypeLabels: Record<string, string> = {
|
||||
IQC: '입고검사',
|
||||
PQC: '공정검사',
|
||||
FQC: '최종검사',
|
||||
};
|
||||
|
||||
// 상태 컬러 매핑
|
||||
export const statusColorMap: Record<string, string> = {
|
||||
대기: 'bg-gray-100 text-gray-800',
|
||||
export const statusColorMap: Record<InspectionStatus, string> = {
|
||||
접수: 'bg-gray-100 text-gray-800',
|
||||
진행중: 'bg-blue-100 text-blue-800',
|
||||
완료: 'bg-green-100 text-green-800',
|
||||
};
|
||||
|
||||
// 판정 컬러 매핑
|
||||
export const statusCalendarColorMap: Record<InspectionStatus, string> = {
|
||||
접수: 'bg-blue-500',
|
||||
진행중: 'bg-blue-700',
|
||||
완료: 'bg-blue-400',
|
||||
};
|
||||
|
||||
export const judgmentColorMap: Record<string, string> = {
|
||||
합격: 'bg-green-100 text-green-800',
|
||||
불합격: 'bg-red-100 text-red-800',
|
||||
적합: 'text-green-600',
|
||||
부적합: 'text-red-600',
|
||||
};
|
||||
|
||||
// 측정값 판정 함수
|
||||
export const judgeMeasurement = (spec: string, value: number): '적합' | '부적합' => {
|
||||
// spec 예시: "16.5 ± 1" 또는 "300 ± 4"
|
||||
const match = spec.match(/^([\d.]+)\s*±\s*([\d.]+)$/);
|
||||
if (!match) return '적합'; // 파싱 실패 시 기본 적합
|
||||
// ===== 공통 관련자 정보 기본값 =====
|
||||
|
||||
const [, targetStr, toleranceStr] = match;
|
||||
const target = parseFloat(targetStr);
|
||||
const tolerance = parseFloat(toleranceStr);
|
||||
const defaultConstructionSite = {
|
||||
siteName: '현장명',
|
||||
landLocation: '주소명',
|
||||
lotNumber: '',
|
||||
};
|
||||
|
||||
const min = target - tolerance;
|
||||
const max = target + tolerance;
|
||||
const defaultMaterialDistributor = {
|
||||
companyName: '회사명',
|
||||
companyAddress: '주소명',
|
||||
representativeName: '홍길동',
|
||||
phone: '02-1234-1234',
|
||||
};
|
||||
|
||||
return value >= min && value <= max ? '적합' : '부적합';
|
||||
};
|
||||
const defaultConstructor = {
|
||||
companyName: '회사명',
|
||||
companyAddress: '주소명',
|
||||
name: '홍길동',
|
||||
phone: '02-1234-1234',
|
||||
};
|
||||
|
||||
const defaultSupervisor = {
|
||||
officeName: '회사명',
|
||||
officeAddress: '주소명',
|
||||
name: '홍길동',
|
||||
phone: '02-1234-1234',
|
||||
};
|
||||
|
||||
// ===== Mock 수주 선택 목록 (모달용) =====
|
||||
|
||||
export const mockOrderSelectItems: OrderSelectItem[] = [
|
||||
{ id: 'os-1', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-2', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-3', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-4', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-5', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-6', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
{ id: 'os-7', orderNumber: '123123', siteName: '현장명', deliveryDate: '2026-01-01', locationCount: 3 },
|
||||
];
|
||||
|
||||
// ===== Mock 수주 설정 항목 =====
|
||||
|
||||
const defaultOrderItems: OrderSettingItem[] = [
|
||||
{
|
||||
id: 'oi-1',
|
||||
orderNumber: '123123',
|
||||
floor: '1층',
|
||||
symbol: '부호명',
|
||||
orderWidth: 4100,
|
||||
orderHeight: 2700,
|
||||
constructionWidth: 4100,
|
||||
constructionHeight: 2700,
|
||||
changeReason: '',
|
||||
},
|
||||
{
|
||||
id: 'oi-2',
|
||||
orderNumber: '123123',
|
||||
floor: '2층',
|
||||
symbol: '부호명',
|
||||
orderWidth: 4100,
|
||||
orderHeight: 2700,
|
||||
constructionWidth: 4100,
|
||||
constructionHeight: 2700,
|
||||
changeReason: '',
|
||||
},
|
||||
];
|
||||
|
||||
// ===== Mock 제품검사 데이터 =====
|
||||
|
||||
export const mockInspections: ProductInspection[] = [
|
||||
{
|
||||
id: '1',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '완료',
|
||||
inspectionPeriod: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
status: '접수',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '4건 누락',
|
||||
inspectionPeriod: '2026-01-01~2026-01-02',
|
||||
inspector: '홍길동',
|
||||
status: '진행중',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-02',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '3건 누락',
|
||||
inspectionPeriod: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
status: '접수',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '완료',
|
||||
inspectionPeriod: '2026-01-01~2026-01-02',
|
||||
inspector: '홍길동',
|
||||
status: '완료',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-02',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '4건 누락',
|
||||
inspectionPeriod: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
status: '완료',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-01',
|
||||
inspector: '진행중',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '3건 누락',
|
||||
inspectionPeriod: '2026-01-01~2026-01-02',
|
||||
inspector: '홍길동',
|
||||
status: '접수',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-02',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
qualityDocNumber: '123123',
|
||||
siteName: '현장명',
|
||||
client: '회사명',
|
||||
locationCount: 5,
|
||||
requiredInfo: '완료',
|
||||
inspectionPeriod: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
status: '완료',
|
||||
author: '홍길동',
|
||||
receptionDate: '2026-01-01',
|
||||
manager: '홍길동',
|
||||
managerContact: '010-1234-1234',
|
||||
constructionSite: defaultConstructionSite,
|
||||
materialDistributor: defaultMaterialDistributor,
|
||||
constructorInfo: defaultConstructor,
|
||||
supervisor: defaultSupervisor,
|
||||
scheduleInfo: {
|
||||
visitRequestDate: '2026-01-01',
|
||||
startDate: '2026-01-01',
|
||||
endDate: '2026-01-01',
|
||||
inspector: '홍길동',
|
||||
sitePostalCode: '123',
|
||||
siteAddress: '서울특별시 서초구 서초대로 123',
|
||||
siteAddressDetail: '대한건물 12층 1201호',
|
||||
},
|
||||
orderItems: defaultOrderItems,
|
||||
},
|
||||
];
|
||||
|
||||
// ===== Mock 통계 =====
|
||||
|
||||
export const mockStats: InspectionStats = {
|
||||
receptionCount: 10,
|
||||
inProgressCount: 10,
|
||||
completedCount: 10,
|
||||
};
|
||||
|
||||
// ===== 통계 계산 =====
|
||||
|
||||
export const calculateStats = (inspections: ProductInspection[]): InspectionStats => {
|
||||
return {
|
||||
receptionCount: inspections.filter(i => i.status === '접수').length,
|
||||
inProgressCount: inspections.filter(i => i.status === '진행중').length,
|
||||
completedCount: inspections.filter(i => i.status === '완료').length,
|
||||
};
|
||||
};
|
||||
|
||||
// ===== Mock 캘린더 데이터 =====
|
||||
|
||||
export const mockCalendarItems: InspectionCalendarItem[] = [
|
||||
{ id: 'cal-1', startDate: '2026-01-13', endDate: '2026-01-14', inspector: '홍길동', siteName: '현장명', status: '완료' },
|
||||
{ id: 'cal-2', startDate: '2026-01-15', endDate: '2026-01-17', inspector: '홍길동', siteName: '현장명', status: '진행중' },
|
||||
{ id: 'cal-3', startDate: '2026-01-23', endDate: '2026-01-25', inspector: '홍길동', siteName: '현장명', status: '진행중' },
|
||||
{ id: 'cal-4', startDate: '2026-01-26', endDate: '2026-01-26', inspector: '홍길동', siteName: '현장명', status: '접수' },
|
||||
{ id: 'cal-5', startDate: '2026-01-26', endDate: '2026-01-27', inspector: '홍길동', siteName: '현장명', status: '접수' },
|
||||
{ id: 'cal-6', startDate: '2026-01-27', endDate: '2026-01-28', inspector: '홍길동', siteName: '현장명', status: '접수' },
|
||||
{ id: 'cal-7', startDate: '2026-01-28', endDate: '2026-01-28', inspector: '홍길동', siteName: '현장명', status: '접수' },
|
||||
];
|
||||
|
||||
// ===== 수주 규격 비교 유틸 =====
|
||||
|
||||
export const isOrderSpecSame = (item: OrderSettingItem): boolean => {
|
||||
return item.orderWidth === item.constructionWidth && item.orderHeight === item.constructionHeight;
|
||||
};
|
||||
|
||||
export const calculateOrderSummary = (items: OrderSettingItem[]) => {
|
||||
const total = items.length;
|
||||
const same = items.filter(isOrderSpecSame).length;
|
||||
const changed = total - same;
|
||||
return { total, same, changed };
|
||||
};
|
||||
|
||||
// ===== 빈 폼 기본값 =====
|
||||
|
||||
export const emptyConstructionSite = {
|
||||
siteName: '',
|
||||
landLocation: '',
|
||||
lotNumber: '',
|
||||
};
|
||||
|
||||
export const emptyMaterialDistributor = {
|
||||
companyName: '',
|
||||
companyAddress: '',
|
||||
representativeName: '',
|
||||
phone: '',
|
||||
};
|
||||
|
||||
export const emptyConstructor = {
|
||||
companyName: '',
|
||||
companyAddress: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
};
|
||||
|
||||
export const emptySupervisor = {
|
||||
officeName: '',
|
||||
officeAddress: '',
|
||||
name: '',
|
||||
phone: '',
|
||||
};
|
||||
|
||||
export const emptyScheduleInfo = {
|
||||
visitRequestDate: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
inspector: '',
|
||||
sitePostalCode: '',
|
||||
siteAddress: '',
|
||||
siteAddressDetail: '',
|
||||
};
|
||||
|
||||
// ===== 문서 데이터 변환 헬퍼 =====
|
||||
|
||||
/** ProductInspection → InspectionRequestDocument 변환 */
|
||||
export const buildRequestDocumentData = (
|
||||
inspection: ProductInspection
|
||||
): InspectionRequestDocument => ({
|
||||
documentNumber: `REQ-${inspection.qualityDocNumber}`,
|
||||
createdDate: inspection.receptionDate,
|
||||
approvalLine: [
|
||||
{ role: '작성', name: inspection.author, department: '' },
|
||||
{ role: '승인', name: '', department: '' },
|
||||
],
|
||||
client: inspection.client,
|
||||
companyName: inspection.materialDistributor.companyName,
|
||||
manager: inspection.manager,
|
||||
orderNumber: inspection.orderItems[0]?.orderNumber || '',
|
||||
managerContact: inspection.managerContact,
|
||||
siteName: inspection.siteName,
|
||||
deliveryDate: '',
|
||||
siteAddress: `${inspection.scheduleInfo.siteAddress} ${inspection.scheduleInfo.siteAddressDetail}`.trim(),
|
||||
totalLocations: String(inspection.locationCount),
|
||||
receptionDate: inspection.receptionDate,
|
||||
visitRequestDate: inspection.scheduleInfo.visitRequestDate,
|
||||
constructionSite: inspection.constructionSite,
|
||||
materialDistributor: inspection.materialDistributor,
|
||||
constructorInfo: inspection.constructorInfo,
|
||||
supervisor: inspection.supervisor,
|
||||
priorNoticeItems: inspection.orderItems,
|
||||
});
|
||||
|
||||
/** Mock 검사항목 (제품검사성적서용) - 기획서 기반 상세 항목 */
|
||||
export const mockReportInspectionItems: ReportInspectionItem[] = [
|
||||
// 1. 겉모양 (5개 세부항목) — 항목1+2+3 병합: 육안검사/전수검사 (7행)
|
||||
{ no: 1, category: '겉모양', subCategory: '가공상태', criteria: '사용상 해로운 결함이 없을 것', method: '육안검사', frequency: '전수검사', methodSpan: 7, freqSpan: 7, measuredValueSpan: 7 },
|
||||
{ no: 1, category: '겉모양', subCategory: '재봉상태', criteria: '내화실에 의해 견고하게 접합되어야 함', method: '', frequency: '' },
|
||||
{ no: 1, category: '겉모양', subCategory: '조립상태', criteria: '핸드링이 견고하게 조립되어야 함', method: '', frequency: '' },
|
||||
{ no: 1, category: '겉모양', subCategory: '연기차단재', criteria: '연기차단재 설치여부(케이스 W80, 가이드레일 W50(양쪽 설치))', method: '', frequency: '' },
|
||||
{ no: 1, category: '겉모양', subCategory: '하단마감재', criteria: '내부 무겁방절 설치 유무', method: '', frequency: '' },
|
||||
// 2. 모터
|
||||
{ no: 2, category: '모터', criteria: '인정제품과 동일사양', method: '', frequency: '' },
|
||||
// 3. 재질
|
||||
{ no: 3, category: '재질', criteria: 'WY-SC780 인쇄상태 확인', method: '', frequency: '' },
|
||||
// 4. 치수(오픈사이즈) (4개 세부항목) — 항목4만 병합: 체크검사/전수검사 (4행)
|
||||
{ no: 4, category: '치수\n(오픈사이즈)', subCategory: '길이', criteria: '수주 치수 ± 30mm', method: '체크검사', frequency: '전수검사', methodSpan: 4, freqSpan: 4 },
|
||||
{ no: 4, category: '치수\n(오픈사이즈)', subCategory: '높이', criteria: '수주 치수 ± 30mm', method: '', frequency: '' },
|
||||
{ no: 4, category: '치수\n(오픈사이즈)', subCategory: '가이드레일 간격', criteria: '10 ± 5mm (측정부위 @ 높이 100 이하)', method: '', frequency: '' },
|
||||
{ no: 4, category: '치수\n(오픈사이즈)', subCategory: '하단막대 간격', criteria: '간격 (③+④)\n가이드레일과 하단마감재 측 사이 25mm 이내', method: '', frequency: '' },
|
||||
// 5. 작동테스트 — 판정 없음
|
||||
{ no: 5, category: '작동테스트', subCategory: '개폐성능', criteria: '작동 유무 확인\n(일부 및 완전폐쇄)', method: '', frequency: '', hideJudgment: true },
|
||||
// 6. 내화시험 (3개 세부항목) — "비차열\n차열성" 3행 병합, 항목 6+7+8+9 검사방법/주기/판정 모두 병합 (10행)
|
||||
{ no: 6, category: '내화시험', subCategory: '비차열\n차열성', subCategorySpan: 3, criteria: '6mm 균열게이지 관통 후 150mm 이동 유무', method: '공인\n시험기관\n시험\n성적서', frequency: '1회/5년', methodSpan: 10, freqSpan: 10, measuredValueSpan: 10, judgmentSpan: 10 },
|
||||
{ no: 6, category: '내화시험', criteria: '25mm 균열게이지 관통 유무', method: '', frequency: '' },
|
||||
{ no: 6, category: '내화시험', criteria: '10초 이상 지속되는 화염 발생 유무', method: '', frequency: '' },
|
||||
// 7. 차연시험 — No.6의 span에 포함됨
|
||||
{ no: 7, category: '차연시험', subCategory: '공기누설량', criteria: '25Pa 일 때 공기누설량 0.9m³/min·m² 이하', method: '', frequency: '' },
|
||||
// 8. 개폐시험 (5개 세부항목) — "평균속도" 2행 병합 (전도개폐 + 자중강화)
|
||||
{ no: 8, category: '개폐시험', criteria: '개폐의 원활한 작동', method: '', frequency: '' },
|
||||
{ no: 8, category: '개폐시험', subCategory: '평균속도', subCategorySpan: 2, criteria: '전도개폐 2.5~6.5m/min', method: '', frequency: '' },
|
||||
{ no: 8, category: '개폐시험', criteria: '자중강화 3~7m/min', method: '', frequency: '' },
|
||||
{ no: 8, category: '개폐시험', criteria: '개폐 시 상부 및 하부 끝부분에서 자동정지', method: '', frequency: '' },
|
||||
{ no: 8, category: '개폐시험', criteria: '강화 중 임의의 위치에서 정지', method: '', frequency: '' },
|
||||
// 9. 내충격시험
|
||||
{ no: 9, category: '내충격시험', criteria: '방화상 유해한 파괴, 박리 탈락 유무', method: '', frequency: '' },
|
||||
];
|
||||
|
||||
/** ProductInspection → InspectionReportDocument 변환 */
|
||||
export const buildReportDocumentData = (
|
||||
inspection: ProductInspection
|
||||
): InspectionReportDocument => ({
|
||||
documentNumber: `RPT-${inspection.qualityDocNumber}`,
|
||||
createdDate: inspection.receptionDate,
|
||||
approvalLine: [
|
||||
{ role: '작성', name: inspection.scheduleInfo.inspector || inspection.author, department: '' },
|
||||
{ role: '승인', name: '', department: '' },
|
||||
],
|
||||
productName: '방화스크린',
|
||||
productLotNo: inspection.qualityDocNumber,
|
||||
productCode: '',
|
||||
lotSize: String(inspection.locationCount),
|
||||
client: inspection.client,
|
||||
inspectionDate: inspection.scheduleInfo.startDate,
|
||||
siteName: inspection.siteName,
|
||||
inspector: inspection.scheduleInfo.inspector,
|
||||
inspectionItems: mockReportInspectionItems,
|
||||
specialNotes: '',
|
||||
finalJudgment: inspection.status === '완료' ? '합격' : '합격',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user