fix(WEB): 폼 컴포넌트 DatePicker 적용 및 코드 정리
- ExpectedExpenseManagement DatePicker 적용 및 간소화 - BoardForm 날짜 필드 개선 - AttendanceInfoDialog, ReasonInfoDialog 코드 정리 - ReceivingDetail 기능 보강 - ShipmentCreate/Edit DatePicker 적용 - VehicleDispatchEdit 수정 - WorkOrderCreate 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,8 +42,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { TableRow, TableCell } from '@/components/ui/table';
|
||||
import {
|
||||
Select,
|
||||
@@ -142,7 +141,7 @@ export function ExpectedExpenseManagement({
|
||||
|
||||
// 예상 지급일 변경 다이얼로그
|
||||
const [showDateChangeDialog, setShowDateChangeDialog] = useState(false);
|
||||
const [newExpectedDate, setNewExpectedDate] = useState<Date | undefined>(undefined);
|
||||
const [newExpectedDate, setNewExpectedDate] = useState('');
|
||||
|
||||
// 삭제 다이얼로그
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
@@ -166,7 +165,6 @@ export function ExpectedExpenseManagement({
|
||||
paymentStatus: 'pending',
|
||||
note: '',
|
||||
});
|
||||
const [formExpectedDate, setFormExpectedDate] = useState<Date | undefined>(new Date());
|
||||
|
||||
// 거래처/계좌 옵션
|
||||
const [clientOptions, setClientOptions] = useState<{ id: string; name: string }[]>([]);
|
||||
@@ -207,7 +205,6 @@ export function ExpectedExpenseManagement({
|
||||
paymentStatus: 'pending',
|
||||
note: '',
|
||||
});
|
||||
setFormExpectedDate(new Date());
|
||||
setEditingItem(null);
|
||||
}, []);
|
||||
|
||||
@@ -232,7 +229,6 @@ export function ExpectedExpenseManagement({
|
||||
paymentStatus: item.paymentStatus,
|
||||
note: item.note,
|
||||
});
|
||||
setFormExpectedDate(item.expectedPaymentDate ? new Date(item.expectedPaymentDate) : new Date());
|
||||
setShowFormDialog(true);
|
||||
}, []);
|
||||
|
||||
@@ -520,22 +516,21 @@ export function ExpectedExpenseManagement({
|
||||
// ===== 예상 지급일 변경 핸들러 =====
|
||||
const handleOpenDateChangeDialog = useCallback(() => {
|
||||
if (selectedItems.size === 0) return;
|
||||
setNewExpectedDate(undefined);
|
||||
setNewExpectedDate('');
|
||||
setShowDateChangeDialog(true);
|
||||
}, [selectedItems.size]);
|
||||
|
||||
const handleConfirmDateChange = useCallback(async () => {
|
||||
if (!newExpectedDate || selectedItems.size === 0) return;
|
||||
|
||||
const newDateStr = format(newExpectedDate, 'yyyy-MM-dd');
|
||||
const selectedIds = Array.from(selectedItems);
|
||||
|
||||
startTransition(async () => {
|
||||
const result = await updateExpectedPaymentDate(selectedIds, newDateStr);
|
||||
const result = await updateExpectedPaymentDate(selectedIds, newExpectedDate);
|
||||
if (result.success) {
|
||||
setData(prev => prev.map(item =>
|
||||
selectedItems.has(item.id)
|
||||
? { ...item, expectedPaymentDate: newDateStr }
|
||||
? { ...item, expectedPaymentDate: newExpectedDate }
|
||||
: item
|
||||
));
|
||||
toast.success(`${result.updatedCount || selectedIds.length}건의 예상 지급일이 변경되었습니다.`);
|
||||
@@ -544,7 +539,7 @@ export function ExpectedExpenseManagement({
|
||||
toast.error(result.error || '예상 지급일 변경에 실패했습니다.');
|
||||
}
|
||||
setShowDateChangeDialog(false);
|
||||
setNewExpectedDate(undefined);
|
||||
setNewExpectedDate('');
|
||||
});
|
||||
}, [newExpectedDate, selectedItems]);
|
||||
|
||||
@@ -1063,24 +1058,11 @@ export function ExpectedExpenseManagement({
|
||||
{/* 예상 지급일 선택 */}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">변경할 예상 지급일</label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start text-left font-normal"
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{newExpectedDate ? format(newExpectedDate, 'yyyy-MM-dd') : '날짜 선택'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={newExpectedDate}
|
||||
onSelect={setNewExpectedDate}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<DatePicker
|
||||
value={newExpectedDate}
|
||||
onChange={setNewExpectedDate}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1113,29 +1095,11 @@ export function ExpectedExpenseManagement({
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>예상 지급일 *</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-full justify-start text-left font-normal"
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{formExpectedDate ? format(formExpectedDate, 'yyyy-MM-dd') : '날짜 선택'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={formExpectedDate}
|
||||
onSelect={(date) => {
|
||||
setFormExpectedDate(date);
|
||||
if (date) {
|
||||
setFormData(prev => ({ ...prev, expectedPaymentDate: format(date, 'yyyy-MM-dd') }));
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<DatePicker
|
||||
value={formData.expectedPaymentDate}
|
||||
onChange={(date) => setFormData(prev => ({ ...prev, expectedPaymentDate: date }))}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 거래유형 */}
|
||||
|
||||
@@ -166,8 +166,8 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
|
||||
}, [boardCode, title, content]);
|
||||
|
||||
// ===== 저장 핸들러 =====
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!validate()) return;
|
||||
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
|
||||
if (!validate()) return { success: false, error: '' };
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
@@ -186,20 +186,19 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
|
||||
result = await updatePost(boardCode, initialData.id, postData);
|
||||
}
|
||||
|
||||
if (result?.success) {
|
||||
toast.success(mode === 'create' ? '게시글이 등록되었습니다.' : '게시글이 수정되었습니다.');
|
||||
router.push('/ko/board');
|
||||
} else {
|
||||
toast.error(result?.error || '게시글 저장에 실패했습니다.');
|
||||
if (!result?.success) {
|
||||
return { success: false, error: result?.error || '게시글 저장에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('게시글 저장 오류:', error);
|
||||
toast.error('게시글 저장에 실패했습니다.');
|
||||
const errorMessage = error instanceof Error ? error.message : '게시글 저장에 실패했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [boardCode, title, content, isPinned, mode, initialData, router, validate]);
|
||||
}, [boardCode, title, content, isPinned, mode, initialData, validate]);
|
||||
|
||||
// ===== 취소 핸들러 =====
|
||||
const handleCancel = useCallback(() => {
|
||||
@@ -418,9 +417,8 @@ export function BoardForm({ mode, initialData }: BoardFormProps) {
|
||||
mode={mode}
|
||||
isLoading={isBoardsLoading}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={async (_data: Record<string, unknown>) => {
|
||||
await handleSubmit();
|
||||
return { success: true };
|
||||
onSubmit={async () => {
|
||||
return await handleSubmit();
|
||||
}}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
@@ -17,16 +17,8 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { format } from 'date-fns';
|
||||
import { ko } from 'date-fns/locale';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type {
|
||||
AttendanceInfoDialogProps,
|
||||
AttendanceFormData,
|
||||
@@ -59,7 +51,6 @@ export function AttendanceInfoDialog({
|
||||
onSave,
|
||||
}: AttendanceInfoDialogProps) {
|
||||
const [formData, setFormData] = useState<AttendanceFormData>(initialFormData);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
||||
|
||||
// 모드별 타이틀
|
||||
const title = mode === 'create' ? '근태 정보' : '근태 정보';
|
||||
@@ -82,10 +73,8 @@ export function AttendanceInfoDialog({
|
||||
weekendOvertimeHours: '0',
|
||||
weekendOvertimeMinutes: '0',
|
||||
});
|
||||
setSelectedDate(new Date(attendance.baseDate));
|
||||
} else if (open && mode === 'create') {
|
||||
setFormData(initialFormData);
|
||||
setSelectedDate(new Date());
|
||||
}
|
||||
}, [open, attendance, mode]);
|
||||
|
||||
@@ -94,14 +83,6 @@ export function AttendanceInfoDialog({
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// 날짜 변경 핸들러
|
||||
const handleDateChange = (date: Date | undefined) => {
|
||||
setSelectedDate(date);
|
||||
if (date) {
|
||||
setFormData(prev => ({ ...prev, baseDate: format(date, 'yyyy-MM-dd') }));
|
||||
}
|
||||
};
|
||||
|
||||
// 저장
|
||||
const handleSubmit = () => {
|
||||
onSave(formData);
|
||||
@@ -142,29 +123,12 @@ export function AttendanceInfoDialog({
|
||||
{/* 기준일 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium min-w-[80px]">기준일</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
'w-[200px] justify-start text-left font-normal',
|
||||
!selectedDate && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{selectedDate ? format(selectedDate, 'yyyy-MM-dd') : '날짜 선택'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="end">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={selectedDate}
|
||||
onSelect={handleDateChange}
|
||||
locale={ko}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<DatePicker
|
||||
value={formData.baseDate}
|
||||
onChange={(date) => handleChange('baseDate', date)}
|
||||
className="w-[200px]"
|
||||
align="end"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 출근 시간 */}
|
||||
|
||||
@@ -17,16 +17,8 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Calendar } from '@/components/ui/calendar';
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { CalendarIcon } from 'lucide-react';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
import { format } from 'date-fns';
|
||||
import { ko } from 'date-fns/locale';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type {
|
||||
ReasonInfoDialogProps,
|
||||
ReasonFormData,
|
||||
@@ -47,13 +39,11 @@ export function ReasonInfoDialog({
|
||||
onSubmit,
|
||||
}: ReasonInfoDialogProps) {
|
||||
const [formData, setFormData] = useState<ReasonFormData>(initialFormData);
|
||||
const [selectedDate, setSelectedDate] = useState<Date | undefined>(new Date());
|
||||
|
||||
// 데이터 초기화
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setFormData(initialFormData);
|
||||
setSelectedDate(new Date());
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
@@ -62,14 +52,6 @@ export function ReasonInfoDialog({
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
// 날짜 변경 핸들러
|
||||
const handleDateChange = (date: Date | undefined) => {
|
||||
setSelectedDate(date);
|
||||
if (date) {
|
||||
setFormData(prev => ({ ...prev, baseDate: format(date, 'yyyy-MM-dd') }));
|
||||
}
|
||||
};
|
||||
|
||||
// 등록 (문서 작성 화면으로 이동)
|
||||
const handleSubmit = () => {
|
||||
onSubmit(formData);
|
||||
@@ -107,29 +89,12 @@ export function ReasonInfoDialog({
|
||||
{/* 기준일 */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label className="text-sm font-medium min-w-[80px]">기준일</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className={cn(
|
||||
'w-[200px] justify-start text-left font-normal',
|
||||
!selectedDate && 'text-muted-foreground'
|
||||
)}
|
||||
>
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{selectedDate ? format(selectedDate, 'yyyy-MM-dd') : '날짜 선택'}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="end">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={selectedDate}
|
||||
onSelect={handleDateChange}
|
||||
locale={ko}
|
||||
initialFocus
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<DatePicker
|
||||
value={formData.baseDate}
|
||||
onChange={(date) => handleChange('baseDate', date)}
|
||||
className="w-[200px]"
|
||||
align="end"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 유형 선택 */}
|
||||
|
||||
@@ -227,31 +227,41 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
|
||||
}));
|
||||
};
|
||||
|
||||
// 저장 핸들러
|
||||
const handleSave = async () => {
|
||||
// 저장 핸들러 - IntegratedDetailTemplate의 onSubmit에서 호출
|
||||
// 반환값으로 성공/실패를 전달하여 템플릿이 toast/navigation 처리
|
||||
const handleSave = async (): Promise<{ success: boolean; error?: string }> => {
|
||||
// 클라이언트 사이드 필수 필드 검증
|
||||
const errors: string[] = [];
|
||||
if (!formData.itemCode) errors.push('품목코드');
|
||||
if (!formData.supplier) errors.push('발주처');
|
||||
if (!formData.receivingQty) errors.push('입고수량');
|
||||
if (!formData.receivingDate) errors.push('입고일');
|
||||
|
||||
if (errors.length > 0) {
|
||||
return { success: false, error: `필수 항목을 입력해주세요: ${errors.join(', ')}` };
|
||||
}
|
||||
|
||||
setIsSaving(true);
|
||||
try {
|
||||
if (isNewMode) {
|
||||
const result = await createReceiving(formData);
|
||||
if (result.success) {
|
||||
toast.success('입고가 등록되었습니다.');
|
||||
router.push('/ko/material/receiving-management');
|
||||
} else {
|
||||
toast.error(result.error || '등록에 실패했습니다.');
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '등록에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} else if (isEditMode) {
|
||||
const result = await updateReceiving(id, formData);
|
||||
if (result.success) {
|
||||
toast.success('입고 정보가 수정되었습니다.');
|
||||
router.push(`/ko/material/receiving-management/${id}?mode=view`);
|
||||
} else {
|
||||
toast.error(result.error || '수정에 실패했습니다.');
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '수정에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
return { success: false, error: '알 수 없는 모드입니다.' };
|
||||
} catch (err) {
|
||||
if (isNextRedirectError(err)) throw err;
|
||||
console.error('[ReceivingDetail] handleSave error:', err);
|
||||
toast.error('저장 중 오류가 발생했습니다.');
|
||||
const errorMessage = err instanceof Error ? err.message : '저장 중 오류가 발생했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSaving(false);
|
||||
}
|
||||
@@ -754,8 +764,7 @@ export function ReceivingDetail({ id, mode = 'view' }: Props) {
|
||||
renderView={() => renderViewContent()}
|
||||
renderForm={() => renderFormContent()}
|
||||
onSubmit={async () => {
|
||||
await handleSave();
|
||||
return { success: true };
|
||||
return await handleSave();
|
||||
}}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
|
||||
@@ -296,25 +296,25 @@ export function ShipmentCreate() {
|
||||
return errors.length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!validateForm()) return;
|
||||
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
|
||||
if (!validateForm()) return { success: false, error: '' };
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const result = await createShipment(formData);
|
||||
if (result.success) {
|
||||
router.push('/ko/outbound/shipments');
|
||||
} else {
|
||||
setValidationErrors([result.error || '출고 등록에 실패했습니다.']);
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '출고 등록에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
if (isNextRedirectError(err)) throw err;
|
||||
console.error('[ShipmentCreate] handleSubmit error:', err);
|
||||
setValidationErrors(['저장 중 오류가 발생했습니다.']);
|
||||
const errorMessage = err instanceof Error ? err.message : '저장 중 오류가 발생했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [formData, router]);
|
||||
}, [formData]);
|
||||
|
||||
// 제품 부품 테이블 렌더링
|
||||
const renderPartsTable = (parts: ProductPart[]) => (
|
||||
@@ -762,9 +762,8 @@ export function ShipmentCreate() {
|
||||
mode="create"
|
||||
isLoading={isLoading}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={async (_data: Record<string, unknown>) => {
|
||||
await handleSubmit();
|
||||
return { success: true };
|
||||
onSubmit={async () => {
|
||||
return await handleSubmit();
|
||||
}}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
@@ -295,25 +295,25 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
|
||||
return errors.length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
if (!validateForm()) return;
|
||||
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
|
||||
if (!validateForm()) return { success: false, error: '' };
|
||||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const result = await updateShipment(id, formData);
|
||||
if (result.success) {
|
||||
router.push(`/ko/outbound/shipments/${id}?mode=view`);
|
||||
} else {
|
||||
setValidationErrors([result.error || '출고 수정에 실패했습니다.']);
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '출고 수정에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
if (isNextRedirectError(err)) throw err;
|
||||
console.error('[ShipmentEdit] handleSubmit error:', err);
|
||||
setValidationErrors(['저장 중 오류가 발생했습니다.']);
|
||||
const errorMessage = err instanceof Error ? err.message : '저장 중 오류가 발생했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [id, formData, router]);
|
||||
}, [id, formData]);
|
||||
|
||||
// 제품 부품 테이블 렌더링
|
||||
const renderPartsTable = (parts: ProductPart[]) => (
|
||||
@@ -781,9 +781,8 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
|
||||
mode="edit"
|
||||
isLoading={isLoading}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={async (_data: Record<string, unknown>) => {
|
||||
await handleSubmit();
|
||||
return { success: true };
|
||||
onSubmit={async () => {
|
||||
return await handleSubmit();
|
||||
}}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '@/components/ui/select';
|
||||
import { IntegratedDetailTemplate } from '@/components/templates/IntegratedDetailTemplate';
|
||||
import { vehicleDispatchEditConfig } from './vehicleDispatchConfig';
|
||||
import { isNextRedirectError } from '@/lib/utils/redirect-error';
|
||||
import { getVehicleDispatchById, updateVehicleDispatch } from './actions';
|
||||
import {
|
||||
VEHICLE_DISPATCH_STATUS_LABELS,
|
||||
@@ -135,22 +136,23 @@ export function VehicleDispatchEdit({ id }: VehicleDispatchEditProps) {
|
||||
router.push(`/ko/outbound/vehicle-dispatches/${id}?mode=view`);
|
||||
}, [router, id]);
|
||||
|
||||
const handleSubmit = useCallback(async () => {
|
||||
const handleSubmit = useCallback(async (): Promise<{ success: boolean; error?: string }> => {
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
const result = await updateVehicleDispatch(id, formData);
|
||||
if (result.success) {
|
||||
router.push(`/ko/outbound/vehicle-dispatches/${id}?mode=view`);
|
||||
} else {
|
||||
setValidationErrors([result.error || '배차차량 수정에 실패했습니다.']);
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '배차차량 수정에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (err) {
|
||||
if (isNextRedirectError(err)) throw err;
|
||||
console.error('[VehicleDispatchEdit] handleSubmit error:', err);
|
||||
setValidationErrors(['저장 중 오류가 발생했습니다.']);
|
||||
const errorMessage = err instanceof Error ? err.message : '저장 중 오류가 발생했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}, [id, formData, router]);
|
||||
}, [id, formData]);
|
||||
|
||||
// 동적 config
|
||||
const dynamicConfig = {
|
||||
@@ -387,9 +389,8 @@ export function VehicleDispatchEdit({ id }: VehicleDispatchEditProps) {
|
||||
mode="edit"
|
||||
isLoading={isLoading}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={async (_data: Record<string, unknown>) => {
|
||||
await handleSubmit();
|
||||
return { success: true };
|
||||
onSubmit={async () => {
|
||||
return await handleSubmit();
|
||||
}}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
@@ -170,7 +170,7 @@ export function WorkOrderCreate() {
|
||||
};
|
||||
|
||||
// 폼 제출
|
||||
const handleSubmit = async () => {
|
||||
const handleSubmit = async (): Promise<{ success: boolean; error?: string }> => {
|
||||
// Validation 체크
|
||||
const errors: ValidationErrors = {};
|
||||
|
||||
@@ -200,7 +200,7 @@ export function WorkOrderCreate() {
|
||||
setValidationErrors(errors);
|
||||
// 페이지 상단으로 스크롤
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
return;
|
||||
return { success: false, error: '' };
|
||||
}
|
||||
|
||||
// 에러 초기화
|
||||
@@ -218,16 +218,15 @@ export function WorkOrderCreate() {
|
||||
note: formData.note || undefined,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
toast.success('작업지시가 등록되었습니다.');
|
||||
router.push('/production/work-orders');
|
||||
} else {
|
||||
toast.error(result.error || '작업지시 등록에 실패했습니다.');
|
||||
if (!result.success) {
|
||||
return { success: false, error: result.error || '작업지시 등록에 실패했습니다.' };
|
||||
}
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (isNextRedirectError(error)) throw error;
|
||||
console.error('[WorkOrderCreate] handleSubmit error:', error);
|
||||
toast.error('작업지시 등록 중 오류가 발생했습니다.');
|
||||
const errorMessage = error instanceof Error ? error.message : '작업지시 등록 중 오류가 발생했습니다.';
|
||||
return { success: false, error: errorMessage };
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
@@ -518,9 +517,8 @@ export function WorkOrderCreate() {
|
||||
mode="create"
|
||||
isLoading={isLoadingProcesses}
|
||||
onCancel={handleCancel}
|
||||
onSubmit={async (_data: Record<string, unknown>) => {
|
||||
await handleSubmit();
|
||||
return { success: true };
|
||||
onSubmit={async () => {
|
||||
return await handleSubmit();
|
||||
}}
|
||||
renderForm={renderFormContent}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user