feat: 모바일 반응형 UI 개선 및 공휴일/일정 시스템 통합

- MobileCard 접기/펼치기(collapsible) 기능 추가 및 반응형 레이아웃 개선
- DatePicker 공휴일/세무일정 색상 코딩 통합, DateTimePicker 신규 추가
- useCalendarScheduleInit 훅으로 전역 공휴일/일정 데이터 캐싱
- 전 도메인 날짜 필드 DatePicker 표준화 (104 files)
- 생산대시보드/작업지시 모바일 호환성 강화
- 견적서/주문관리 반응형 그리드 적용
- 회계 모듈 기능 개선 (매입상세 결재연동, 미수금현황 조회조건 등)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-26 21:27:40 +09:00
parent 2777ecf664
commit b1686aaf66
107 changed files with 1703 additions and 970 deletions

View File

@@ -7,9 +7,10 @@
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Plus, X as XIcon, ChevronDown, Search } from 'lucide-react';
import { Plus, Trash2, ChevronDown, Search } from 'lucide-react';
import { getTodayString } from '@/lib/utils/date';
import { Input } from '@/components/ui/input';
import { DateTimePicker } from '@/components/ui/date-time-picker';
import { DatePicker } from '@/components/ui/date-picker';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
@@ -378,7 +379,7 @@ export function ShipmentCreate() {
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="grid grid-cols-1 gap-4">
{/* 출고번호 - 자동생성 */}
<div>
<div className="text-sm text-muted-foreground mb-1"></div>
@@ -588,11 +589,10 @@ export function ShipmentCreate() {
</Select>
</TableCell>
<TableCell className="p-1">
<Input
type="datetime-local"
<DateTimePicker
value={dispatch.arrivalDateTime}
onChange={(e) => handleDispatchChange(index, 'arrivalDateTime', e.target.value)}
className="h-8"
onChange={(val) => handleDispatchChange(index, 'arrivalDateTime', val)}
size="sm"
disabled={isSubmitting}
/>
</TableCell>
@@ -651,7 +651,7 @@ export function ShipmentCreate() {
onClick={() => handleRemoveDispatch(index)}
disabled={isSubmitting}
>
<XIcon className="w-4 h-4 text-red-500" />
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
)}
</TableCell>

View File

@@ -38,6 +38,7 @@ import {
DialogHeader,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { DateTimePicker } from '@/components/ui/date-time-picker';
import { Label } from '@/components/ui/label';
import { PhoneInput } from '@/components/ui/phone-input';
import { DocumentViewer } from '@/components/document-system/viewer/DocumentViewer';
@@ -327,7 +328,7 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="grid grid-cols-1 gap-4">
{renderInfoField('로트번호', detail.lotNo)}
{renderInfoField('현장명', detail.siteName)}
{renderInfoField('수주처', detail.customerName)}
@@ -577,12 +578,10 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
{targetStatus === 'ready' && (
<div className="space-y-2">
<Label htmlFor="loadingTime"> ()</Label>
<Input
id="loadingTime"
type="datetime-local"
<DateTimePicker
value={statusFormData.loadingTime}
onChange={(e) =>
setStatusFormData((prev) => ({ ...prev, loadingTime: e.target.value }))
onChange={(val) =>
setStatusFormData((prev) => ({ ...prev, loadingTime: val }))
}
/>
</div>
@@ -629,12 +628,10 @@ export function ShipmentDetail({ id }: ShipmentDetailProps) {
{targetStatus === 'completed' && (
<div className="space-y-2">
<Label htmlFor="confirmedArrival"> ()</Label>
<Input
id="confirmedArrival"
type="datetime-local"
<DateTimePicker
value={statusFormData.confirmedArrival}
onChange={(e) =>
setStatusFormData((prev) => ({ ...prev, confirmedArrival: e.target.value }))
onChange={(val) =>
setStatusFormData((prev) => ({ ...prev, confirmedArrival: val }))
}
/>
</div>

View File

@@ -7,8 +7,9 @@
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Plus, X as XIcon, ChevronDown, Search } from 'lucide-react';
import { Plus, Trash2, ChevronDown, Search } from 'lucide-react';
import { Input } from '@/components/ui/input';
import { DateTimePicker } from '@/components/ui/date-time-picker';
import { DatePicker } from '@/components/ui/date-picker';
import { Label } from '@/components/ui/label';
import { Button } from '@/components/ui/button';
@@ -390,7 +391,7 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
<CardTitle className="text-base"> </CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
<div className="grid grid-cols-1 gap-4">
<div className="space-y-1">
<Label className="text-muted-foreground"></Label>
<div className="font-medium">{detail.lotNo}</div>
@@ -587,11 +588,10 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
</Select>
</TableCell>
<TableCell className="p-1">
<Input
type="datetime-local"
<DateTimePicker
value={dispatch.arrivalDateTime}
onChange={(e) => handleDispatchChange(index, 'arrivalDateTime', e.target.value)}
className="h-8"
onChange={(val) => handleDispatchChange(index, 'arrivalDateTime', val)}
size="sm"
disabled={isSubmitting}
/>
</TableCell>
@@ -650,7 +650,7 @@ export function ShipmentEdit({ id }: ShipmentEditProps) {
onClick={() => handleRemoveDispatch(index)}
disabled={isSubmitting}
>
<XIcon className="w-4 h-4 text-red-500" />
<Trash2 className="w-4 h-4 text-red-500" />
</Button>
)}
</TableCell>

View File

@@ -335,7 +335,7 @@ export function ShipmentList() {
headerBadges={
<>
<Badge variant="outline" className="text-xs">
#{globalIndex}
{globalIndex}
</Badge>
<Badge variant="outline" className="text-xs">
{item.shipmentNo}

View File

@@ -8,6 +8,7 @@
import { useState, useCallback, useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { Input } from '@/components/ui/input';
import { DateTimePicker } from '@/components/ui/date-time-picker';
import { Label } from '@/components/ui/label';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -266,10 +267,9 @@ export function VehicleDispatchEdit({ id }: VehicleDispatchEditProps) {
</div>
<div className="space-y-2">
<Label></Label>
<Input
type="datetime-local"
<DateTimePicker
value={formData.arrivalDateTime}
onChange={(e) => handleInputChange('arrivalDateTime', e.target.value)}
onChange={(val) => handleInputChange('arrivalDateTime', val)}
disabled={isSubmitting}
/>
</div>

View File

@@ -271,7 +271,7 @@ export function VehicleDispatchList() {
headerBadges={
<>
<Badge variant="outline" className="text-xs">
#{globalIndex}
{globalIndex}
</Badge>
<Badge variant="outline" className="text-xs">
{item.dispatchNo}