Merge remote-tracking branch 'origin/master'

This commit is contained in:
2026-02-11 15:10:15 +09:00
96 changed files with 4930 additions and 6550 deletions

View File

@@ -4,16 +4,11 @@
* 중간검사 미리보기 모달
*
* 설정된 검사 항목들로 실제 성적서가 어떻게 보일지 미리보기
* DocumentViewer preset="readonly" 사용 → 줌/드래그/인쇄/PDF 자동 제공
*/
import { Fragment } from 'react';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { DocumentViewer } from '@/components/document-system';
import { Badge } from '@/components/ui/badge';
import type { InspectionSetting } from '@/types/process';
@@ -30,21 +25,16 @@ export function InspectionPreviewModal({
}: InspectionPreviewModalProps) {
if (!inspectionSetting) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl">
<DialogHeader>
<DialogTitle> </DialogTitle>
</DialogHeader>
<div className="py-12 text-center text-muted-foreground">
. .
</div>
<div className="flex justify-end">
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
</div>
</DialogContent>
</Dialog>
<DocumentViewer
title="중간검사 미리보기"
preset="readonly"
open={open}
onOpenChange={onOpenChange}
>
<div className="py-12 text-center text-muted-foreground">
. .
</div>
</DocumentViewer>
);
}
@@ -69,214 +59,206 @@ export function InspectionPreviewModal({
const sampleRows = [1, 2, 3, 4, 5];
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="w-[95vw] max-w-[1400px] sm:max-w-[1400px] max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle> </DialogTitle>
</DialogHeader>
<div className="space-y-6 mt-4">
{/* 헤더 정보 */}
<div className="flex items-center gap-4 p-4 bg-muted/50 rounded-lg">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<Badge variant="outline">{inspectionSetting.standardName || '미설정'}</Badge>
</div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium"> :</span>
<Badge>{activeAppearanceItems.length + activeDimensionItems.length}</Badge>
</div>
<DocumentViewer
title="중간검사 미리보기"
preset="readonly"
open={open}
onOpenChange={onOpenChange}
>
<div className="space-y-6 p-6">
{/* 헤더 정보 */}
<div className="flex items-center gap-4 p-4 bg-muted/50 rounded-lg">
<div className="flex items-center gap-2">
<span className="text-sm font-medium">:</span>
<Badge variant="outline">{inspectionSetting.standardName || '미설정'}</Badge>
</div>
<div className="flex items-center gap-2">
<span className="text-sm font-medium"> :</span>
<Badge>{activeAppearanceItems.length + activeDimensionItems.length}</Badge>
</div>
</div>
{/* 중간검사 기준서 */}
<div className="border rounded-lg overflow-hidden">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-b">
{/* 중간검사 기준서 */}
<div className="border rounded-lg overflow-hidden">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-b">
</div>
<div className="p-4 grid grid-cols-2 gap-4">
{/* 도해 이미지 영역 */}
<div className="border rounded-lg p-4 min-h-[200px] flex items-center justify-center bg-muted/30">
{inspectionSetting.schematicImage ? (
<img
src={inspectionSetting.schematicImage}
alt="도해 이미지"
className="max-w-full max-h-[180px] object-contain"
/>
) : (
<span className="text-muted-foreground text-sm"> </span>
)}
</div>
<div className="p-4 grid grid-cols-2 gap-4">
{/* 도해 이미지 영역 */}
<div className="border rounded-lg p-4 min-h-[200px] flex items-center justify-center bg-muted/30">
{inspectionSetting.schematicImage ? (
<img
src={inspectionSetting.schematicImage}
alt="도해 이미지"
className="max-w-full max-h-[180px] object-contain"
/>
) : (
<span className="text-muted-foreground text-sm"> </span>
)}
</div>
{/* 검사기준 이미지 또는 검사 항목 테이블 */}
<div className="border rounded-lg overflow-hidden min-h-[200px] flex items-center justify-center bg-muted/30">
{inspectionSetting.inspectionStandardImage ? (
<img
src={inspectionSetting.inspectionStandardImage}
alt="검사기준 이미지"
className="max-w-full max-h-[180px] object-contain"
/>
) : (
<div className="w-full">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="border-b px-3 py-2 text-left"></th>
<th className="border-b px-3 py-2 text-left"></th>
<th className="border-b px-3 py-2 text-left"></th>
{/* 검사기준 이미지 또는 검사 항목 테이블 */}
<div className="border rounded-lg overflow-hidden min-h-[200px] flex items-center justify-center bg-muted/30">
{inspectionSetting.inspectionStandardImage ? (
<img
src={inspectionSetting.inspectionStandardImage}
alt="검사기준 이미지"
className="max-w-full max-h-[180px] object-contain"
/>
) : (
<div className="w-full">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="border-b px-3 py-2 text-left"></th>
<th className="border-b px-3 py-2 text-left"></th>
<th className="border-b px-3 py-2 text-left"></th>
</tr>
</thead>
<tbody>
{activeAppearanceItems.map((item) => (
<tr key={item.key} className="border-b last:border-b-0">
<td className="px-3 py-2">{item.label}</td>
<td className="px-3 py-2"></td>
<td className="px-3 py-2">-</td>
</tr>
</thead>
<tbody>
{activeAppearanceItems.map((item) => (
<tr key={item.key} className="border-b last:border-b-0">
<td className="px-3 py-2">{item.label}</td>
<td className="px-3 py-2"></td>
<td className="px-3 py-2">-</td>
</tr>
))}
{activeDimensionItems.map((item) => (
<tr key={item.key} className="border-b last:border-b-0">
<td className="px-3 py-2">{item.label}</td>
<td className="px-3 py-2">{item.method}</td>
<td className="px-3 py-2">{item.point}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
))}
{activeDimensionItems.map((item) => (
<tr key={item.key} className="border-b last:border-b-0">
<td className="px-3 py-2">{item.label}</td>
<td className="px-3 py-2">{item.method}</td>
<td className="px-3 py-2">{item.point}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
</div>
{/* 중간검사 DATA */}
<div className="border rounded-lg overflow-hidden">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-b">
DATA
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="border-b border-r px-3 py-2 text-center w-12">No.</th>
{/* 겉모양 항목들 */}
{/* 중간검사 DATA */}
<div className="border rounded-lg overflow-hidden">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-b">
DATA
</div>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead className="bg-muted/50">
<tr>
<th className="border-b border-r px-3 py-2 text-center w-12">No.</th>
{/* 겉모양 항목들 */}
{activeAppearanceItems.map((item) => (
<th
key={item.key}
className="border-b border-r px-3 py-2 text-center min-w-[80px]"
>
{item.label}
</th>
))}
{/* 치수 항목들 */}
{activeDimensionItems.map((item) => (
<th
key={item.key}
className="border-b border-r px-3 py-2 text-center"
colSpan={2}
>
{item.label} (mm)
</th>
))}
{/* 판정 */}
{inspectionSetting.judgment && (
<th className="border-b px-3 py-2 text-center w-20">
<br />
<span className="text-xs">(/)</span>
</th>
)}
</tr>
{/* 치수 서브헤더 */}
{activeDimensionItems.length > 0 && (
<tr className="bg-muted/30">
<th className="border-b border-r px-3 py-1"></th>
{activeAppearanceItems.map((item) => (
<th
key={item.key}
className="border-b border-r px-3 py-2 text-center min-w-[80px]"
>
{item.label}
<th key={item.key} className="border-b border-r px-3 py-1 text-xs">
/
</th>
))}
{/* 치수 항목들 */}
{activeDimensionItems.map((item) => (
<th
key={item.key}
className="border-b border-r px-3 py-2 text-center"
colSpan={2}
>
{item.label} (mm)
</th>
<Fragment key={`${item.key}-header`}>
<th className="border-b border-r px-3 py-1 text-xs">
</th>
<th className="border-b border-r px-3 py-1 text-xs">
</th>
</Fragment>
))}
{/* 판정 */}
{inspectionSetting.judgment && (
<th className="border-b px-3 py-2 text-center w-20">
<br />
<span className="text-xs">(/)</span>
</th>
<th className="border-b px-3 py-1"></th>
)}
</tr>
{/* 치수 서브헤더 */}
{activeDimensionItems.length > 0 && (
<tr className="bg-muted/30">
<th className="border-b border-r px-3 py-1"></th>
{activeAppearanceItems.map((item) => (
<th key={item.key} className="border-b border-r px-3 py-1 text-xs">
/
</th>
))}
{activeDimensionItems.map((item) => (
<Fragment key={`${item.key}-header`}>
<th className="border-b border-r px-3 py-1 text-xs">
</th>
<th className="border-b border-r px-3 py-1 text-xs">
</th>
</Fragment>
))}
{inspectionSetting.judgment && (
<th className="border-b px-3 py-1"></th>
)}
</tr>
)}
</thead>
<tbody>
{sampleRows.map((row) => (
<tr key={row} className="border-b last:border-b-0 hover:bg-muted/20">
<td className="border-r px-3 py-2 text-center">{row}</td>
{/* 겉모양 샘플 데이터 */}
{activeAppearanceItems.map((item) => (
<td key={item.key} className="border-r px-3 py-2 text-center">
<span className="text-muted-foreground"> </span>
<br />
<span className="text-muted-foreground"> </span>
)}
</thead>
<tbody>
{sampleRows.map((row) => (
<tr key={row} className="border-b last:border-b-0 hover:bg-muted/20">
<td className="border-r px-3 py-2 text-center">{row}</td>
{/* 겉모양 샘플 데이터 */}
{activeAppearanceItems.map((item) => (
<td key={item.key} className="border-r px-3 py-2 text-center">
<span className="text-muted-foreground"> </span>
<br />
<span className="text-muted-foreground"> </span>
</td>
))}
{/* 치수 샘플 데이터 */}
{activeDimensionItems.map((item) => (
<Fragment key={`${item.key}-data-${row}`}>
<td className="border-r px-3 py-2 text-center text-muted-foreground">
-
</td>
))}
{/* 치수 샘플 데이터 */}
{activeDimensionItems.map((item) => (
<Fragment key={`${item.key}-data-${row}`}>
<td className="border-r px-3 py-2 text-center text-muted-foreground">
-
</td>
<td className="border-r px-3 py-2 text-center text-muted-foreground">
-
</td>
</Fragment>
))}
{/* 판정 샘플 */}
{inspectionSetting.judgment && (
<td className="px-3 py-2 text-center">
<span className="text-muted-foreground">-</span>
<td className="border-r px-3 py-2 text-center text-muted-foreground">
-
</td>
)}
</tr>
))}
</tbody>
</table>
</Fragment>
))}
{/* 판정 샘플 */}
{inspectionSetting.judgment && (
<td className="px-3 py-2 text-center">
<span className="text-muted-foreground">-</span>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* 부적합 내용 */}
{inspectionSetting.nonConformingContent && (
<div className="border rounded-lg overflow-hidden">
<div className="grid grid-cols-2">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-r">
</div>
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm">
</div>
</div>
<div className="grid grid-cols-2">
<div className="px-4 py-3 border-r min-h-[60px] text-muted-foreground text-sm">
( )
</div>
<div className="px-4 py-3 text-center text-muted-foreground text-sm">
/
</div>
</div>
</div>
{/* 부적합 내용 */}
{inspectionSetting.nonConformingContent && (
<div className="border rounded-lg overflow-hidden">
<div className="grid grid-cols-2">
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm border-r">
</div>
<div className="bg-muted/50 px-4 py-2 font-semibold text-sm">
</div>
</div>
<div className="grid grid-cols-2">
<div className="px-4 py-3 border-r min-h-[60px] text-muted-foreground text-sm">
( )
</div>
<div className="px-4 py-3 text-center text-muted-foreground text-sm">
/
</div>
</div>
</div>
)}
</div>
{/* 버튼 영역 */}
<div className="flex justify-end mt-6">
<Button variant="outline" onClick={() => onOpenChange(false)}>
</Button>
</div>
</DialogContent>
</Dialog>
)}
</div>
</DocumentViewer>
);
}

View File

@@ -17,7 +17,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { useMenuStore } from '@/store/menuStore';
import { useMenuStore } from '@/stores/menuStore';
import { usePermission } from '@/hooks/usePermission';
import { toast } from 'sonner';
import { DeleteConfirmDialog } from '@/components/ui/confirm-dialog';

View File

@@ -17,7 +17,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { PageLayout } from '@/components/organisms/PageLayout';
import { PageHeader } from '@/components/organisms/PageHeader';
import { useMenuStore } from '@/store/menuStore';
import { useMenuStore } from '@/stores/menuStore';
import { usePermission } from '@/hooks/usePermission';
import { deleteProcessStep } from './actions';
import { toast } from 'sonner';