feat: add My Uploads listing with card and table views

This commit is contained in:
2026-06-18 21:22:44 +05:30
parent dff9e2222f
commit a18fff4d6f
28 changed files with 1060 additions and 127 deletions

View File

@@ -0,0 +1,107 @@
'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;
onViewDetails: (upload: UploadVideo) => void;
onViewProcessedVideo: (upload: UploadVideo) => void;
}
export function UploadCardGrid({
uploads,
isLoading,
skip,
limit,
total,
onPageChange,
onLimitChange,
onUploadNew,
onOpenUpload,
onViewDetails,
onViewProcessedVideo,
}: UploadCardGridProps) {
const skeletonItems = Array.from({ length: Math.min(limit, 20) });
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}
onViewDetails={onViewDetails}
onViewProcessedVideo={onViewProcessedVideo}
/>
</div>
</CardContent>
</Card>
))}
</div>
<CardPagination
skip={skip}
limit={limit}
total={total}
count={uploads.length}
onPageChange={onPageChange}
onLimitChange={onLimitChange}
/>
</section>
);
}