Files
Vault/app/dashboard/liked-videos-client.tsx
twotalesanimation 81ad7e4ea9 Initial commit
2026-06-11 10:46:09 +02:00

226 lines
7.5 KiB
TypeScript

'use client';
import * as React from 'react';
import { useRouter } from 'next/navigation';
import Image from 'next/image';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { AppSidebar } from '@/components/app-sidebar';
import { SiteHeader } from '@/components/site-header';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import { Button } from '@/components/ui/button';
interface LikedVideoItem {
id: string;
videoId: string;
createdAt: string;
video: {
id: string;
title: string;
thumbnail?: string;
durationSec?: number;
url: string;
playlist: {
title: string;
course: {
id: string;
title: string;
};
};
};
}
export default function LikedVideosClient() {
const router = useRouter();
const [likes, setLikes] = React.useState<LikedVideoItem[]>([]);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
const fetchLikes = async () => {
try {
const res = await fetch('/api/likes/all');
if (res.ok) {
const data = await res.json();
setLikes(data);
}
} catch (err) {
console.error('Failed to fetch liked videos', err);
} finally {
setIsLoading(false);
}
};
fetchLikes();
}, []);
const handleVideoClick = (videoId: string) => {
router.push(`/videoplayer?videoId=${videoId}`);
};
const formatTime = (seconds?: number) => {
if (!seconds) return '0:00';
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}:${secs.toString().padStart(2, '0')}`;
};
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
};
if (isLoading) {
return (
<SidebarProvider
style={
{
'--sidebar-width': 'calc(var(--spacing) * 72)',
'--header-height': 'calc(var(--spacing) * 12)',
} as React.CSSProperties
}
>
<AppSidebar variant="inset" />
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<Card>
<CardHeader>
<CardTitle>Liked Videos</CardTitle>
</CardHeader>
<CardContent>
<div className="flex items-center justify-center py-8">
<p className="text-muted-foreground">Loading...</p>
</div>
</CardContent>
</Card>
</div>
</SidebarInset>
</SidebarProvider>
);
}
return (
<SidebarProvider
style={
{
'--sidebar-width': 'calc(var(--spacing) * 72)',
'--header-height': 'calc(var(--spacing) * 12)',
} as React.CSSProperties
}
>
<AppSidebar variant="inset" />
<SidebarInset>
<SiteHeader />
<div className="flex flex-1 flex-col gap-4 p-4 md:gap-8 md:p-8">
<Card>
<CardHeader>
<CardTitle>Liked Videos</CardTitle>
</CardHeader>
<CardContent>
{likes.length === 0 ? (
<div className="flex items-center justify-center py-8">
<p className="text-muted-foreground">No liked videos yet</p>
</div>
) : (
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>Thumbnail</TableHead>
<TableHead>Title</TableHead>
<TableHead>Playlist</TableHead>
<TableHead>Course</TableHead>
<TableHead>Duration</TableHead>
<TableHead>Liked On</TableHead>
<TableHead>Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{likes.map((item) => (
<TableRow key={item.id}>
<TableCell>
<div
className="cursor-pointer hover:opacity-80 transition-opacity"
onClick={() => handleVideoClick(item.video.id)}
>
{item.video.thumbnail ? (
<div className="relative w-24 h-14">
<Image
src={item.video.thumbnail}
alt={item.video.title}
fill
unoptimized
className="object-cover rounded"
/>
</div>
) : (
<div className="w-24 h-14 bg-muted rounded flex items-center justify-center">
<span className="text-xs text-muted-foreground">
No image
</span>
</div>
)}
</div>
</TableCell>
<TableCell>
<p
className="font-medium max-w-xs truncate cursor-pointer hover:underline"
onClick={() => handleVideoClick(item.video.id)}
>
{item.video.title}
</p>
</TableCell>
<TableCell>
<span className="text-sm">
{item.video.playlist.title}
</span>
</TableCell>
<TableCell>
<span className="text-sm">
{item.video.playlist.course.title}
</span>
</TableCell>
<TableCell>
<span className="text-sm">
{formatTime(item.video.durationSec)}
</span>
</TableCell>
<TableCell>
<span className="text-sm text-muted-foreground">
{formatDate(item.createdAt)}
</span>
</TableCell>
<TableCell>
<Button
variant="outline"
size="sm"
onClick={() => handleVideoClick(item.video.id)}
>
Watch
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</CardContent>
</Card>
</div>
</SidebarInset>
</SidebarProvider>
);
}