canina/frontend/application/components/VetGallery.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

200 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { Play, Star, ShieldCheck, X } from "lucide-react";
import { motion, AnimatePresence } from "motion/react";
import Link from "next/link";
import SafeImage from "./SafeImage";
import { useSettingsStore } from "../lib/store/settingsStore";
import { videoService, Video } from "../lib/services/videoService";
export interface TestimonialItem {
id?: string;
authorName?: string;
roleTitle?: string;
avatarUrl?: string;
content?: string;
clinicName?: string;
vetName?: string;
imageUrl?: string;
quote?: string;
title?: string;
thumbnail?: string;
videoUrl?: string;
description?: string;
isActive?: boolean;
}
const FALLBACK_VIDEOS = [
{
id: "v1",
title: "نحوه آماده‌سازی کانی‌هیدروکس GAG",
doctor: "دکتر کلاوس هنینگ",
duration: "۰۱:۴۵",
thumbnail: "https://images.unsplash.com/photo-1576091160550-217359f42f8c?auto=format&fit=crop&q=80&w=400",
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=400",
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: "دوز مصرفی مکمل‌های کلسیم در نژادهای بزرگ برای جلوگیری از بدشکلی‌های استخوانی."
}
];
interface DisplayVideoItem {
id: string;
title?: string;
doctor?: string;
duration?: string;
thumbnail?: string;
videoUrl?: string;
description?: string;
quote?: string;
imageUrl?: string;
vetName?: string;
}
export default function VetGallery({ testimonials = [] }: { testimonials?: TestimonialItem[] }) {
const getText = useSettingsStore(state => state.getText);
const [apiVideos, setApiVideos] = useState<Video[]>([]);
const [selectedVideo, setSelectedVideo] = useState<DisplayVideoItem | null>(null);
React.useEffect(() => {
videoService.getVideos({ featured: true, limit: 3 }).then((data) => {
if (data && data.length > 0) {
setApiVideos(data);
}
});
}, []);
const activeTestimonials = testimonials.filter(t => t.isActive !== false);
const displayItems: DisplayVideoItem[] = activeTestimonials.length > 0
? activeTestimonials.map((t, idx) => ({
id: t.id || `t-${idx}`,
title: (t.authorName || t.vetName || '') + (t.roleTitle ? `${t.roleTitle}` : (t.clinicName ? `${t.clinicName}` : '')),
doctor: (t.authorName || t.vetName || '') + (t.roleTitle ? ` (${t.roleTitle})` : ''),
duration: "۰۲:۰۰",
thumbnail: t.avatarUrl || t.imageUrl || "https://images.unsplash.com/photo-1576091160550-217359f42f8c?auto=format&fit=crop&q=80&w=400",
videoUrl: "https://www.w3schools.com/html/mov_bbb.mp4",
description: t.content || t.quote || '',
quote: t.content || t.quote || ''
}))
: (apiVideos.length > 0 ? apiVideos : FALLBACK_VIDEOS);
return (
<section className="py-24 bg-white font-vazir" dir="rtl">
<div className="max-w-7xl mx-auto px-4">
<div className="flex flex-col md:flex-row md:items-end justify-between mb-16 gap-6">
<div className="max-w-2xl">
<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">
<Star className="w-3 h-3" />
{getText('videos_badge', "Vet Insights")}
</div>
<h2 className="text-4xl md:text-5xl font-black text-medical-gray-900 leading-tight font-lalezar tracking-wide">
{getText('videos_title', "مشاوره ویدئویی")} <span className="text-canina-blue underline decoration-4 decoration-canina-blue/20 underline-offset-8">{getText('videos_title_highlight', "دوست دامپزشک پت شما")}</span>
</h2>
<p className="text-lg text-medical-gray-500 font-medium mt-6">
{getText('videos_desc', "تیم علمی و فرموله‌کنندگان کمپانی کنینا در آلمان مستقیماً با شما صحبت می‌کنند؛ آشنایی با نحوه مصرف، مزایای بالینی و دانش پزشکی پشت هر محصول.")}
</p>
</div>
<Link
href="/videos"
className="inline-flex items-center gap-2 text-canina-blue font-bold text-sm hover:translate-x-1 transition-transform rtl:hover:-translate-x-1"
>
{getText('videos_view_all', "مشاهده همه ویدئوها")}
<Play className="w-4 h-4" />
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{displayItems.map((video, index) => (
<motion.button
key={video.id}
whileHover={{ y: -8 }}
onClick={() => setSelectedVideo(video)}
className="w-full text-right block group cursor-pointer focus-visible:ring-4 focus-visible:ring-canina-blue/30 focus-visible:outline-none rounded-[2rem] p-2"
>
<div className="relative aspect-video rounded-[2rem] overflow-hidden mb-4 shadow-xl">
<div className="absolute inset-0 bg-medical-gray-900/20 group-hover:bg-transparent transition-colors z-10" />
<SafeImage
src={video.thumbnail || video.imageUrl || '/images/vets/vet1.webp'}
alt={video.title || video.vetName || 'ویدیو دامپزشک'}
className="w-full h-full"
imgClassName="object-cover grayscale-[30%] group-hover:grayscale-0 transition-all duration-700"
/>
<div className="absolute inset-0 flex items-center justify-center z-20">
<div className="w-16 h-16 bg-white/90 backdrop-blur-sm rounded-full flex items-center justify-center text-canina-blue scale-90 group-hover:scale-100 group-hover:bg-white transition-all shadow-xl">
<Play className="w-6 h-6 ml-1" />
</div>
</div>
<div className="absolute bottom-4 right-4 bg-black/60 backdrop-blur-md text-white px-2 py-1 rounded-lg text-xs font-black z-20 font-vazir flex items-center gap-1 shadow-lg">
<span></span>
<span>{video.duration || (index === 0 ? '۱:۴۵' : index === 1 ? '۲:۲۰' : '۳:۱۰')}</span>
</div>
</div>
<h4 className="text-lg font-black text-medical-gray-900 mb-2 group-hover:text-canina-blue transition-colors leading-tight font-vazir">
{video.title || (video.quote ? video.quote.substring(0, 40) + '...' : '')}
</h4>
<div className="flex items-center gap-2 text-medical-gray-500 font-bold text-sm font-vazir">
<ShieldCheck className="w-4 h-4 text-canina-blue" />
{video.doctor || video.vetName}
</div>
</motion.button>
))}
</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-medical-gray-900/90 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-black/20 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-8 bg-gradient-to-t from-black/80 to-transparent">
<h3 className="text-white text-2xl font-black mb-2">{selectedVideo.title}</h3>
<p className="text-white/60 text-sm font-medium">{selectedVideo.doctor}</p>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
</section>
);
}