canina/frontend/application/components/VetGallery.tsx
parsa aghaei 062718fb84
Some checks failed
Deploy Canina / deploy (push) Successful in 2m2s
E2E Playwright Tests / Run Full E2E & Security Suites (push) Failing after 8s
perf(mobile): add cart aria-label, fix heading levels h3, and remove duplicate font network chains
2026-09-02 14:21:11 +03:30

185 lines
8.2 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";
import VideoModalPlayer from './VideoModalPlayer';
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 [isLoading, setIsLoading] = useState(true);
const [selectedVideo, setSelectedVideo] = useState<Video | null>(null);
React.useEffect(() => {
setIsLoading(true);
videoService.getVideos({ featured: true, limit: 3 })
.then((data) => {
if (data && Array.isArray(data)) {
setApiVideos(data);
}
})
.catch((err) => {
console.error('Failed to load featured videos:', err);
setApiVideos([]);
})
.finally(() => {
setIsLoading(false);
});
}, []);
if (!isLoading && apiVideos.length === 0) {
return null; // Don't render video section if no videos are configured
}
return (
<section className="py-8 sm:py-20 bg-white font-vazir relative overflow-hidden" 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-6 sm:mb-16 gap-4 sm: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-3 sm:mb-4">
<Star className="w-3 h-3" />
{getText('videos_badge', "Vet Insights")}
</div>
<h2 className="text-2xl sm:text-4xl md:text-5xl font-black text-medical-gray-900 leading-tight font-lalezar tracking-wide" suppressHydrationWarning>
{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-sm sm:text-lg text-medical-gray-500 font-medium mt-2 sm:mt-6" suppressHydrationWarning>
{getText('videos_desc', "تیم علمی و فرموله‌کنندگان کمپانی کنینا در آلمان مستقیماً با شما صحبت می‌کنند؛ آشنایی با نحوه مصرف، مزایای بالینی و دانش پزشکی پشت هر محصول.")}
</p>
</div>
<Link
href="/videos"
className="inline-flex items-center gap-2 text-canina-blue font-bold text-xs sm: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>
{/* Video Grid or Skeleton */}
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[1, 2, 3].map((i) => (
<div key={i} className="rounded-[2rem] p-2 space-y-4 animate-pulse">
<div className="aspect-video bg-medical-gray-100 rounded-[2rem]" />
<div className="h-6 bg-medical-gray-100 rounded-lg w-3/4" />
<div className="h-4 bg-medical-gray-100 rounded-lg w-1/2" />
</div>
))}
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{apiVideos.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 bg-medical-gray-900">
<div className="absolute inset-0 bg-medical-gray-900/20 group-hover:bg-transparent transition-colors z-10" />
<SafeImage
src={video.thumbnail || ''}
alt={video.title || 'ویدیو دامپزشک'}
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>
{video.duration && (
<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}</span>
</div>
)}
</div>
<h3 className="text-lg font-black text-medical-gray-900 mb-2 group-hover:text-canina-blue transition-colors leading-tight font-vazir">
{video.title}
</h3>
<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 || 'کادر علمی کنینا'}
</div>
</motion.button>
))}
</div>
)}
</div>
{/* Custom Feature-Rich Video Player Modal */}
<VideoModalPlayer
isOpen={!!selectedVideo}
onClose={() => setSelectedVideo(null)}
video={selectedVideo}
/>
</section>
);
}