feat(WEB): 생산 대시보드 최근 완료 작업 카드 추가
- 4컬럼 레이아웃으로 변경 (긴급/지연/완료/작업자) - recentCompletedOrders 필터 추가 (최신 5건) - WorkOrderCard에 showCompleted 옵션 추가
This commit is contained in:
@@ -106,7 +106,7 @@ export default function ProductionDashboard() {
|
||||
// ===== 필터링된 데이터 (탭에서 이미 필터링됨) =====
|
||||
const filteredOrders = workOrders;
|
||||
|
||||
// ===== 긴급/지연 작업 필터링 =====
|
||||
// ===== 긴급/지연/완료 작업 필터링 =====
|
||||
const urgentOrders = useMemo(
|
||||
() => filteredOrders.filter((o) => o.isUrgent).slice(0, 5),
|
||||
[filteredOrders]
|
||||
@@ -115,6 +115,14 @@ export default function ProductionDashboard() {
|
||||
() => filteredOrders.filter((o) => o.isDelayed).slice(0, 5),
|
||||
[filteredOrders]
|
||||
);
|
||||
// 최근 완료 작업 (createdAt 기준 정렬, 최신 5건)
|
||||
const recentCompletedOrders = useMemo(
|
||||
() => filteredOrders
|
||||
.filter((o) => o.status === 'completed')
|
||||
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||
.slice(0, 5),
|
||||
[filteredOrders]
|
||||
);
|
||||
|
||||
// ===== 핸들러 =====
|
||||
const handleOrderClick = (id: string) => {
|
||||
@@ -212,8 +220,8 @@ export default function ProductionDashboard() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 3컬럼 레이아웃 */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* 4컬럼 레이아웃 */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
||||
{/* 긴급 작업 */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
@@ -271,6 +279,35 @@ export default function ProductionDashboard() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 최근 완료 작업 */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-500" />
|
||||
최근 완료
|
||||
<Badge className="ml-auto bg-green-100 text-green-800 hover:bg-green-100">
|
||||
{stats.completed}
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2">
|
||||
{recentCompletedOrders.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground text-center py-4">
|
||||
완료된 작업이 없습니다.
|
||||
</p>
|
||||
) : (
|
||||
recentCompletedOrders.map((order) => (
|
||||
<WorkOrderCard
|
||||
key={order.id}
|
||||
order={order}
|
||||
onClick={() => handleOrderClick(order.id)}
|
||||
showCompleted
|
||||
/>
|
||||
))
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 작업자별 현황 */}
|
||||
<Card>
|
||||
<CardHeader className="pb-3">
|
||||
@@ -328,15 +365,23 @@ interface WorkOrderCardProps {
|
||||
order: WorkOrder;
|
||||
onClick: () => void;
|
||||
showDelay?: boolean;
|
||||
showCompleted?: boolean;
|
||||
}
|
||||
|
||||
function WorkOrderCard({ order, onClick, showDelay }: WorkOrderCardProps) {
|
||||
function WorkOrderCard({ order, onClick, showDelay, showCompleted }: WorkOrderCardProps) {
|
||||
const statusColors = {
|
||||
waiting: 'bg-gray-100 text-gray-800',
|
||||
inProgress: 'bg-blue-100 text-blue-800',
|
||||
completed: 'bg-green-100 text-green-800',
|
||||
};
|
||||
|
||||
// 완료일 포맷팅 (createdAt을 완료일로 사용 - 실제로는 완료 시점의 timestamp 필요)
|
||||
const formatDate = (dateStr: string) => {
|
||||
if (!dateStr) return '';
|
||||
const date = new Date(dateStr);
|
||||
return `${date.getMonth() + 1}/${date.getDate()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
@@ -348,9 +393,11 @@ function WorkOrderCard({ order, onClick, showDelay }: WorkOrderCardProps) {
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{order.orderNo}
|
||||
</span>
|
||||
<Badge className={`text-xs ${statusColors[order.status]}`}>
|
||||
{STATUS_LABELS[order.status]}
|
||||
</Badge>
|
||||
{!showCompleted && (
|
||||
<Badge className={`text-xs ${statusColors[order.status]}`}>
|
||||
{STATUS_LABELS[order.status]}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm font-medium mt-1 truncate">{order.productName}</p>
|
||||
<p className="text-xs text-muted-foreground truncate">{order.client}</p>
|
||||
@@ -365,12 +412,15 @@ function WorkOrderCard({ order, onClick, showDelay }: WorkOrderCardProps) {
|
||||
{showDelay && order.delayDays && (
|
||||
<p className="text-xs text-orange-600 mt-1">+{order.delayDays}일 지연</p>
|
||||
)}
|
||||
{order.isUrgent && !showDelay && (
|
||||
{showCompleted && (
|
||||
<p className="text-xs text-green-600 mt-1">{formatDate(order.createdAt)}</p>
|
||||
)}
|
||||
{order.isUrgent && !showDelay && !showCompleted && (
|
||||
<p className="text-xs text-muted-foreground mt-1">순위 {order.priority}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{order.instruction && (
|
||||
{order.instruction && !showCompleted && (
|
||||
<p className="text-xs text-muted-foreground mt-2 truncate">
|
||||
{order.instruction}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user