feat: 공정관리 페이지 및 컴포넌트 추가

- 공정관리 목록/상세/등록/수정 페이지 구현
- ProcessListClient, ProcessDetail, ProcessForm 컴포넌트 추가
- ProcessWorkLogPreviewModal, RuleModal 추가
- MobileCard 공통 컴포넌트 추가
- WorkLogModal.tsx 개선
- .gitignore 업데이트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2025-12-26 15:48:08 +09:00
parent 41ef0bdd86
commit f0c0de2ecd
14 changed files with 1801 additions and 18 deletions

View File

@@ -0,0 +1,99 @@
'use client';
import { ReactNode } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Checkbox } from '@/components/ui/checkbox';
import { cn } from '@/lib/utils';
interface MobileCardDetail {
label: string;
value: string | ReactNode;
}
interface MobileCardProps {
title: string;
subtitle?: string;
description?: string;
badge?: string;
badgeVariant?: 'default' | 'secondary' | 'destructive' | 'outline';
isSelected?: boolean;
onToggle?: () => void;
onClick?: () => void;
details?: MobileCardDetail[];
actions?: ReactNode;
className?: string;
}
export function MobileCard({
title,
subtitle,
description,
badge,
badgeVariant = 'default',
isSelected = false,
onToggle,
onClick,
details = [],
actions,
className,
}: MobileCardProps) {
return (
<Card
className={cn(
'cursor-pointer transition-colors hover:bg-muted/50',
isSelected && 'ring-2 ring-primary',
className
)}
onClick={onClick}
>
<CardContent className="p-4">
<div className="flex items-start gap-3">
{onToggle && (
<div onClick={(e) => e.stopPropagation()}>
<Checkbox checked={isSelected} onCheckedChange={onToggle} />
</div>
)}
<div className="flex-1 min-w-0">
{/* 헤더 */}
<div className="flex items-start justify-between gap-2 mb-2">
<div>
<div className="font-medium truncate">{title}</div>
{subtitle && (
<div className="text-sm text-muted-foreground">{subtitle}</div>
)}
</div>
{badge && <Badge variant={badgeVariant}>{badge}</Badge>}
</div>
{/* 설명 */}
{description && (
<p className="text-sm text-muted-foreground mb-3 line-clamp-2">
{description}
</p>
)}
{/* 상세 정보 */}
{details.length > 0 && (
<div className="grid grid-cols-2 gap-2 text-sm">
{details.map((detail, index) => (
<div key={index}>
<span className="text-muted-foreground">{detail.label}: </span>
<span className="font-medium">{detail.value}</span>
</div>
))}
</div>
)}
{/* 액션 */}
{actions && (
<div className="mt-3 pt-3 border-t" onClick={(e) => e.stopPropagation()}>
{actions}
</div>
)}
</div>
</div>
</CardContent>
</Card>
);
}