feat(WEB): 공정 관리 UI 개선

- ProcessDetail: 공정 상세 정보 표시 개선
- ProcessForm: 공정 등록/수정 폼 유효성 검사 강화
- RuleModal: 공정 규칙 설정 모달 리팩토링
This commit is contained in:
2025-12-30 17:21:38 +09:00
parent 68babd54be
commit 62bf081adb
3 changed files with 171 additions and 72 deletions

View File

@@ -1,14 +1,15 @@
'use client';
import { useState } from 'react';
import { useState, useMemo } from 'react';
import { useRouter } from 'next/navigation';
import { List, Edit, Wrench } from 'lucide-react';
import { List, Edit, Wrench, Package } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { PageLayout } from '@/components/organisms/PageLayout';
import { ProcessWorkLogPreviewModal } from './ProcessWorkLogPreviewModal';
import type { Process } from '@/types/process';
import { MATCHING_TYPE_OPTIONS } from '@/types/process';
interface ProcessDetailProps {
process: Process;
@@ -18,6 +19,23 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
const router = useRouter();
const [workLogModalOpen, setWorkLogModalOpen] = useState(false);
// 패턴 규칙과 개별 품목 분리
const { patternRules, individualItems } = useMemo(() => {
const patterns = process.classificationRules.filter(
(rule) => rule.registrationType === 'pattern'
);
const individuals = process.classificationRules.filter(
(rule) => rule.registrationType === 'individual'
);
return { patternRules: patterns, individualItems: individuals };
}, [process.classificationRules]);
// 매칭 타입 라벨
const getMatchingTypeLabel = (type: string) => {
const option = MATCHING_TYPE_OPTIONS.find((opt) => opt.value === type);
return option?.label || type;
};
const handleEdit = () => {
router.push(`/ko/master-data/process-management/${process.id}/edit`);
};
@@ -110,20 +128,23 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
</CardContent>
</Card>
{/* 자동 분류 규칙 */}
{/* 자동 분류 규칙 (패턴 기반) */}
<Card>
<CardHeader className="bg-muted/50">
<CardTitle className="text-base"> </CardTitle>
<CardTitle className="text-base flex items-center gap-2">
<Wrench className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent className="pt-6">
{process.classificationRules.length === 0 ? (
<div className="text-center py-12 text-muted-foreground">
<Wrench className="h-12 w-12 mx-auto mb-4 opacity-30" />
<p> </p>
{patternRules.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
<Wrench className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p className="text-sm"> </p>
</div>
) : (
<div className="space-y-3">
{process.classificationRules.map((rule) => (
{patternRules.map((rule) => (
<div
key={rule.id}
className="flex items-center justify-between p-4 border rounded-lg"
@@ -134,7 +155,51 @@ export function ProcessDetail({ process }: ProcessDetailProps) {
</Badge>
<div>
<div className="font-medium">
{rule.ruleType} - "{rule.conditionValue}"
{rule.ruleType} · {getMatchingTypeLabel(rule.matchingType)} · "{rule.conditionValue}"
</div>
{rule.description && (
<div className="text-sm text-muted-foreground">
{rule.description}
</div>
)}
</div>
</div>
<Badge variant="outline">: {rule.priority}</Badge>
</div>
))}
</div>
)}
</CardContent>
</Card>
{/* 개별 품목 */}
<Card>
<CardHeader className="bg-muted/50">
<CardTitle className="text-base flex items-center gap-2">
<Package className="h-4 w-4" />
</CardTitle>
</CardHeader>
<CardContent className="pt-6">
{individualItems.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
<Package className="h-10 w-10 mx-auto mb-3 opacity-30" />
<p className="text-sm"> </p>
</div>
) : (
<div className="space-y-3">
{individualItems.map((rule) => (
<div
key={rule.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div className="flex items-center gap-4">
<Badge variant={rule.isActive ? 'default' : 'secondary'}>
{rule.isActive ? '활성' : '비활성'}
</Badge>
<div>
<div className="font-medium">
{rule.conditionValue}
</div>
{rule.description && (
<div className="text-sm text-muted-foreground">