fix: [receiving] 입고 등록 초기값 설정 및 UX 개선
- 작성자 필드에 세션 사용자 이름 기본값 설정 - 입고일 필드에 오늘 날짜 기본값 설정 - 등록 완료 후 목록 대신 생성된 입고 상세 페이지로 바로 이동 - 수입검사 저장 시 rendered_html 크기 제한 (500KB 초과 시 제외, 413 방지) - Dialog 접근성 경고 수정 (DialogDescription 추가)
This commit is contained in:
@@ -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 ? (
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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 || '등록에 실패했습니다.');
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user