116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
"use client";
|
|
|
|
import { Service } from "@/app/types/types";
|
|
import { getCookie } from "cookies-next";
|
|
import { useEffect, useState } from "react";
|
|
import Skeleton from "react-loading-skeleton";
|
|
import "react-loading-skeleton/dist/skeleton.css";
|
|
import { motion } from "framer-motion";
|
|
import Image from "next/image";
|
|
|
|
interface ServicesProps {
|
|
dict: {
|
|
website: {
|
|
homePage: {
|
|
services: {
|
|
title: string;
|
|
description: string;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|
|
|
|
export default function Services({ dict }: ServicesProps) {
|
|
const [services, setServices] = useState<Service[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const fetchServices = async () => {
|
|
setLoading(true);
|
|
const response = await fetch(
|
|
`/api/website/services/${getCookie("selectedLocale") !== undefined ? getCookie("selectedLocale") : getCookie("defaultLocale")}`,
|
|
).then((res) => res.json());
|
|
|
|
setServices(response);
|
|
setLoading(false);
|
|
};
|
|
|
|
fetchServices();
|
|
}, [getCookie("selectedLocale")]);
|
|
|
|
const renderServiceCard = (service?: Service, index?: number) => (
|
|
<motion.div
|
|
key={service?.id || index}
|
|
initial={{ opacity: 0, y: 30 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5, delay: (index || 0) * 0.1 }}
|
|
viewport={{ once: true }}
|
|
whileHover={{ y: -10 }}
|
|
className="group flex h-full w-full flex-col items-start rounded-3xl glass-panel p-8 transition-all duration-300 hover:border-primary/50"
|
|
>
|
|
<div className="mb-6 relative overflow-hidden rounded-2xl w-20 h-20 bg-primary/10 flex items-center justify-center group-hover:bg-primary/20 transition-colors">
|
|
{loading ? (
|
|
<Skeleton containerClassName="w-12 h-12" />
|
|
) : (
|
|
<img
|
|
src={service!.imageSrc}
|
|
alt={service!.altText}
|
|
className="w-12 h-12 object-contain transition-transform duration-300 group-hover:scale-110 dark:brightness-0 dark:invert dark-invert-target"
|
|
/>
|
|
)}
|
|
</div>
|
|
<h3 className="mb-4 text-2xl font-bold text-foreground group-hover:text-primary transition-colors">
|
|
{loading ? (
|
|
<Skeleton containerClassName="w-full" height={32} />
|
|
) : (
|
|
service!.title
|
|
)}
|
|
</h3>
|
|
<p className="text-muted-foreground leading-relaxed text-sm md:text-base">
|
|
{loading ? (
|
|
<Skeleton containerClassName="w-full" height={60} />
|
|
) : (
|
|
service!.description
|
|
)}
|
|
</p>
|
|
</motion.div>
|
|
);
|
|
|
|
return (
|
|
<section
|
|
className="relative flex w-full max-w-[1200px] flex-col items-center py-20"
|
|
id="services"
|
|
>
|
|
<div className="text-center mb-16 space-y-4">
|
|
<motion.h2
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6 }}
|
|
viewport={{ once: true }}
|
|
className="text-4xl md:text-5xl font-bold text-foreground tracking-tight"
|
|
>
|
|
{dict.website.homePage.services.title}
|
|
</motion.h2>
|
|
<motion.p
|
|
initial={{ opacity: 0, y: 20 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.6, delay: 0.2 }}
|
|
viewport={{ once: true }}
|
|
className="text-lg text-muted-foreground max-w-2xl mx-auto text-balance"
|
|
>
|
|
{dict.website.homePage.services.description}
|
|
</motion.p>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 max-w-5xl mx-auto w-full">
|
|
{loading
|
|
? Array.from({ length: 3 }).map((_, index) =>
|
|
renderServiceCard(undefined, index),
|
|
)
|
|
: services.map((service, index) => renderServiceCard(service, index))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|