Aggiunge visite specialistiche e aggiorna sezione servizi
This commit is contained in:
BIN
clinica-app/client/public/images/ortovet.webp
Normal file
BIN
clinica-app/client/public/images/ortovet.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
@@ -1,31 +1,57 @@
|
||||
/*
|
||||
* DESIGN: "Clinical Warmth"
|
||||
* Sezione servizi con 6 card principali allineate al menu della clinica.
|
||||
* Layout: immagine in alto, bordo superiore colorato, hover lift.
|
||||
* Sezione servizi con due livelli:
|
||||
* - Servizi Clinici
|
||||
* - Visite Specialistiche
|
||||
*/
|
||||
import { motion, useInView } from "framer-motion";
|
||||
import { useRef } from "react";
|
||||
import { Activity, ArrowRight, FlaskConical, Scan, Stethoscope, Video } from "lucide-react";
|
||||
import {
|
||||
Activity,
|
||||
Apple,
|
||||
ArrowRight,
|
||||
Bone,
|
||||
Eye,
|
||||
FlaskConical,
|
||||
Scan,
|
||||
Sparkles,
|
||||
Stethoscope,
|
||||
Video,
|
||||
} from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const services = [
|
||||
type ServiceItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
description: string;
|
||||
image: string;
|
||||
imageClassName?: string;
|
||||
imageHoverClassName?: string;
|
||||
icon: React.ComponentType<{ size?: number; className?: string }>;
|
||||
color: string;
|
||||
features: string[];
|
||||
tone?: "default" | "specialist";
|
||||
};
|
||||
|
||||
const clinicalServices: ServiceItem[] = [
|
||||
{
|
||||
id: "visite-cliniche",
|
||||
title: "Visite Cliniche",
|
||||
subtitle: "Medicina preventiva",
|
||||
description:
|
||||
"Percorsi di prevenzione e visite cliniche complete per monitorare lo stato di salute di cani e gatti in ogni fase della vita. Un approccio attento, continuativo e personalizzato.",
|
||||
"Percorsi di prevenzione e visite cliniche complete per monitorare lo stato di salute di cani e gatti in ogni fase della vita.",
|
||||
image: "https://images.unsplash.com/photo-1628009368231-7bb7cfcb0def?w=900&q=85",
|
||||
icon: Stethoscope,
|
||||
color: "#1B4F72",
|
||||
features: ["Check-up periodici", "Vaccinazioni", "Profilassi antiparassitaria", "Piani prevenzione senior"],
|
||||
features: ["Check-up periodici", "Vaccinazioni", "Profilassi antiparassitaria", "Controlli senior"],
|
||||
},
|
||||
{
|
||||
id: "ecografia",
|
||||
title: "Ecografia",
|
||||
subtitle: "Diagnostica non invasiva",
|
||||
description:
|
||||
"Indagini ecografiche per approfondire in modo rapido e non invasivo organi addominali, apparato urinario, apparato riproduttivo e principali sospetti clinici.",
|
||||
"Indagini ecografiche rapide per approfondire apparato addominale, urinario e riproduttivo con maggiore precisione clinica.",
|
||||
image: "https://images.unsplash.com/photo-1579684385127-1ef15d508118?w=900&q=85",
|
||||
icon: Activity,
|
||||
color: "#4ECDC4",
|
||||
@@ -36,7 +62,7 @@ const services = [
|
||||
title: "Radiologia",
|
||||
subtitle: "Diagnostica per immagini",
|
||||
description:
|
||||
"Radiografie digitali e diagnostica per immagini a supporto della visita clinica, utili per valutare apparato scheletrico, torace, addome e urgenze.",
|
||||
"Radiografie digitali e diagnostica per immagini per apparato scheletrico, torace, addome e approfondimenti d'urgenza.",
|
||||
image: "https://d2xsxph8kpxj0f.cloudfront.net/310519663483796440/DLUbzKbSnCG3dLeob4sLeC/radiology_39785f88.jpg",
|
||||
icon: Scan,
|
||||
color: "#2E86AB",
|
||||
@@ -47,7 +73,7 @@ const services = [
|
||||
title: "Laboratorio",
|
||||
subtitle: "Analisi interne",
|
||||
description:
|
||||
"Laboratorio interno per esami rapidi e mirati, fondamentale per supportare diagnosi tempestive, monitoraggi terapeutici e controlli pre-operatori.",
|
||||
"Laboratorio interno per esami rapidi e mirati, utile per diagnosi tempestive, monitoraggi terapeutici e controlli pre-operatori.",
|
||||
image: "https://d2xsxph8kpxj0f.cloudfront.net/310519663483796440/DLUbzKbSnCG3dLeob4sLeC/laboratory_5a1c4119.jpg",
|
||||
icon: FlaskConical,
|
||||
color: "#1B4F72",
|
||||
@@ -58,7 +84,7 @@ const services = [
|
||||
title: "Endoscopia",
|
||||
subtitle: "Esplorazione mini-invasiva",
|
||||
description:
|
||||
"Tecniche endoscopiche per esplorare apparato digerente e vie respiratorie, riducendo invasivita e tempi di recupero quando il quadro clinico lo consente.",
|
||||
"Tecniche endoscopiche per esplorare apparato digerente e vie respiratorie riducendo invasivita e tempi di recupero.",
|
||||
image: "https://images.unsplash.com/photo-1530026405186-ed1f139313f8?w=900&q=85",
|
||||
icon: Video,
|
||||
color: "#4ECDC4",
|
||||
@@ -69,7 +95,7 @@ const services = [
|
||||
title: "Laparoscopia",
|
||||
subtitle: "Chirurgia mini-invasiva",
|
||||
description:
|
||||
"Procedure mini-invasive che permettono interventi piu delicati, incisioni ridotte e recuperi piu confortevoli rispetto alla chirurgia tradizionale.",
|
||||
"Procedure mini-invasive con incisioni ridotte, recuperi piu confortevoli e maggiore delicatezza nei passaggi operatori.",
|
||||
image: "https://d2xsxph8kpxj0f.cloudfront.net/310519663483796440/DLUbzKbSnCG3dLeob4sLeC/surgery_e92904ed.jpg",
|
||||
icon: Activity,
|
||||
color: "#2E86AB",
|
||||
@@ -77,25 +103,103 @@ const services = [
|
||||
},
|
||||
];
|
||||
|
||||
function ServiceCard({ service, index }: { service: typeof services[0]; index: number }) {
|
||||
const specialistVisits: ServiceItem[] = [
|
||||
{
|
||||
id: "oncologia",
|
||||
title: "Oncologia",
|
||||
subtitle: "Percorsi dedicati",
|
||||
description:
|
||||
"Valutazioni specialistiche per definire iter diagnostici e strategie terapeutiche personalizzate nei casi oncologici.",
|
||||
image: "https://images.unsplash.com/photo-1511174511562-5f97f4f4a759?w=900&q=85",
|
||||
icon: Stethoscope,
|
||||
color: "#A95F3A",
|
||||
features: ["Inquadramento clinico", "Piani terapeutici", "Monitoraggi periodici"],
|
||||
tone: "specialist",
|
||||
},
|
||||
{
|
||||
id: "dermatologia",
|
||||
title: "Dermatologia",
|
||||
subtitle: "Cute e mantello",
|
||||
description:
|
||||
"Approfondimenti per prurito, alopecie, otiti ricorrenti e alterazioni cutanee che richiedono un percorso mirato.",
|
||||
image: "https://images.unsplash.com/photo-1516734212186-a967f81ad0d7?w=900&q=85",
|
||||
icon: Sparkles,
|
||||
color: "#B76E79",
|
||||
features: ["Allergie", "Citologie cutanee", "Otiti croniche"],
|
||||
tone: "specialist",
|
||||
},
|
||||
{
|
||||
id: "oculistica",
|
||||
title: "Oculistica",
|
||||
subtitle: "Vista e benessere oculare",
|
||||
description:
|
||||
"Controlli specialistici per disturbi oculari, lacrimazione, arrossamenti e valutazioni funzionali della vista.",
|
||||
image: "https://images.unsplash.com/photo-1517849845537-4d257902454a?w=900&q=85",
|
||||
icon: Eye,
|
||||
color: "#4C7A9F",
|
||||
features: ["Esame del segmento anteriore", "Pressione oculare", "Follow-up oculari"],
|
||||
tone: "specialist",
|
||||
},
|
||||
{
|
||||
id: "nutrizione",
|
||||
title: "Nutrizione",
|
||||
subtitle: "Equilibrio alimentare",
|
||||
description:
|
||||
"Consulenze per impostare piani nutrizionali su misura in base a eta, patologie, stile di vita e obiettivi clinici.",
|
||||
image: "https://images.unsplash.com/photo-1548199973-03cce0bbc87b?w=900&q=85",
|
||||
icon: Apple,
|
||||
color: "#7EA55A",
|
||||
features: ["Diete personalizzate", "Gestione del peso", "Supporto nutrizionale"],
|
||||
tone: "specialist",
|
||||
},
|
||||
{
|
||||
id: "ortopedia",
|
||||
title: "Ortopedia",
|
||||
subtitle: "Movimento e postura",
|
||||
description:
|
||||
"Valutazioni specialistiche per zoppie, dolore articolare, traumi e impostazione del corretto iter ortopedico.",
|
||||
image: "/images/ortovet.webp",
|
||||
imageClassName: "scale-[0.5]",
|
||||
imageHoverClassName: "group-hover:scale-[0.7]",
|
||||
icon: Bone,
|
||||
color: "#6D7B8C",
|
||||
features: ["Valutazioni zoppia", "Controlli articolari", "Percorsi post-trauma"],
|
||||
tone: "specialist",
|
||||
},
|
||||
];
|
||||
|
||||
function ServiceCard({
|
||||
service,
|
||||
index,
|
||||
className = "",
|
||||
}: {
|
||||
service: ServiceItem;
|
||||
index: number;
|
||||
className?: string;
|
||||
}) {
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: "-50px" });
|
||||
const specialist = service.tone === "specialist";
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: index * 0.12 }}
|
||||
className="group bg-white rounded-2xl overflow-hidden shadow-md hover:shadow-xl transition-all duration-400 hover:-translate-y-1"
|
||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
||||
className={`group overflow-hidden rounded-2xl transition-all duration-400 hover:-translate-y-1 ${className} ${
|
||||
specialist
|
||||
? "border border-[#D7C1A8] bg-gradient-to-b from-[#fffaf2] to-white shadow-[0_18px_45px_rgba(169,95,58,0.12)] hover:shadow-[0_22px_50px_rgba(169,95,58,0.18)]"
|
||||
: "bg-white shadow-md hover:shadow-xl"
|
||||
}`}
|
||||
>
|
||||
<div className="h-1" style={{ backgroundColor: service.color }} />
|
||||
<div className="h-1.5" style={{ backgroundColor: service.color }} />
|
||||
|
||||
<div className="relative h-52 overflow-hidden">
|
||||
<img
|
||||
src={service.image}
|
||||
alt={service.title}
|
||||
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
className={`h-full w-full object-cover transition-transform duration-700 ${service.imageHoverClassName ?? "group-hover:scale-105"} ${service.imageClassName ?? ""}`}
|
||||
/>
|
||||
<div
|
||||
className="absolute top-4 right-4 w-12 h-12 rounded-full flex items-center justify-center shadow-lg"
|
||||
@@ -103,37 +207,42 @@ function ServiceCard({ service, index }: { service: typeof services[0]; index: n
|
||||
>
|
||||
<service.icon size={22} className="text-white" />
|
||||
</div>
|
||||
{specialist && (
|
||||
<div className="absolute left-4 top-4 rounded-full bg-white/90 px-3 py-1 text-[11px] font-semibold uppercase tracking-[0.16em] text-[#7d5233] shadow-sm">
|
||||
Alta specializzazione
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-6">
|
||||
<div className="text-xs uppercase tracking-widest font-semibold mb-1" style={{ color: service.color }}>
|
||||
<div className="mb-1 text-xs font-semibold uppercase tracking-widest" style={{ color: service.color }}>
|
||||
{service.subtitle}
|
||||
</div>
|
||||
<h3
|
||||
className="text-[#1B4F72] mb-3"
|
||||
className="mb-3 text-[#1B4F72]"
|
||||
style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: "1.75rem", fontWeight: 600 }}
|
||||
>
|
||||
{service.title}
|
||||
</h3>
|
||||
<p className="text-gray-600 text-sm leading-relaxed mb-4">{service.description}</p>
|
||||
<p className="mb-4 text-sm leading-relaxed text-gray-600">{service.description}</p>
|
||||
|
||||
<ul className="space-y-1.5 mb-5">
|
||||
<ul className="mb-5 space-y-1.5">
|
||||
{service.features.map((feat) => (
|
||||
<li key={feat} className="flex items-center gap-2 text-sm text-gray-600">
|
||||
<span className="w-1.5 h-1.5 rounded-full flex-shrink-0" style={{ backgroundColor: service.color }} />
|
||||
<span className="h-1.5 w-1.5 flex-shrink-0 rounded-full" style={{ backgroundColor: service.color }} />
|
||||
{feat}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<button
|
||||
className="flex items-center gap-1.5 text-sm font-semibold transition-all duration-200 group/btn"
|
||||
className="group/btn flex items-center gap-1.5 text-sm font-semibold transition-all duration-200"
|
||||
style={{ color: service.color }}
|
||||
onClick={() => {
|
||||
toast("Sezione in arrivo", { description: `La pagina dedicata a ${service.title} sara disponibile a breve.` });
|
||||
}}
|
||||
>
|
||||
Scopri di piu
|
||||
Approfondisci
|
||||
<ArrowRight size={15} className="transition-transform duration-200 group-hover/btn:translate-x-1" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -143,7 +252,9 @@ function ServiceCard({ service, index }: { service: typeof services[0]; index: n
|
||||
|
||||
export default function ServicesSection() {
|
||||
const ref = useRef(null);
|
||||
const specialistRef = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: "-80px" });
|
||||
const specialistInView = useInView(specialistRef, { once: true, margin: "-80px" });
|
||||
|
||||
return (
|
||||
<section id="servizi" className="py-20 md:py-28" style={{ backgroundColor: "#F5F0E8" }}>
|
||||
@@ -155,38 +266,74 @@ export default function ServicesSection() {
|
||||
transition={{ duration: 0.7 }}
|
||||
className="mb-14"
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-12 h-0.5 bg-[#4ECDC4]" />
|
||||
<span className="text-[#4ECDC4] text-sm font-semibold uppercase tracking-widest">
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
<div className="h-0.5 w-12 bg-[#4ECDC4]" />
|
||||
<span className="text-sm font-semibold uppercase tracking-widest text-[#4ECDC4]">
|
||||
Specializzazioni
|
||||
</span>
|
||||
</div>
|
||||
<h2
|
||||
className="text-[#1B4F72] mb-4"
|
||||
className="mb-4 text-[#1B4F72]"
|
||||
style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: "clamp(2rem, 4vw, 3rem)", fontWeight: 600 }}
|
||||
>
|
||||
Servizi Specialistici
|
||||
Servizi Clinici
|
||||
</h2>
|
||||
<p className="text-gray-600 max-w-xl text-base leading-relaxed">
|
||||
<p className="max-w-xl text-base leading-relaxed text-gray-600">
|
||||
Sei aree di eccellenza clinica per accompagnare prevenzione, diagnosi e trattamento
|
||||
dei tuoi animali domestici, con tecnologie avanzate e professionisti esperti.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-8">
|
||||
{services.map((service, index) => (
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-2 xl:grid-cols-3">
|
||||
{clinicalServices.map((service, index) => (
|
||||
<ServiceCard key={service.id} service={service} index={index} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
ref={specialistRef}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={specialistInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.7, delay: 0.2 }}
|
||||
className="mt-20"
|
||||
>
|
||||
<div className="mb-4 flex items-center gap-3">
|
||||
<div className="h-0.5 w-12 bg-[#A95F3A]" />
|
||||
<span className="text-sm font-semibold uppercase tracking-widest text-[#A95F3A]">
|
||||
Alta specializzazione
|
||||
</span>
|
||||
</div>
|
||||
<h3
|
||||
className="mb-4 text-[#1B4F72]"
|
||||
style={{ fontFamily: "'Cormorant Garamond', serif", fontSize: "clamp(1.9rem, 3.7vw, 2.8rem)", fontWeight: 600 }}
|
||||
>
|
||||
Visite Specialistiche
|
||||
</h3>
|
||||
<p className="max-w-2xl text-base leading-relaxed text-gray-600">
|
||||
Un secondo livello di consulenza clinica per casi che richiedono competenze dedicate,
|
||||
approfondimenti mirati e percorsi di valutazione ad alto valore aggiunto.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<div className="mt-10 grid grid-cols-1 gap-8 md:grid-cols-2 xl:grid-cols-6">
|
||||
{specialistVisits.map((service, index) => (
|
||||
<ServiceCard
|
||||
key={service.id}
|
||||
service={service}
|
||||
index={index}
|
||||
className={index < 3 ? "xl:col-span-2" : "xl:col-span-3"}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={isInView ? { opacity: 1 } : {}}
|
||||
animate={specialistInView ? { opacity: 1 } : {}}
|
||||
transition={{ duration: 0.7, delay: 0.6 }}
|
||||
className="text-center mt-12"
|
||||
className="mt-12 text-center"
|
||||
>
|
||||
<button
|
||||
className="inline-flex items-center gap-2 text-[#1B4F72] font-semibold border-2 border-[#1B4F72] px-8 py-3 rounded-full hover:bg-[#1B4F72] hover:text-white transition-all duration-300"
|
||||
className="inline-flex items-center gap-2 rounded-full border-2 border-[#1B4F72] px-8 py-3 font-semibold text-[#1B4F72] transition-all duration-300 hover:bg-[#1B4F72] hover:text-white"
|
||||
onClick={() => {
|
||||
toast("Tutti i servizi", { description: "La pagina completa dei servizi sara disponibile a breve." });
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user