feat(WEB): DatePicker 공통화 및 공정관리/작업자화면 대폭 개선

DatePicker 공통화:
- date-picker.tsx 공통 컴포넌트 신규 추가
- 전체 폼 컴포넌트 DatePicker 통일 적용 (50+ 파일)
- DateRangeSelector 개선

공정관리:
- RuleModal 대폭 리팩토링 (-592줄 → 간소화)
- ProcessForm, StepForm 개선
- ProcessDetail 수정, actions 확장

작업자화면:
- WorkerScreen 기능 대폭 확장 (+543줄)
- WorkItemCard 개선
- types 확장

회계/인사/영업/품질:
- BadDebtDetail, BillDetail, DepositDetail, SalesDetail 등 DatePicker 적용
- EmployeeForm, VacationDialog 등 DatePicker 적용
- OrderRegistration, QuoteRegistration DatePicker 적용
- InspectionCreate, InspectionDetail DatePicker 적용

공사관리/CEO대시보드:
- BiddingDetail, ContractDetail, HandoverReport 등 DatePicker 적용
- ScheduleDetailModal, TodayIssueSection 개선

기타:
- WorkOrderCreate/Edit/Detail/List 개선
- ShipmentCreate/Edit, ReceivingDetail 개선
- calendar, calendarEvents 수정
- datepicker 마이그레이션 체크리스트 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-06 15:48:00 +09:00
parent e453753bdd
commit c2ed71540f
68 changed files with 1436 additions and 1134 deletions

View File

