Files
sam-react-prod/src/components/business/construction/progress-billing/tables/PhotoTable.tsx
byeongcheolryu db47a15544 feat(WEB): 공사관리 시스템 및 CEO 대시보드 기능 확장
- 공사현장관리: 프로젝트 상세, 공정관리, 칸반보드 구현
- 이슈관리: 현장 이슈 등록/조회 기능 추가
- 근로자현황: 일별 근로자 출역 현황 페이지 추가
- 유틸리티관리: 현장 유틸리티 관리 페이지 추가
- 기성청구: 기성청구 관리 페이지 추가
- CEO 대시보드: 현황판(StatusBoardSection) 추가, 설정 다이얼로그 개선
- 발주관리: 모바일 필터 적용, 리스트 UI 개선
- 공용 컴포넌트: MobileFilter, IntegratedListTemplateV2 개선, CalendarHeader 반응형 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 17:18:29 +09:00

147 lines
5.4 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}{selectedItems.size > 0 && ', ' + 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>
);
}