108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
'use client';
|
|
|
|
import { FileVideo } from 'lucide-react';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import type { UploadVideo } from '@/types';
|
|
|
|
import { CardPagination } from './CardPagination';
|
|
import { UploadActionsMenu } from './UploadActionsMenu';
|
|
import { UploadThumbnail } from './UploadThumbnail';
|
|
import { formatUploadedAt } from './uploadUtils';
|
|
|
|
interface UploadCardGridProps {
|
|
uploads: UploadVideo[];
|
|
isLoading: boolean;
|
|
skip: number;
|
|
limit: number;
|
|
total: number;
|
|
onPageChange: (skip: number) => void;
|
|
onLimitChange: (limit: number) => void;
|
|
onUploadNew: () => void;
|
|
onOpenUpload: (upload: UploadVideo) => void;
|
|
onViewVideo: (upload: UploadVideo) => void;
|
|
onViewDetails: (upload: UploadVideo) => void;
|
|
}
|
|
|
|
export function UploadCardGrid({
|
|
uploads,
|
|
isLoading,
|
|
skip,
|
|
limit,
|
|
total,
|
|
onPageChange,
|
|
onLimitChange,
|
|
onUploadNew,
|
|
onOpenUpload,
|
|
onViewVideo,
|
|
onViewDetails,
|
|
}: UploadCardGridProps) {
|
|
const skeletonItems = Array.from({ length: 6 });
|
|
|
|
if (!isLoading && uploads.length === 0) {
|
|
return (
|
|
<Card>
|
|
<CardContent className="flex min-h-80 flex-col items-center justify-center gap-3 text-center">
|
|
<div className="flex size-12 items-center justify-center rounded-md border bg-muted/40">
|
|
<FileVideo className="size-6 text-muted-foreground" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-base">No uploads found</h2>
|
|
<p className="text-sm text-muted-foreground">
|
|
Upload your first road inspection video.
|
|
</p>
|
|
</div>
|
|
<Button onClick={onUploadNew}>Upload New</Button>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<section className="space-y-4">
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5">
|
|
{isLoading
|
|
? skeletonItems.map((_, index) => (
|
|
<Card key={index} className="h-62 animate-pulse bg-muted/30" />
|
|
))
|
|
: uploads.map((upload) => (
|
|
<Card
|
|
key={upload.video_id}
|
|
className="cursor-pointer overflow-hidden transition-colors hover:bg-muted/30"
|
|
onClick={() => onOpenUpload(upload)}
|
|
>
|
|
<CardContent className="space-y-3 p-3">
|
|
<UploadThumbnail upload={upload} />
|
|
<div className="flex items-start gap-2">
|
|
<div className="min-w-0 flex-1">
|
|
<p className="truncate text-sm font-medium">
|
|
{upload.filename}
|
|
</p>
|
|
<p className="text-xs text-muted-foreground">
|
|
Uploaded {formatUploadedAt(upload.uploaded_at)}
|
|
</p>
|
|
</div>
|
|
<UploadActionsMenu
|
|
upload={upload}
|
|
onViewVideo={onViewVideo}
|
|
onViewDetails={onViewDetails}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<CardPagination
|
|
skip={skip}
|
|
limit={limit}
|
|
total={total}
|
|
count={uploads.length}
|
|
onPageChange={onPageChange}
|
|
onLimitChange={onLimitChange}
|
|
/>
|
|
</section>
|
|
);
|
|
}
|