canina/frontend/application/components/VideosPage.tsx
parsa aghaei 7615aa0e51 fix: resolve seed encoding, search param, and wiki data source issues
- Fix seed-products.ts TS error (implicit any) and BOM handling
- Re-run full seed to restore correct Persian encoding in DB
- Fix productService query→search param mismatch
- Fix IngredientWiki hardcoded port 4000→use settingsStore
- Restore seed-products-data.json from git after accidental corruption
2026-07-11 15:40:49 +03:30

190 lines
8.8 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, Star, ShieldCheck, X, Search, Clock, User } from "lucide-react";
const ALL_VIDEOS = [
{
id: "v1",
title: "نحوه آماده‌سازی کانی‌هیدروکس",
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: "https://images.unsplash.com/photo-1583337130417-3346a1be7dee?auto=format&fit=crop&q=80&w=600",
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 در جذب توکسین‌های روده‌ای."
}
];
import { useRouter } from 'next/navigation';
export default function VideosPage() {
const router = useRouter();
const [selectedVideo, setSelectedVideo] = useState<typeof ALL_VIDEOS[0] | null>(null);
const [search, setSearch] = useState("");
const filteredVideos = ALL_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)}
>
<div className="relative aspect-video rounded-[2.5rem] overflow-hidden mb-6 shadow-2xl border border-white/5">
<img
src={video.thumbnail}
alt={video.title}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700 opacity-60 group-hover:opacity-100"
referrerPolicy="no-referrer"
/>
<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>
);
}