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,8 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { AlertCircle, Loader2 } from 'lucide-react';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { AlertCircle, Loader2, Save } from 'lucide-react';
|
||||
import { DocumentViewer } from '@/components/document-system';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { toast } from 'sonner';
|
||||
import { Document, DocumentItem } from '../types';
|
||||
import { MOCK_ORDER_DATA, MOCK_SHIPMENT_DETAIL } from '../mockData';
|
||||
|
||||
@@ -17,7 +19,7 @@ import {
|
||||
JointbarInspectionDocument,
|
||||
QualityDocumentUploader,
|
||||
} from './documents';
|
||||
import type { ImportInspectionTemplate } from './documents/ImportInspectionDocument';
|
||||
import type { ImportInspectionTemplate, ImportInspectionRef } from './documents/ImportInspectionDocument';
|
||||
|
||||
// 작업일지 + 중간검사 성적서 문서 컴포넌트 import (공정별 신규 버전)
|
||||
import {
|
||||
@@ -293,6 +295,10 @@ export const InspectionModalV2 = ({
|
||||
const [isLoadingTemplate, setIsLoadingTemplate] = useState(false);
|
||||
const [templateError, setTemplateError] = useState<string | null>(null);
|
||||
|
||||
// 수입검사 저장용 ref/상태
|
||||
const importDocRef = useRef<ImportInspectionRef>(null);
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
|
||||
// 수입검사 템플릿 로드 (모달 열릴 때)
|
||||
useEffect(() => {
|
||||
if (isOpen && doc?.type === 'import' && itemName && specification) {
|
||||
@@ -385,6 +391,23 @@ export const InspectionModalV2 = ({
|
||||
}
|
||||
};
|
||||
|
||||
// 수입검사 저장 핸들러
|
||||
const handleImportSave = useCallback(async () => {
|
||||
if (!importDocRef.current) return;
|
||||
|
||||
const data = importDocRef.current.getInspectionData();
|
||||
setIsSaving(true);
|
||||
try {
|
||||
// TODO: 실제 저장 API 연동
|
||||
console.log('[InspectionModalV2] 수입검사 저장 데이터:', data);
|
||||
toast.success('검사 데이터가 저장되었습니다.');
|
||||
} catch {
|
||||
toast.error('저장 중 오류가 발생했습니다.');
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 수입검사 문서 렌더링 (Lazy Loading)
|
||||
const renderImportInspectionDocument = () => {
|
||||
if (isLoadingTemplate) {
|
||||
@@ -396,7 +419,7 @@ export const InspectionModalV2 = ({
|
||||
}
|
||||
|
||||
// 템플릿이 로드되면 전달, 아니면 기본 템플릿 사용
|
||||
return <ImportInspectionDocument template={importTemplate || undefined} />;
|
||||
return <ImportInspectionDocument ref={importDocRef} template={importTemplate || undefined} />;
|
||||
};
|
||||
|
||||
// 문서 타입에 따른 컨텐츠 렌더링
|
||||
@@ -433,6 +456,18 @@ export const InspectionModalV2 = ({
|
||||
console.log('[InspectionModalV2] 다운로드 요청:', doc.type);
|
||||
};
|
||||
|
||||
// 수입검사 저장 버튼 (toolbarExtra)
|
||||
const importToolbarExtra = doc.type === 'import' ? (
|
||||
<Button onClick={handleImportSave} disabled={isSaving} size="sm">
|
||||
{isSaving ? (
|
||||
<Loader2 className="w-4 h-4 mr-1.5 animate-spin" />
|
||||
) : (
|
||||
<Save className="w-4 h-4 mr-1.5" />
|
||||
)}
|
||||
{isSaving ? '저장 중...' : '저장'}
|
||||
</Button>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<DocumentViewer
|
||||
title={doc.title}
|
||||
@@ -441,6 +476,7 @@ export const InspectionModalV2 = ({
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => !open && onClose()}
|
||||
onDownload={handleDownload}
|
||||
toolbarExtra={importToolbarExtra}
|
||||
>
|
||||
{renderDocumentContent()}
|
||||
</DocumentViewer>
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
* - API 데이터 기반 동적 렌더링
|
||||
*/
|
||||
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import { DocumentHeader, QualityApprovalTable } from '@/components/document-system';
|
||||
import React, { useState, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react';
|
||||
|
||||
// ============================================
|
||||
// 타입 정의
|
||||
@@ -92,6 +91,11 @@ export interface ImportInspectionTemplate {
|
||||
notes?: string[];
|
||||
}
|
||||
|
||||
/** ref를 통한 데이터 접근 인터페이스 */
|
||||
export interface ImportInspectionRef {
|
||||
getInspectionData: () => unknown;
|
||||
}
|
||||
|
||||
/** 컴포넌트 Props */
|
||||
export interface ImportInspectionDocumentProps {
|
||||
template?: ImportInspectionTemplate;
|
||||
@@ -325,12 +329,12 @@ const isValueInRange = (
|
||||
// 컴포넌트
|
||||
// ============================================
|
||||
|
||||
export const ImportInspectionDocument = ({
|
||||
export const ImportInspectionDocument = forwardRef<ImportInspectionRef, ImportInspectionDocumentProps>(function ImportInspectionDocument({
|
||||
template = MOCK_EGI_TEMPLATE,
|
||||
initialValues,
|
||||
onValuesChange,
|
||||
readOnly = false,
|
||||
}: ImportInspectionDocumentProps) => {
|
||||
}, ref) {
|
||||
// 검사 항목별 입력값 상태
|
||||
const [values, setValues] = useState<Record<string, InspectionItemValue>>(() => {
|
||||
const initial: Record<string, InspectionItemValue> = {};
|
||||
@@ -416,6 +420,22 @@ export const ImportInspectionDocument = ({
|
||||
return null;
|
||||
}, [values, template.inspectionItems]);
|
||||
|
||||
// ref를 통한 데이터 접근
|
||||
useImperativeHandle(ref, () => ({
|
||||
getInspectionData: () => ({
|
||||
templateId: template.templateId,
|
||||
values: Object.values(values),
|
||||
overallResult,
|
||||
}),
|
||||
}), [template.templateId, values, overallResult]);
|
||||
|
||||
// 날짜 포맷
|
||||
const fullDate = new Date().toLocaleDateString('ko-KR', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
});
|
||||
|
||||
// OK/NG 선택 핸들러
|
||||
const handleResultChange = useCallback((itemId: string, result: JudgmentResult) => {
|
||||
if (readOnly) return;
|
||||
@@ -599,18 +619,40 @@ export const ImportInspectionDocument = ({
|
||||
|
||||
return (
|
||||
<div className="bg-white p-8 w-full text-sm shadow-sm print:shadow-none">
|
||||
{/* 문서 헤더 */}
|
||||
<DocumentHeader
|
||||
title="수 입 검 사 성 적 서"
|
||||
logo={{ text: 'KD', subtext: '경동기업' }}
|
||||
customApproval={
|
||||
<QualityApprovalTable
|
||||
type="2col"
|
||||
approvers={{ writer: headerInfo.approvers.writer }}
|
||||
reportDate={headerInfo.reportDate}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
{/* 문서 헤더 (중간검사 성적서 스타일) */}
|
||||
<div className="flex justify-between items-start mb-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">수입검사 성적서</h1>
|
||||
<p className="text-xs text-gray-500 mt-1 whitespace-nowrap">
|
||||
문서번호: {headerInfo.lotNo || template.templateId} | 작성일자: {fullDate}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 결재란 */}
|
||||
<table className="border-collapse text-sm flex-shrink-0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border border-gray-400 px-4 py-1 text-center align-middle" rowSpan={3}>결<br/>재</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center">작성</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center">승인</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center">승인</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center">승인</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-400 px-6 py-3 text-center">{headerInfo.approvers.writer || '-'}</td>
|
||||
<td className="border border-gray-400 px-6 py-3 text-center text-gray-400">이름</td>
|
||||
<td className="border border-gray-400 px-6 py-3 text-center text-gray-400">이름</td>
|
||||
<td className="border border-gray-400 px-6 py-3 text-center text-gray-400">이름</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center whitespace-nowrap">부서명</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center whitespace-nowrap">부서명</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center whitespace-nowrap">부서명</td>
|
||||
<td className="border border-gray-400 px-6 py-1 text-center whitespace-nowrap">부서명</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* 기본 정보 테이블 - 6컬럼 구조 */}
|
||||
<table className="w-full border-collapse mb-4 text-xs">
|
||||
@@ -807,4 +849,4 @@ export const ImportInspectionDocument = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -107,6 +107,9 @@ export const MOCK_SHIPMENT_DETAIL: ShipmentDetail = {
|
||||
driverName: '최운전',
|
||||
driverContact: '010-5555-6666',
|
||||
remarks: '하차 시 주의 요망',
|
||||
vehicleDispatches: [],
|
||||
productGroups: [],
|
||||
otherParts: [],
|
||||
};
|
||||
|
||||
// 품질관리서 목록
|
||||
|
||||
Reference in New Issue
Block a user