@@ -11,6 +11,7 @@ import { useRouter } from 'next/navigation';
import { ArrowLeft, FileText, X, Edit2, Loader2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { DatePicker } from '@/components/ui/date-picker';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
@@ -455,11 +456,9 @@ export function WorkOrderCreate() {
<div className="space-y-2">
<Label> *</Label>
<Input
type="date"
<DatePicker
value={formData.shipmentDate}
onChange={(e) => setFormData({ ...formData, shipmentDate: e.target.value })}
className="bg-white"
onChange={(date) => setFormData({ ...formData, shipmentDate: date })}
/>
</div>

View File

@@ -369,7 +369,7 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
<div className="bg-amber-50 border border-amber-200 rounded-lg p-4 md:p-6">
<h3 className="font-semibold mb-4"> </h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-x-4 md:gap-x-6 gap-y-4">
{/* 1행: 작업번호 | 수주일 | 공정구분 | 로트번호 */}
{/* 1행: 작업번호 | 수주일 | 공정 | 구분 */}
<div className="min-w-0">
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium truncate">{order.workOrderNo}</p>
@@ -379,15 +379,19 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
<p className="font-medium">{order.salesOrderDate || '-'}</p>
</div>
<div className="min-w-0">
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">{order.processName}</p>
</div>
<div className="min-w-0">
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">-</p>
</div>
{/* 2행: 로트번호 | 수주처 | 현장명 | 수주 담당자 */}
<div className="min-w-0">
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium truncate">{order.lotNo}</p>
</div>
{/* 2행: 수주처 | 현장명 | 수주 담당자 | 담당자 연락처 */}
<div>
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">{order.client}</p>
@@ -400,12 +404,12 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
<p className="text-sm text-muted-foreground mb-1"> </p>
<p className="font-medium">-</p>
</div>
{/* 3행: 담당자 연락처 | 출고예정일 | 틀수 | 우선순위 */}
<div>
<p className="text-sm text-muted-foreground mb-1"> </p>
<p className="font-medium">-</p>
</div>
{/* 3행: 출고예정일 | 틀수 | 우선순위 | 부서 */}
<div>
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">{order.shipmentDate || '-'}</p>
@@ -418,12 +422,12 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">{order.priorityLabel || '-'}</p>
</div>
{/* 4행: 부서 | 생산 담당자 | 상태 | 비고 */}
<div>
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium">{order.department || '-'}</p>
</div>
{/* 4행: 생산 담당자 | 상태 | 비고 (colspan 2) */}
<div>
<p className="text-sm text-muted-foreground mb-1"> </p>
<p className="font-medium">
@@ -438,7 +442,7 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
{WORK_ORDER_STATUS_LABELS[order.status]}
</Badge>
</div>
<div className="col-span-2">
<div>
<p className="text-sm text-muted-foreground mb-1"></p>
<p className="font-medium whitespace-pre-wrap">{order.note || '-'}</p>
</div>

View File

@@ -11,6 +11,7 @@ import { useState, useEffect, useCallback } from 'react';
import { useRouter } from 'next/navigation';
import { Pencil, Trash2 } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { DatePicker } from '@/components/ui/date-picker';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Button } from '@/components/ui/button';
@@ -365,7 +366,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
<section className="bg-amber-50 border border-amber-200 rounded-lg p-6">
<h3 className="font-semibold mb-4"> </h3>
<div className="grid grid-cols-4 gap-x-6 gap-y-4">
{/* 1행: 작업번호(읽기) | 수주일(읽기) | 공정구분(셀렉트) | 로트번호(읽기) */}
{/* 1행: 작업번호(읽기) | 수주일(읽기) | 공정(셀렉트) | 구분(읽기) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Input value={workOrder?.workOrderNo || '-'} disabled className="bg-muted" />
@@ -375,7 +376,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
<Input value={workOrder?.salesOrderDate || '-'} disabled className="bg-muted" />
</div>
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"> *</Label>
<Label className="text-sm text-muted-foreground"> *</Label>
<Select
value={formData.processId?.toString() || ''}
onValueChange={(value) => setFormData({ ...formData, processId: parseInt(value) })}
@@ -393,12 +394,16 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
</SelectContent>
</Select>
</div>
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Input value="-" disabled className="bg-muted" />
</div>
{/* 2행: 로트번호(읽기) | 수주처(읽기) | 현장명(입력) | 수주 담당자(읽기) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Input value={formData.orderNo || '-'} disabled className="bg-muted" />
</div>
{/* 2행: 수주처(읽기) | 현장명(입력) | 수주 담당자(읽기) | 담당자 연락처(읽기) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Input value={formData.client} disabled className="bg-muted" />
@@ -415,19 +420,17 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
<Label className="text-sm text-muted-foreground"> </Label>
<Input value="-" disabled className="bg-muted" />
</div>
{/* 3행: 담당자 연락처(읽기) | 출고예정일(입력) | 틀수(읽기) | 우선순위(셀렉트) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"> </Label>
<Input value="-" disabled className="bg-muted" />
</div>
{/* 3행: 출고예정일(입력) | 틀수(읽기) | 우선순위(셀렉트) | 부서(읽기) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"> *</Label>
<Input
type="date"
<DatePicker
value={formData.scheduledDate}
onChange={(e) => setFormData({ ...formData, scheduledDate: e.target.value })}
className="bg-white"
onChange={(date) => setFormData({ ...formData, scheduledDate: date })}
/>
</div>
<div className="space-y-1">
@@ -452,12 +455,12 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
</SelectContent>
</Select>
</div>
{/* 4행: 부서(읽기) | 생산 담당자(선택) | 상태(읽기) | 비고(입력) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Input value={workOrder?.department || '-'} disabled className="bg-muted" />
</div>
{/* 4행: 생산 담당자(선택) | 상태(읽기) | 비고(입력, colspan 2) */}
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"> </Label>
<div
@@ -475,7 +478,7 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
<Label className="text-sm text-muted-foreground"></Label>
<Input value={workOrder ? (workOrder.status === 'waiting' ? '작업대기' : workOrder.status === 'in_progress' ? '작업중' : workOrder.status === 'completed' ? '작업완료' : workOrder.status) : '-'} disabled className="bg-muted" />
</div>
<div className="space-y-1 col-span-2">
<div className="space-y-1">
<Label className="text-sm text-muted-foreground"></Label>
<Textarea
value={formData.note}

View File

@@ -288,6 +288,7 @@ export function WorkOrderList() {
{ key: 'client', label: '수주처', className: 'min-w-[120px]' },
{ key: 'projectName', label: '현장명', className: 'min-w-[150px]' },
{ key: 'shutterCount', label: '틀수', className: 'w-[70px] text-center' },
{ key: 'category', label: '구분', className: 'w-[80px]' },
{ key: 'status', label: '상태', className: 'w-[90px]' },
{ key: 'priority', label: '우선순위', className: 'w-[80px]' },
{ key: 'department', label: '부서', className: 'w-[90px]' },
@@ -376,6 +377,7 @@ export function WorkOrderList() {
<TableCell>{item.client}</TableCell>
<TableCell className="max-w-[200px] truncate">{item.projectName}</TableCell>
<TableCell className="text-center">{item.shutterCount ?? '-'}</TableCell>
<TableCell>{'-'}</TableCell>
<TableCell>
<Badge className={`${WORK_ORDER_STATUS_COLORS[item.status]} border-0`}>
{WORK_ORDER_STATUS_LABELS[item.status]}