From e53d40bd61cade365ee4a6d4323e2ccfb8558ab2 Mon Sep 17 00:00:00 2001 From: "nabijonovdavronbek619@gmail.com" Date: Fri, 9 Jan 2026 11:39:58 +0500 Subject: [PATCH] detail page added --- app/detail/page.tsx | 14 +++ components/detailPage/detailInfo.tsx | 88 +++++++++++++++ components/loading.tsx | 16 +++ components/productSection/ProductCard.tsx | 107 ++++++++++-------- components/productSection/ProductModal.tsx | 122 --------------------- components/productSection/ProductsGrid.tsx | 27 ++--- lib/productZustand.ts | 20 ++++ 7 files changed, 205 insertions(+), 189 deletions(-) create mode 100644 app/detail/page.tsx create mode 100644 components/detailPage/detailInfo.tsx create mode 100644 components/loading.tsx delete mode 100644 components/productSection/ProductModal.tsx diff --git a/app/detail/page.tsx b/app/detail/page.tsx new file mode 100644 index 0000000..c1f0e61 --- /dev/null +++ b/app/detail/page.tsx @@ -0,0 +1,14 @@ +import { ContactForm } from "@/components/ContactForm"; +import DetailInfo from "@/components/detailPage/detailInfo"; +import React from "react"; + +export default function Page() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/components/detailPage/detailInfo.tsx b/components/detailPage/detailInfo.tsx new file mode 100644 index 0000000..5e5a08d --- /dev/null +++ b/components/detailPage/detailInfo.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { motion } from "framer-motion"; +import { X } from "lucide-react"; +import type { Product } from "@/lib/products"; +import { useLanguage } from "@/context/language-context"; +import Link from "next/link"; +import { useProduct, useProductStore } from "@/lib/productZustand"; +import Image from "next/image"; + +export default function DetailInfo() { + const { t, language } = useLanguage(); + const languageIndex = language === "uz" ? true : false; + const product = useProduct((state) => state.product); + if (product === null) return null; + + return ( +
+
+ {/* Header */} +
+

+ {languageIndex ? product.name_uz : product.name_ru} +

+
+ + {/* Content */} +
+
+ {/* Image */} +
+ image +
+ +
+ {/* Description */} +
+ {languageIndex + ? product.description_uz + : product.description_ru} +
+ {/* CTA Buttons */} +
+ + + {t.contact.send} + + +
+
+
+ {/* Specifications */} +
+
+

+ {t.features} +

+
+ {product.features.map((spec, idx) => ( +
+ + {languageIndex ? spec.key_uz : spec.key_ru}: + + + {languageIndex ? spec.value_uz : spec.value_ru} + +
+ ))} +
+
+
+
+
+
+ ); +} diff --git a/components/loading.tsx b/components/loading.tsx new file mode 100644 index 0000000..26e3f50 --- /dev/null +++ b/components/loading.tsx @@ -0,0 +1,16 @@ +import { useLanguage } from "@/context/language-context"; + +export default function Loading() { + const { language } = useLanguage(); + const languageIndex = language === "uz" ? true : false; + + return ( +
+
+ + + {languageIndex ? "Yuklanmoqda..." : "Загрузка..."} + +
+ ); +} diff --git a/components/productSection/ProductCard.tsx b/components/productSection/ProductCard.tsx index c662ff0..4be0792 100644 --- a/components/productSection/ProductCard.tsx +++ b/components/productSection/ProductCard.tsx @@ -5,66 +5,77 @@ import { motion } from "framer-motion"; import { ExternalLink } from "lucide-react"; import { type Product } from "@/lib/products"; import { useLanguage } from "@/context/language-context"; +import Link from "next/link"; +import { useProduct, useProductStore } from "@/lib/productZustand"; interface ProductCardProps { product: Product; - onViewDetails: (slug: number) => void; } -export function ProductCard({ product, onViewDetails }: ProductCardProps) { +export function ProductCard({ product }: ProductCardProps) { const { t, language } = useLanguage(); const languageIndex = language === "uz" ? true : false; + const setProductName = useProductStore((state) => state.setProductName); + const setProducts = useProduct((state) => state.setProducts); return ( - { + setProductName(languageIndex ? product.name_uz : product.name_ru); + setProducts(product); + }} > - {/* Image Container - Fixed Height */} -
- - {languageIndex?product.name_uz:product.name_ru} - - - {/* Gradient Overlay */} -
-
+ + {/* Image Container - Fixed Height */} +
+ + {languageIndex + - {/* Content Container - Flex Grow */} -
- {/* Product Name */} -

- {languageIndex?product.name_uz:product.name_ru} -

+ {/* Gradient Overlay */} +
+
- {/* Short Description - Fixed Height with Line Clamp */} -

- {languageIndex?product.name_uz:product.name_ru} -

+ {/* Content Container - Flex Grow */} +
+ {/* Product Name */} +

+ {languageIndex ? product.name_uz : product.name_ru} +

- {/* CTA Button - Always at Bottom */} - onViewDetails(product.id)} - className="w-full flex items-center justify-center gap-2 px-6 py-3 bg-primary text-white rounded-xl font-semibold shadow-lg shadow-blue-500/30 hover:shadow-xl hover:shadow-primary/40 transition-all duration-300 group/button" - > - {t.details} - - -
+ {/* Short Description - Fixed Height with Line Clamp */} +

+ {languageIndex ? product.name_uz : product.name_ru} +

- {/* Decorative Corner */} -
- + {/* CTA Button - Always at Bottom */} + + {t.details} + + +
+ + {/* Decorative Corner */} +
+ + ); } diff --git a/components/productSection/ProductModal.tsx b/components/productSection/ProductModal.tsx deleted file mode 100644 index b098932..0000000 --- a/components/productSection/ProductModal.tsx +++ /dev/null @@ -1,122 +0,0 @@ -"use client"; - -import { motion, AnimatePresence } from "framer-motion"; -import { X } from "lucide-react"; -import type { Product } from "@/lib/products"; -import { useLanguage } from "@/context/language-context"; -import Link from "next/link"; -import { useProductStore } from "@/lib/productZustand"; -import Image from "next/image"; -import { usePathname } from "next/navigation"; - -interface ProductModalProps { - product: Product; - onClose: () => void; -} - -// for github firma repo - -export function ProductModal({ product, onClose }: ProductModalProps) { - const { t, language } = useLanguage(); - const setProductName = useProductStore((state) => state.setProductName); - const languageIndex = language === "uz" ? true : false; - const pathName = usePathname(); - const isPathName = pathName === "/product"; - - - return ( - - - e.stopPropagation()} - className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto" - > - {/* Header */} -
-

