- 미사용 import/변수/console.log 대량 정리 (100+개 파일) - ItemMasterContext 간소화 (미사용 로직 제거) - IntegratedListTemplateV2 / UniversalListPage 개선 - 결재 컴포넌트(ApprovalBox, DraftBox, ReferenceBox) 정리 - HR 컴포넌트(급여/휴가/부서) 코드 간소화 - globals.css 스타일 정리 및 개선 - AuthenticatedLayout 개선 - middleware CSP 정리 - proxy route 불필요 로깅 제거 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
153 lines
5.6 KiB
TypeScript
153 lines
5.6 KiB
TypeScript
'use client';
|
|
|
|
import Image from 'next/image';
|
|
import { Check } from 'lucide-react';
|
|
import { toast } from 'sonner';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import type { PhotoItem } from '../types';
|
|
|
|
interface PhotoTableProps {
|
|
items: PhotoItem[];
|
|
isViewMode: boolean;
|
|
isEditMode: boolean;
|
|
selectedItems: Set<string>;
|
|
onToggleSelection: (itemId: string) => void;
|
|
onToggleSelectAll: () => void;
|
|
onApplySelected: () => void;
|
|
onPhotoSelect?: (itemId: string, photoIndex: number) => void;
|
|
}
|
|
|
|
export function PhotoTable({
|
|
items,
|
|
isViewMode,
|
|
isEditMode,
|
|
selectedItems,
|
|
onToggleSelection,
|
|
onToggleSelectAll,
|
|
onApplySelected,
|
|
onPhotoSelect,
|
|
}: PhotoTableProps) {
|
|
const allSelected = items.length > 0 && items.every((item) => selectedItems.has(item.id));
|
|
|
|
const handleApply = () => {
|
|
onApplySelected();
|
|
toast.success('적용이 완료되었습니다.');
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<CardTitle className="text-lg">사진대지</CardTitle>
|
|
<span className="text-sm text-muted-foreground">
|
|
전체 {items.length}건
|
|
</span>
|
|
{selectedItems.size > 0 && (
|
|
<>
|
|
<span className="text-sm text-muted-foreground">/</span>
|
|
<span className="text-sm text-primary font-medium">{selectedItems.size}개 항목 선택됨</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
{isEditMode && selectedItems.size > 0 && (
|
|
<Button size="sm" onClick={handleApply}>
|
|
<Check className="h-4 w-4 mr-1" />
|
|
적용
|
|
</Button>
|
|
)}
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-[40px]">
|
|
<Checkbox
|
|
checked={allSelected}
|
|
onCheckedChange={onToggleSelectAll}
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableHead>
|
|
<TableHead className="w-[40px]">번호</TableHead>
|
|
<TableHead className="w-[70px]">시공번호</TableHead>
|
|
<TableHead className="w-[90px]">명칭</TableHead>
|
|
<TableHead>사진</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{items.map((item, index) => (
|
|
<TableRow key={item.id} className="h-[280px]">
|
|
<TableCell className="align-middle">
|
|
<Checkbox
|
|
checked={selectedItems.has(item.id)}
|
|
onCheckedChange={() => onToggleSelection(item.id)}
|
|
disabled={isViewMode}
|
|
/>
|
|
</TableCell>
|
|
<TableCell className="align-middle">{index + 1}</TableCell>
|
|
<TableCell className="align-middle">{item.constructionNumber}</TableCell>
|
|
<TableCell className="align-middle">{item.name}</TableCell>
|
|
<TableCell>
|
|
{item.photos && item.photos.length > 0 ? (
|
|
<div className="flex gap-8 flex-1">
|
|
{item.photos.map((photo, photoIdx) => (
|
|
<label
|
|
key={photoIdx}
|
|
className="flex flex-col items-center gap-3 cursor-pointer flex-1"
|
|
>
|
|
<div
|
|
className={`relative w-full min-w-[280px] h-[200px] border-2 rounded overflow-hidden transition-all bg-muted ${
|
|
item.selectedPhotoIndex === photoIdx
|
|
? 'border-primary ring-2 ring-primary'
|
|
: 'border-border hover:border-primary/50'
|
|
}`}
|
|
>
|
|
<Image
|
|
src={photo}
|
|
alt={item.name + ' 사진 ' + (photoIdx + 1)}
|
|
fill
|
|
className="object-contain"
|
|
/>
|
|
</div>
|
|
{isEditMode && (
|
|
<input
|
|
type="radio"
|
|
name={`photo-select-${item.id}`}
|
|
checked={item.selectedPhotoIndex === photoIdx}
|
|
onChange={() => onPhotoSelect?.(item.id, photoIdx)}
|
|
className="w-5 h-5 accent-primary"
|
|
/>
|
|
)}
|
|
</label>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<span className="text-muted-foreground text-sm">-</span>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
{items.length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={5} className="text-center text-muted-foreground py-8">
|
|
데이터가 없습니다.
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
} |