fix: [receiving] 입고 등록 초기값 설정 및 UX 개선

- 작성자 필드에 세션 사용자 이름 기본값 설정
- 입고일 필드에 오늘 날짜 기본값 설정
- 등록 완료 후 목록 대신 생성된 입고 상세 페이지로 바로 이동
- 수입검사 저장 시 rendered_html 크기 제한 (500KB 초과 시 제외, 413 방지)
- Dialog 접근성 경고 수정 (DialogDescription 추가)
This commit is contained in:
김보곤
2026-03-18 17:25:31 +09:00
parent faac8f657e
commit d08184d728
5 changed files with 54 additions and 26 deletions

View File

@@ -18,6 +18,8 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
VisuallyHidden,
DialogDescription,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -668,7 +670,13 @@ export function ImportInspectionInputModal({
// 캡처 실패 시 무시 — rendered_html 없이 저장 진행
}
// 5. 저장 API 호출
// 5. 저장 API 호출 (rendered_html이 너무 크면 제외 — 413 방지)
const MAX_HTML_SIZE = 500 * 1024; // 500KB 제한
const safeHtml = renderedHtml && renderedHtml.length <= MAX_HTML_SIZE ? renderedHtml : undefined;
if (renderedHtml && renderedHtml.length > MAX_HTML_SIZE) {
console.warn(`[ImportInspection] rendered_html 크기 초과 (${(renderedHtml.length / 1024).toFixed(0)}KB), 제외하고 저장합니다.`);
}
const result = await saveInspectionData({
templateId: parseInt(template.templateId),
itemId,
@@ -677,7 +685,7 @@ export function ImportInspectionInputModal({
attachments,
receivingId,
inspectionResult: overallResult,
rendered_html: renderedHtml,
rendered_html: safeHtml,
});
if (result.success) {
@@ -755,6 +763,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-[700px] max-h-[80vh] 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

@@ -68,28 +68,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: [],
};
}
// 로트번호 생성 (YYMMDD-NN)
function generateLotNo(): string {
@@ -133,7 +135,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);
@@ -294,7 +296,13 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
const result = await createReceiving(saveData);
if (result.success) {
toast.success('입고가 등록되었습니다.');
router.push('/ko/material/receiving-management');
// 등록 완료 후 생성된 입고 상세 페이지로 바로 이동 (목록 경유 방지)
const newId = result.data?.id;
if (newId) {
router.push(`/ko/material/receiving-management/${newId}?mode=view`);
} else {
router.push('/ko/material/receiving-management');
}
return { success: true };
} else {
toast.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';
@@ -111,6 +113,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,
VisuallyHidden,
DialogDescription,
DialogTitle,
} from '@/components/ui/dialog';
interface Props {
@@ -27,6 +30,8 @@ export function SuccessDialog({ open, type, lotNo, onClose }: Props) {
return (
<Dialog open={open} onOpenChange={(newOpen) => !newOpen && onClose()}>
<DialogContent className="max-w-sm">
<VisuallyHidden><DialogTitle> </DialogTitle></VisuallyHidden>
<VisuallyHidden><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" />