feat(WEB): IntegratedDetailTemplate 통합 템플릿 구현 및 Phase 1~8 마이그레이션

- Phase 1: 기안함(DocumentCreate) 마이그레이션
- Phase 2: 작업지시(WorkOrderCreate/Edit) 마이그레이션
- Phase 3: 출하(ShipmentCreate/Edit) 마이그레이션
- Phase 4: 사원(EmployeeForm) 마이그레이션
- Phase 5: 게시판(BoardForm) 마이그레이션
- Phase 6: 1:1문의(InquiryForm) 마이그레이션
- Phase 7: 공정(ProcessForm) 마이그레이션
- Phase 8: 수입검사/품질검사(InspectionCreate) 마이그레이션
- DetailActions에 showSave 옵션 추가
- 각 도메인별 config 파일 생성

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-20 19:31:07 +09:00
parent 6b0ffc810b
commit 62ef2b1ff9
24 changed files with 861 additions and 534 deletions

View File

@@ -1,8 +1,8 @@
'use client';
import { useState, useEffect } from 'react';
import { format, differenceInDays } from 'date-fns';
import { CalendarIcon, Loader2 } from 'lucide-react';
import { differenceInDays, parseISO } from 'date-fns';
import { Loader2 } from 'lucide-react';
import {
Dialog,
DialogContent,
@@ -11,13 +11,8 @@ import {
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Calendar } from '@/components/ui/calendar';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import {
Select,
SelectContent,
@@ -25,7 +20,6 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { cn } from '@/lib/utils';
import type { VacationRequestFormData, LeaveType } from './types';
import { LEAVE_TYPE_LABELS } from './types';
import { getActiveEmployees, type EmployeeOption } from './actions';
@@ -48,8 +42,6 @@ export function VacationRequestDialog({
endDate: '',
vacationDays: 1,
});
const [startDate, setStartDate] = useState<Date | undefined>();
const [endDate, setEndDate] = useState<Date | undefined>();
const [employees, setEmployees] = useState<EmployeeOption[]>([]);
const [isLoadingEmployees, setIsLoadingEmployees] = useState(false);
@@ -76,33 +68,31 @@ export function VacationRequestDialog({
endDate: '',
vacationDays: 1,
});
setStartDate(undefined);
setEndDate(undefined);
}
}, [open]);
// 날짜 변경 시 휴가 일수 자동 계산
useEffect(() => {
if (startDate && endDate) {
const days = differenceInDays(endDate, startDate) + 1;
setFormData(prev => ({
...prev,
startDate: format(startDate, 'yyyy-MM-dd'),
endDate: format(endDate, 'yyyy-MM-dd'),
vacationDays: days > 0 ? days : 1,
}));
if (formData.startDate && formData.endDate) {
const start = parseISO(formData.startDate);
const end = parseISO(formData.endDate);
const days = differenceInDays(end, start) + 1;
if (days > 0 && days !== formData.vacationDays) {
setFormData(prev => ({ ...prev, vacationDays: days }));
}
}
}, [startDate, endDate]);
}, [formData.startDate, formData.endDate, formData.vacationDays]);
const handleSave = () => {
if (!formData.employeeId) {
alert('사원을 선택해주세요.');
return;
}
if (!startDate || !endDate) {
if (!formData.startDate || !formData.endDate) {
alert('휴가 기간을 선택해주세요.');
return;
}
if (endDate < startDate) {
if (formData.endDate < formData.startDate) {
alert('종료일은 시작일 이후여야 합니다.');
return;
}
@@ -174,61 +164,29 @@ export function VacationRequestDialog({
{/* 시작일 */}
<div className="grid gap-2">
<Label></Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
'w-full justify-start text-left font-normal',
!startDate && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{startDate ? format(startDate, 'yyyy-MM-dd') : '시작일 선택'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={startDate}
onSelect={setStartDate}
initialFocus
/>
</PopoverContent>
</Popover>
<Label htmlFor="startDate"></Label>
<Input
id="startDate"
type="date"
value={formData.startDate}
onChange={(e) => setFormData(prev => ({ ...prev, startDate: e.target.value }))}
/>
</div>
{/* 종료일 */}
<div className="grid gap-2">
<Label></Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
'w-full justify-start text-left font-normal',
!endDate && 'text-muted-foreground'
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{endDate ? format(endDate, 'yyyy-MM-dd') : '종료일 선택'}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={endDate}
onSelect={setEndDate}
disabled={(date) => startDate ? date < startDate : false}
initialFocus
/>
</PopoverContent>
</Popover>
<Label htmlFor="endDate"></Label>
<Input
id="endDate"
type="date"
value={formData.endDate}
min={formData.startDate || undefined}
onChange={(e) => setFormData(prev => ({ ...prev, endDate: e.target.value }))}
/>
</div>
{/* 휴가 일수 (자동 계산) */}
{startDate && endDate && (
{formData.startDate && formData.endDate && (
<div className="grid gap-2">
<Label> </Label>
<div className="p-3 bg-muted rounded-md text-center font-medium">