canina/frontend/application/components/VideosPage.tsx
parsa aghaei 8d1b786a24
All checks were successful
Deploy Canina / deploy (push) Successful in 1m50s
chore(brand): rename all Persian occurrences of کانینا to کنینا across the project
2026-08-16 16:40:08 +03:30

204 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { ChevronRight, PlayCircle, ShieldCheck, X, Search, Clock, User } from "lucide-react";
import SafeImage from "./SafeImage";
import { videoService, Video } from "../lib/services/videoService";
import { useRouter } from 'next/navigation';
const FALLBACK_VIDEOS: Video[] = [
{
id: "v1",
title: "نحوه آماده‌سازی کانی‌هیدروکس GAG",
doctor: "دکتر کلاوس هنینگ",
duration: "۰۱:۴۵",
thumbnail: "https://images.unsplash.com/photo-1576091160550-217359f42f8c?auto=format&fit=crop&q=80&w=600",
videoUrl: "https://www.w3schools.com/html/mov_bbb.mp4",
description: "آموزش گام به گام آماده‌سازی پودر Canhydrox GAG برای جذب حداکثری در دستگاه گوارش."
},
{
id: "v2",
title: "اهمیت صدف لب‌سبز برای بازسازی غضروف",
doctor: "دکتر النا اشمیت",
duration: "۰۲:۲۰",
thumbnail: "https://images.unsplash.com/photo-1599443015574-be5fe8a05783?auto=format&fit=crop&q=80&w=600",
videoUrl: "https://www.w3schools.com/html/movie.mp4",
description: "چرا صدف لب‌سبز نیوزیلندی نایاب‌ترین و موثرترین ماده برای بازسازی مفصل است؟"
},
{
id: "v3",
title: "تغذیه هوشمند توله‌سگ‌های نژاد بزرگ",
doctor: "دکتر هلگا فیشر",
duration: "۰۳:۱۰",
thumbnail: "/assets/images/regenerated_image_1779109861747.png",
videoUrl: "https://www.w3schools.com/html/mov_bbb.mp4",
description: "دوز مصرفی مکمل‌های کلسیم در نژادهای بزرگ برای جلوگیری از بدشکلی‌های استخوانی."
},
{
id: "v4",
title: "تاثیر امگا ۳ بر ریزش مو فصلی",
doctor: "دکتر ماری کونیگ",
duration: "۰۵:۱۵",
thumbnail: "https://images.unsplash.com/photo-1516734212186-a967f81ad0d7?auto=format&fit=crop&q=80&w=600",
videoUrl: "https://www.w3schools.com/html/movie.mp4",
description: "چربی‌های ضروری چگونه لایه محافظ پوست را بازسازی می‌کنند."
},
{
id: "v5",
title: "سم‌زدایی گوارشی با اسید هومیک",
doctor: "دکتر ریکاردو کوخ",
duration: "۰۴:۰۰",
thumbnail: "https://images.unsplash.com/photo-1581594693702-fbdc51b2763b?auto=format&fit=crop&q=80&w=600",
videoUrl: "https://www.w3schools.com/html/mov_bbb.mp4",
description: "مکانیزم عمل Moor-Tranke در جذب توکسین‌های روده‌ای."
}
];
export default function VideosPage() {
const router = useRouter();
const [videos, setVideos] = useState<Video[]>(FALLBACK_VIDEOS);
const [selectedVideo, setSelectedVideo] = useState<Video | null>(null);
const [search, setSearch] = useState("");
React.useEffect(() => {
videoService.getVideos({ limit: 100 }).then((data) => {
if (data && data.length > 0) {
setVideos(data);
}
});
}, []);
const filteredVideos = videos.filter(v =>
v.title.includes(search) || v.doctor.includes(search)
);
return (
<div className="min-h-screen bg-medical-gray-900 pt-8 pb-20 px-4 font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto">
{/* Header */}
<div className="flex flex-col md:flex-row items-center justify-between mb-16 gap-6">
<div className="text-right">
<div className="inline-flex items-center gap-2 px-3 py-1 bg-canina-blue/10 text-canina-blue rounded-full text-[10px] font-black uppercase tracking-widest mb-4">
<PlayCircle className="w-3 h-3" />
Video Academy
</div>
<h1 className="text-4xl lg:text-6xl font-black text-white italic leading-tight">
آکادمی ویدئویی <span className="text-canina-blue">کنینا</span>
</h1>
</div>
<button
onClick={() => router.push('/')}
className="px-8 py-3 bg-white/5 border border-white/10 rounded-2xl text-white shadow-sm flex items-center gap-2 font-black hover:bg-white/10 transition-all"
>
<ChevronRight className="w-5 h-5" />
بازگشت
</button>
</div>
{/* Search Bar */}
<div className="mb-12 relative max-w-2xl">
<Search className="absolute right-6 top-1/2 -translate-y-1/2 text-white/30 w-5 h-5" />
<input
type="text"
placeholder="جستجو در ویدئوهای آموزشی..."
value={search}
onChange={e => setSearch(e.target.value)}
className="w-full bg-white/5 border border-white/10 rounded-[2rem] py-5 pr-14 pl-6 text-white outline-none focus:bg-white/10 transition-all font-bold"
/>
</div>
{/* Video Grid */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{filteredVideos.map((video, idx) => (
<motion.div
key={video.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: idx * 0.1 }}
className="group cursor-pointer"
onClick={() => {
setSelectedVideo(video);
videoService.incrementViews(video.id);
}}
>
<div className="relative aspect-video rounded-[2.5rem] overflow-hidden mb-6 shadow-2xl border border-white/5">
<SafeImage
src={video.thumbnail}
alt={video.title}
className="w-full h-full"
imgClassName="object-cover group-hover:scale-110 transition-transform duration-700 opacity-60 group-hover:opacity-100"
/>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-16 h-16 bg-canina-blue rounded-full flex items-center justify-center text-white shadow-2xl group-hover:scale-110 transition-transform">
<PlayCircle className="w-10 h-10" />
</div>
</div>
<div className="absolute bottom-4 right-4 bg-black/60 backdrop-blur-md text-white px-3 py-1 rounded-lg text-[10px] font-black">
{video.duration}
</div>
</div>
<div className="px-4">
<h3 className="text-xl font-black text-white group-hover:text-canina-blue transition-colors mb-2">{video.title}</h3>
<div className="flex items-center gap-4 text-xs font-bold text-white/40">
<div className="flex items-center gap-1">
<User className="w-3 h-3" />
{video.doctor}
</div>
<div className="flex items-center gap-1">
<Clock className="w-3 h-3" />
دسترسی دائمی
</div>
</div>
</div>
</motion.div>
))}
</div>
</div>
{/* Video Modal */}
<AnimatePresence>
{selectedVideo && (
<div className="fixed inset-0 z-[110] flex items-center justify-center p-4">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={() => setSelectedVideo(null)}
className="absolute inset-0 bg-black/95 backdrop-blur-xl"
/>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
className="bg-black w-full max-w-5xl aspect-video rounded-[3rem] overflow-hidden relative z-10 shadow-2xl border border-white/10"
>
<button
onClick={() => setSelectedVideo(null)}
className="absolute top-6 left-6 text-white/50 hover:text-white z-20 transition-colors bg-white/5 p-2 rounded-full"
>
<X className="w-8 h-8" />
</button>
<video
src={selectedVideo.videoUrl}
controls
autoPlay
className="w-full h-full object-contain"
/>
<div className="absolute bottom-0 left-0 right-0 p-10 bg-gradient-to-t from-black/90 to-transparent">
<div className="flex items-center gap-2 text-canina-blue text-xs font-black mb-2 uppercase tracking-widest">
<ShieldCheck className="w-4 h-4" />
Verified Insight
</div>
<h3 className="text-white text-3xl font-black mb-2 italic">{selectedVideo.title}</h3>
<p className="text-white/60 text-lg font-medium">{selectedVideo.doctor}</p>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
</div>
);
}