refactor: 모달 Content 컴포넌트 분리 및 파일 입력 UI 공통화
- 모달 컴포넌트에서 Content 분리하여 재사용성 향상 - EstimateDocumentContent, DirectConstructionContent 등 - WorkLogContent, QuotePreviewContent, ReceivingReceiptContent - 파일 입력 공통 UI 컴포넌트 추가 - file-dropzone, file-input, file-list, image-upload - 폼 컴포넌트 코드 정리 및 중복 제거 (-4,056줄) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,8 @@
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { FileDropzone } from '@/components/ui/file-dropzone';
|
||||
import { NumberInput } from '@/components/ui/number-input';
|
||||
import { FileImage, Plus, Trash2, X, Download, Loader2 } from 'lucide-react';
|
||||
import type { BendingDetail } from '@/types/item';
|
||||
@@ -215,11 +215,9 @@ export default function BendingDiagramSection({
|
||||
<div>
|
||||
<Label>파일 선택</Label>
|
||||
<div className="mt-2 space-y-3">
|
||||
<Input
|
||||
type="file"
|
||||
accept="image/*,.pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
<FileDropzone
|
||||
onFilesSelect={(files) => {
|
||||
const file = files[0];
|
||||
if (file && typeof window !== 'undefined') {
|
||||
setBendingDiagramFile(file);
|
||||
const reader = new window.FileReader();
|
||||
@@ -229,13 +227,14 @@ export default function BendingDiagramSection({
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
}}
|
||||
accept="image/*,.pdf"
|
||||
maxSize={10}
|
||||
disabled={isSubmitting}
|
||||
title="클릭하거나 파일을 드래그하세요"
|
||||
description={selectedPartType === 'ASSEMBLY'
|
||||
? '조립품 전개도 이미지를 업로드하세요(JPG, PNG, PDF 등)'
|
||||
: '절곡품 전개도 이미지를 업로드하세요(JPG, PNG, PDF 등)'}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
* {selectedPartType === 'ASSEMBLY'
|
||||
? '조립품 전개도 이미지를 업로드하세요(JPG, PNG, PDF 등)'
|
||||
: '절곡품 전개도 이미지를 업로드하세요(JPG, PNG, PDF 등)'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 전개도 이미지 미리보기 */}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* 제품 (FG) 폼 컴포넌트
|
||||
*/
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
@@ -14,7 +13,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { X } from 'lucide-react';
|
||||
import { FileInput } from '@/components/ui/file-input';
|
||||
import type { UseFormRegister, UseFormSetValue, UseFormGetValues, FieldErrors } from 'react-hook-form';
|
||||
import type { CreateItemFormData } from '@/lib/utils/validation';
|
||||
|
||||
@@ -243,79 +242,29 @@ export function ProductCertificationSection({
|
||||
{/* 시방서 파일 */}
|
||||
<div className="space-y-2">
|
||||
<Label>시방서 (PDF)</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="cursor-pointer">
|
||||
<span className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium border rounded-md bg-background hover:bg-accent hover:text-accent-foreground">
|
||||
파일 선택
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setSpecificationFile(file);
|
||||
}
|
||||
}}
|
||||
className="hidden"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</label>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{specificationFile ? specificationFile.name : '선택된 파일 없음'}
|
||||
</span>
|
||||
{specificationFile && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSpecificationFile(null)}
|
||||
disabled={isSubmitting}
|
||||
className="h-6 w-6 p-0"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<FileInput
|
||||
value={specificationFile}
|
||||
onFileSelect={setSpecificationFile}
|
||||
onFileRemove={() => setSpecificationFile(null)}
|
||||
accept=".pdf"
|
||||
disabled={isSubmitting}
|
||||
buttonText="파일 선택"
|
||||
placeholder="선택된 파일 없음"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 인정서 파일 */}
|
||||
<div className="space-y-2">
|
||||
<Label>인정서 (PDF)</Label>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="cursor-pointer">
|
||||
<span className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium border rounded-md bg-background hover:bg-accent hover:text-accent-foreground">
|
||||
파일 선택
|
||||
</span>
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setCertificationFile(file);
|
||||
}
|
||||
}}
|
||||
className="hidden"
|
||||
disabled={isSubmitting}
|
||||
/>
|
||||
</label>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{certificationFile ? certificationFile.name : '선택된 파일 없음'}
|
||||
</span>
|
||||
{certificationFile && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setCertificationFile(null)}
|
||||
disabled={isSubmitting}
|
||||
className="h-6 w-6 p-0"
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<FileInput
|
||||
value={certificationFile}
|
||||
onFileSelect={setCertificationFile}
|
||||
onFileRemove={() => setCertificationFile(null)}
|
||||
accept=".pdf"
|
||||
disabled={isSubmitting}
|
||||
buttonText="파일 선택"
|
||||
placeholder="선택된 파일 없음"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 비고 */}
|
||||
|
||||
Reference in New Issue
Block a user