fix: [material] BOM 트리뷰어 개선 + 입고관리 다이얼로그 보강

This commit is contained in:
유병철
2026-03-18 17:50:04 +09:00
parent 341f2b3e3f
commit 85862dbace
6 changed files with 201 additions and 145 deletions

View File

@@ -18,6 +18,8 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
VisuallyHidden,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -668,7 +670,11 @@ export function ImportInspectionInputModal({
// 캡처 실패 시 무시 — rendered_html 없이 저장 진행
}
// 5. 저장 API 호출
// 5. 저장 API 호출 (rendered_html 500KB 초과 시 제외 — 413 에러 방지)
const MAX_HTML_SIZE = 500 * 1024;
const safeHtml = renderedHtml && renderedHtml.length <= MAX_HTML_SIZE
? renderedHtml : undefined;
const result = await saveInspectionData({
templateId: parseInt(template.templateId),
itemId,
@@ -677,7 +683,7 @@ export function ImportInspectionInputModal({
attachments,
receivingId,
inspectionResult: overallResult,
rendered_html: renderedHtml,
rendered_html: safeHtml,
});
if (result.success) {
@@ -755,6 +761,7 @@ export function ImportInspectionInputModal({
<DialogContent className="w-[95vw] max-w-[500px] sm:max-w-[500px] max-h-[90vh] flex flex-col p-0">
<DialogHeader className="px-6 pt-6 pb-0 shrink-0">
<DialogTitle className="text-lg font-bold"></DialogTitle>
<VisuallyHidden><DialogDescription> </DialogDescription></VisuallyHidden>
</DialogHeader>
{isLoadingTemplate ? (

View File

@@ -19,6 +19,8 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
VisuallyHidden,
} from '@/components/ui/dialog';
import {
Select,
@@ -127,6 +129,7 @@ export function InventoryAdjustmentDialog({ open, onOpenChange, onSave }: Props)
<DialogContent className="max-w-[95vw] w-[95vw] max-h-[90vh] flex flex-col">
<DialogHeader>
<DialogTitle className="text-lg font-semibold"> </DialogTitle>
<VisuallyHidden><DialogDescription> </DialogDescription></VisuallyHidden>
</DialogHeader>
<div className="flex flex-col gap-4 flex-1 min-h-0">

View File

@@ -67,28 +67,30 @@ interface Props {
mode?: 'view' | 'edit' | 'new';
}
// 초기 폼 데이터
const INITIAL_FORM_DATA: Partial<ReceivingDetailType> = {
materialNo: '',
supplierMaterialNo: '',
lotNo: '',
itemCode: '',
itemName: '',
specification: '',
unit: 'EA',
supplier: '',
manufacturer: '',
receivingQty: undefined,
receivingDate: '',
createdBy: '',
status: 'receiving_pending',
remark: '',
inspectionDate: '',
inspectionResult: '',
certificateFile: undefined,
certificateFileId: undefined,
inventoryAdjustments: [],
};
// 초기 폼 데이터 (동적 함수 — 세션 사용자 이름 + 오늘 날짜 기본값)
function createInitialFormData(): Partial<ReceivingDetailType> {
return {
materialNo: '',
supplierMaterialNo: '',
lotNo: '',
itemCode: '',
itemName: '',
specification: '',
unit: 'EA',
supplier: '',
manufacturer: '',
receivingQty: undefined,
receivingDate: getTodayString(),
createdBy: getLoggedInUserName(),
status: 'receiving_pending',
remark: '',
inspectionDate: '',
inspectionResult: '',
certificateFile: undefined,
certificateFileId: undefined,
inventoryAdjustments: [],
};
}
// localStorage에서 로그인 사용자 정보 가져오기
function getLoggedInUser(): { name: string; department: string } {
@@ -122,7 +124,7 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
const [error, setError] = useState<string | null>(null);
// 폼 데이터 (등록/수정 모드용)
const [formData, setFormData] = useState<Partial<ReceivingDetailType>>(INITIAL_FORM_DATA);
const [formData, setFormData] = useState<Partial<ReceivingDetailType>>(createInitialFormData);
// 업로드된 파일 상태 (File 객체)
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
@@ -275,8 +277,14 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
const result = await createReceiving(saveData);
if (result.success) {
toast.success('입고가 등록되었습니다.');
router.push('/ko/material/receiving-management');
return { success: true };
const newId = result.data?.id;
if (newId) {
router.push(`/ko/material/receiving-management/${newId}?mode=view`);
} else {
router.push('/ko/material/receiving-management');
}
// 커스텀 네비게이션 처리: error='' → 템플릿의 navigateToList() 호출 방지
return { success: false, error: '' };
} else {
toast.error(result.error || '등록에 실패했습니다.');
return { success: false, error: result.error };

View File

@@ -19,6 +19,8 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
VisuallyHidden,
} from '@/components/ui/dialog';
import { Alert, AlertDescription } from '@/components/ui/alert';
import type { ReceivingDetail, ReceivingProcessFormData } from './types';
@@ -99,6 +101,7 @@ export function ReceivingProcessDialog({ open, onOpenChange, detail, onComplete
<DialogContent className="max-w-md">
<DialogHeader>
<DialogTitle> </DialogTitle>
<VisuallyHidden><DialogDescription> </DialogDescription></VisuallyHidden>
</DialogHeader>
<div className="space-y-6 mt-4">

View File

@@ -9,6 +9,9 @@ import { Button } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogTitle,
DialogDescription,
VisuallyHidden,
} from '@/components/ui/dialog';
interface Props {
@@ -27,6 +30,10 @@ export function SuccessDialog({ open, type, lotNo, onClose }: Props) {
return (
<Dialog open={open} onOpenChange={(newOpen) => !newOpen && onClose()}>
<DialogContent className="max-w-sm">
<VisuallyHidden>
<DialogTitle> </DialogTitle>
<DialogDescription> </DialogDescription>
</VisuallyHidden>
<div className="flex flex-col items-center text-center py-6 space-y-4">
<div className="w-16 h-16 rounded-full bg-green-100 flex items-center justify-center">
<CheckCircle2 className="w-10 h-10 text-green-600" />