connected backend to service page and detail
This commit is contained in:
@@ -61,7 +61,7 @@ export default function SlugPage() {
|
||||
const features = product.features.map((item: any) => item.name);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#1e1d1c] px-4 md:px-8">
|
||||
<div className="min-h-screen bg-[#1e1d1c] px-4 md:px-8 pb-35">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="pt-30 pb-10">
|
||||
<Breadcrumb />
|
||||
|
||||
280
app/[locale]/services/detail/page.tsx
Normal file
280
app/[locale]/services/detail/page.tsx
Normal file
@@ -0,0 +1,280 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import { motion } from "framer-motion";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import {
|
||||
getOperationalSystems,
|
||||
SystemFeature,
|
||||
} from "@/lib/api/demoapi/operationalSystems";
|
||||
import { Breadcrumb } from "@/components/breadCrumb";
|
||||
|
||||
export default function OperationalSystemsPage() {
|
||||
const t = useTranslations("operationalSystems");
|
||||
const [data, setData] = useState<SystemFeature[] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
// Demo data - fallback ma'lumotlar
|
||||
const demoData: SystemFeature[] = [
|
||||
{
|
||||
id: "1",
|
||||
title: t("systems.sprinkler.title"),
|
||||
shortDesc:t("systems.sprinkler.short-desc"),
|
||||
description: t("systems.sprinkler.description"),
|
||||
features: [
|
||||
t("systems.sprinkler.features.0"),
|
||||
t("systems.sprinkler.features.1"),
|
||||
t("systems.sprinkler.features.2"),
|
||||
t("systems.sprinkler.features.3"),
|
||||
],
|
||||
image: "/images/services/sprinkler.jpg",
|
||||
},
|
||||
];
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(false);
|
||||
|
||||
const result = await getOperationalSystems();
|
||||
|
||||
// Agar backend ma'lumot qaytarsa
|
||||
if (result && result.length > 0) {
|
||||
setData(result);
|
||||
} else {
|
||||
// Backend bo'sh ma'lumot qaytarsa, demo ma'lumotlarni ishlatamiz
|
||||
setData(demoData);
|
||||
}
|
||||
} catch (err) {
|
||||
setError(true);
|
||||
// Xatolik bo'lsa, demo ma'lumotlarni ishlatamiz
|
||||
setData(demoData);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// Animation variants
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.15,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const itemVariants = {
|
||||
hidden: { opacity: 0, y: 30 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.22, 1, 0.36, 1] as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const cardVariants = {
|
||||
hidden: { opacity: 0, scale: 0.95 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.5,
|
||||
ease: [0.22, 1, 0.36, 1] as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Loading state
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-black">
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="text-center space-y-6"
|
||||
>
|
||||
<motion.div
|
||||
animate={{ rotate: 360 }}
|
||||
transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
|
||||
className="w-16 h-16 border-4 border-red-500 border-t-transparent rounded-full mx-auto"
|
||||
/>
|
||||
<p className="text-white text-xl font-medium font-unbounded">
|
||||
{t("loading")}
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Error state with retry
|
||||
if (error && !data) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-black px-4">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="text-center space-y-6 max-w-md"
|
||||
>
|
||||
<div className="text-7xl">⚠️</div>
|
||||
<p className="text-white text-xl font-medium">{t("error")}</p>
|
||||
<motion.button
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
onClick={fetchData}
|
||||
className="px-6 py-3 bg-red-500 text-white rounded-lg font-medium hover:bg-red-600 transition-colors"
|
||||
>
|
||||
{t("retry")}
|
||||
</motion.button>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="pt-20 md:pt-30 pb-35 max-w-6xl mx-auto w-full px-4">
|
||||
{/* Header */}
|
||||
{/* <motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="flex md:flex-row flex-col items-start justify-between gap-6 md:gap-8 w-full mb-16 md:mb-24"
|
||||
>
|
||||
<motion.div variants={itemVariants} className="space-y-4 md:space-y-8 flex-1">
|
||||
<h1 className="bg-linear-to-br from-white via-white to-gray-400 text-transparent bg-clip-text text-3xl md:text-4xl lg:text-5xl font-semibold font-almarai">
|
||||
{t('title')}
|
||||
</h1>
|
||||
<p className="text-white font-medium font-unbounded text-base sm:text-lg md:text-xl max-w-2xl leading-relaxed">
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
variants={itemVariants}
|
||||
whileHover={{ scale: 1.05, rotate: 3 }}
|
||||
transition={{ type: "spring", stiffness: 300, damping: 20 }}
|
||||
className="shrink-0 relative"
|
||||
>
|
||||
<Image
|
||||
src="/images/home/redShlang.png"
|
||||
alt="Fire hose"
|
||||
fill
|
||||
priority
|
||||
className="w-32 h-32 sm:w-40 sm:h-40 md:w-48 md:h-48 lg:w-52 lg:h-52 object-contain"
|
||||
/>
|
||||
</motion.div>
|
||||
</motion.div> */}
|
||||
<div className="mb-5">
|
||||
<Breadcrumb />
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
{!data || data.length === 0 ? (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="text-center py-20"
|
||||
>
|
||||
<p className="text-gray-400 text-xl font-unbounded">{t("noData")}</p>
|
||||
</motion.div>
|
||||
) : (
|
||||
<motion.div
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
className="space-y-12 md:space-y-20"
|
||||
>
|
||||
{data.map((system, index) => (
|
||||
<motion.div
|
||||
key={system.id}
|
||||
variants={cardVariants}
|
||||
whileHover={{ y: -8 }}
|
||||
className={`flex flex-col gap-8 md:gap-12 items-center`}
|
||||
>
|
||||
{/* Image Section */}
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.03 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="w-full relative h-64 md:h-80 lg:h-96 rounded-xl overflow-hidden"
|
||||
>
|
||||
<Image
|
||||
src={system.image}
|
||||
alt={system.title}
|
||||
fill
|
||||
className="object-cover w-full h-auto"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-linear-to-t from-black/10 to-transparent" />
|
||||
</motion.div>
|
||||
|
||||
{/* Content Section */}
|
||||
<div className="w-full space-y-6">
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="text-2xl md:text-3xl lg:text-4xl font-bold text-white font-almarai"
|
||||
>
|
||||
{system.title}
|
||||
</motion.h2>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="text-gray-300 text-sm md:text-base leading-relaxed font-unbounded"
|
||||
>
|
||||
{system.shortDesc}
|
||||
</motion.p>
|
||||
|
||||
<motion.p
|
||||
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="text-gray-300 text-sm md:text-base leading-relaxed font-unbounded"
|
||||
>
|
||||
{system.description}
|
||||
</motion.p>
|
||||
|
||||
<div className="space-y-4">
|
||||
|
||||
<ul className="space-y-3">
|
||||
{system.features.map((feature, featureIndex) => (
|
||||
<motion.li
|
||||
key={featureIndex}
|
||||
initial={{ opacity: 0, x: index % 2 === 0 ? -20 : 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.3 + featureIndex * 0.1,
|
||||
}}
|
||||
className="flex items-start gap-3 text-gray-300"
|
||||
>
|
||||
<span className="text-sm md:text-base font-unbounded">
|
||||
{feature}
|
||||
</span>
|
||||
</motion.li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user