- {languageIndex ? product.name_uz : product.name_ru} -

- - - -
- - {/* Content */} -
-
- {/* Image */} -
- image -
- - {/* Specifications */} -
-
-

- {t.features} -

-
- {product.features.map((spec, idx) => ( -
- - {languageIndex ? spec.key_uz : spec.key_ru}: - - - {languageIndex ? spec.value_uz : spec.value_ru} - -
- ))} -
-
- - {/* CTA Buttons */} -
- - { - onClose(); - setProductName( - languageIndex ? product.name_uz : product.name_ru - ); - }} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - className="w-full px-6 py-3 bg-primary/80 text-white rounded-lg font-semibold hover:bg-primary transition-colors" - > - {t.contact.send} - - -
-
-
-
- {languageIndex ? product.description_uz : product.description_ru} -
-
-
-
-
- ); -} diff --git a/components/productSection/ProductsGrid.tsx b/components/productSection/ProductsGrid.tsx index 1ca5376..f4b0403 100644 --- a/components/productSection/ProductsGrid.tsx +++ b/components/productSection/ProductsGrid.tsx @@ -8,37 +8,32 @@ import { useLanguage } from "@/context/language-context"; import Link from "next/link"; import { ChevronsRight } from "lucide-react"; import { ProductCard } from "./ProductCard"; -import { ProductModal } from "./ProductModal"; import axios from "axios"; import EmptyState from "../productsPage/emptyData"; +import Loading from "../loading"; // hello everyone export function ProductsGrid() { const { t } = useLanguage(); - const [selectedProduct, setSelectedProduct] = useState(null); const [allProducts, setAllProducts] = useState([]); + const [loading, setLoading] = useState(false); useEffect(() => { async function getData() { + setLoading(true); await axios .get("https://admin.promtechno.uz/api/products/") .then((res) => { console.log("all data main page: ", res?.data); const allData = res?.data || []; setAllProducts(allData.slice(0, 3)); + setLoading(false); }); } getData(); }, []); - const handleViewDetails = (slug: number) => { - const product = allProducts.find((p: any) => p.id === slug); - if (product) { - setSelectedProduct(product); - } - }; - return ( <>
@@ -66,8 +61,11 @@ export function ProductsGrid() {
+ {loading &&
+
} + {/* Product Grid */} - {allProducts && allProducts.length > 0 ? ( + { loading|| allProducts && allProducts.length > 0 ? ( ))} @@ -96,14 +93,6 @@ export function ProductsGrid() {
- - {/* Product Modalll */} - {selectedProduct && ( - setSelectedProduct(null)} - /> - )} ); } diff --git a/lib/productZustand.ts b/lib/productZustand.ts index 8102ccd..bc9cc79 100644 --- a/lib/productZustand.ts +++ b/lib/productZustand.ts @@ -1,5 +1,6 @@ import { create } from "zustand"; import { devtools, persist } from "zustand/middleware"; +import type { Product } from "@/lib/products"; interface ProductStore { productName: string; @@ -21,3 +22,22 @@ export const useProductStore = create()( ) ) ); + +interface ProductDetail { + product: Product | null; + setProducts: (product: Product) => void; +} + +export const useProduct = create()( + devtools( + persist( + (set) => ({ + product: null, + setProducts: (product: Product) => set({ product }), + }), + { + name: "product-detail-storage", + } + ) + ) +);