139 lines
3.9 KiB
TypeScript
139 lines
3.9 KiB
TypeScript
/**
|
|
* File: page.tsx
|
|
* Created by: AI Assistant
|
|
* Date: 2025-11-29
|
|
* Purpose: Video list page for kreatiVortex platform
|
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { Link } from '@/i18n/routing';
|
|
import ActionButton from '@/components/ActionButton';
|
|
import VideoCard from '@/components/VideoCard';
|
|
import { useFetch } from '@/hooks/useFetch';
|
|
import { useTranslations } from 'next-intl';
|
|
import { Video } from 'lucide-react';
|
|
import { useState, useEffect } from 'react';
|
|
import { canUploadVideos } from '@/lib/admin';
|
|
|
|
interface VideoData extends Record<string, unknown> {
|
|
id: string;
|
|
title: string;
|
|
description: string;
|
|
videoType: 'YOUTUBE' | 'LOCAL';
|
|
videoUrl: string;
|
|
viewCount: number;
|
|
createdAt: string;
|
|
isPublic: boolean;
|
|
uploaderId: string;
|
|
uploader: {
|
|
user: {
|
|
name: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
export default function VideosPage() {
|
|
const t = useTranslations('Videos');
|
|
const { data: videos, loading, refetch } = useFetch<VideoData[]>('/api/videos');
|
|
const [userProfile, setUserProfile] = useState<any>(null);
|
|
|
|
|
|
useEffect(() => {
|
|
// Fetch user profile to check permissions
|
|
const fetchProfile = async () => {
|
|
try {
|
|
const response = await fetch('/api/user/profile', {
|
|
credentials: 'include'
|
|
});
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setUserProfile(data.data);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching user profile:', error);
|
|
}
|
|
};
|
|
|
|
fetchProfile();
|
|
}, []);
|
|
|
|
const handleDelete = async (videoId: string) => {
|
|
if (!confirm(t('confirmDelete'))) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`/api/videos/${videoId}`, {
|
|
method: 'DELETE',
|
|
credentials: 'include'
|
|
});
|
|
|
|
if (response.ok) {
|
|
// Refresh videos list using refetch instead of window.reload
|
|
await refetch();
|
|
} else {
|
|
alert(t('errorOccurred'));
|
|
}
|
|
} catch (error) {
|
|
console.error('Error deleting video:', error);
|
|
alert(t('errorOccurred'));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex justify-between items-center">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-white">{t('title')}</h1>
|
|
<p className="text-gray-400">{t('subtitle')}</p>
|
|
</div>
|
|
{canUploadVideos(userProfile) && (
|
|
<Link href="/dashboard/videos/new">
|
|
<ActionButton>
|
|
{t('uploadButton')}
|
|
</ActionButton>
|
|
</Link>
|
|
)}
|
|
</div>
|
|
|
|
{/* Loading State */}
|
|
{loading ? (
|
|
<div className="text-center text-gray-400 py-12">
|
|
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gold-400"></div>
|
|
<p className="mt-4">{t('loading')}</p>
|
|
</div>
|
|
) : videos && videos.length > 0 ? (
|
|
/* Video Grid */
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4 sm:gap-6">
|
|
{videos.map((video) => (
|
|
<VideoCard
|
|
key={video.id}
|
|
video={video}
|
|
userProfile={userProfile}
|
|
onDelete={handleDelete}
|
|
/>
|
|
))}
|
|
</div>
|
|
) : (
|
|
/* Empty State */
|
|
<div className="text-center py-12">
|
|
<div className="text-gray-400 mb-4">
|
|
<Video className="w-16 h-16 mx-auto mb-4 text-gray-500" />
|
|
<h3 className="text-xl font-semibold text-white mb-2">{t('noVideos')}</h3>
|
|
<p className="text-gray-400 mb-6">{t('noVideosDesc')}</p>
|
|
{canUploadVideos(userProfile) && (
|
|
<Link href="/dashboard/videos/new">
|
|
<ActionButton>
|
|
{t('uploadFirst')}
|
|
</ActionButton>
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |