diff --git a/app/detail/page.tsx b/app/detail/page.tsx deleted file mode 100644 index c1f0e61..0000000 --- a/app/detail/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ContactForm } from "@/components/ContactForm"; -import DetailInfo from "@/components/detailPage/detailInfo"; -import React from "react"; - -export default function Page() { - return ( -
- -
- -
-
- ); -} diff --git a/app/product/[slug]/page.tsx b/app/product/[slug]/page.tsx new file mode 100644 index 0000000..9b52a78 --- /dev/null +++ b/app/product/[slug]/page.tsx @@ -0,0 +1,38 @@ +import { ContactForm } from "@/components/ContactForm"; +import DetailInfo from "@/components/detailPage/detailInfo"; +import { Product } from "@/lib/products"; +import { generateSlug } from "@/lib/slug"; +import { getAllProducts } from "@/lib/api"; +import { notFound } from "next/navigation"; + +export async function generateStaticParams() { + const products = await getAllProducts(); + + return products.map((product) => ({ + slug: generateSlug(product.name_uz), + })); +} + +async function getProduct(slug: string): Promise { + const products = await getAllProducts(); + + return products.find((product) => generateSlug(product.name_uz) === slug); +} + +export default async function Page({ params }: { params: any }) { + const { slug } = await params; + const product = await getProduct(slug); + + if (!product) { + notFound(); + } + + return ( +
+ +
+ +
+
+ ); +} diff --git a/components/detailPage/detailInfo.tsx b/components/detailPage/detailInfo.tsx index 417f385..0b02ded 100644 --- a/components/detailPage/detailInfo.tsx +++ b/components/detailPage/detailInfo.tsx @@ -3,14 +3,16 @@ import { motion } from "framer-motion"; import { useLanguage } from "@/context/language-context"; import Link from "next/link"; -import { useProduct, useProductStore } from "@/lib/productZustand"; import Image from "next/image"; +import { Product } from "@/lib/products"; -export default function DetailInfo() { +interface DetailInfoProps { + product: Product; +} + +export default function DetailInfo({ product }: DetailInfoProps) { const { t, language } = useLanguage(); const languageIndex = language === "uz" ? true : false; - const product = useProduct((state) => state.product); - if (product === null) return null; const handleScroll = (href: string) => { const element = document.querySelector(href); @@ -74,7 +76,7 @@ export default function DetailInfo() { {t.features}
- {product.features.map((spec, idx) => ( + {product.features?.map((spec, idx) => (
state.setProducts); return ( { setProductName(languageIndex ? product.name_uz : product.name_ru); setProducts(product); @@ -63,14 +63,14 @@ export function ProductCard({ product }: ProductCardProps) {

{/* CTA Button - Always at Bottom */} - {t.details} - +
{/* Decorative Corner */} diff --git a/components/productSection/ProductsGrid.tsx b/components/productSection/ProductsGrid.tsx index d74e421..878bb92 100644 --- a/components/productSection/ProductsGrid.tsx +++ b/components/productSection/ProductsGrid.tsx @@ -11,6 +11,8 @@ import { ProductCard } from "./ProductCard"; import axios from "axios"; import EmptyState from "../productsPage/emptyData"; import Loading from "../loading"; +import { getAllProducts } from "@/lib/api"; +import { generateSlug } from "@/lib/slug"; // hello everyone @@ -22,14 +24,14 @@ export function ProductsGrid() { 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); - }); + const products = await getAllProducts(); + setAllProducts( + products.map((product: any) => ({ + ...product, + slug: generateSlug(product.name_uz), + })).slice(0, 3) + ); + setLoading(false); } getData(); }, []); diff --git a/components/productsPage/products.tsx b/components/productsPage/products.tsx index 2235bcc..810748a 100644 --- a/components/productsPage/products.tsx +++ b/components/productsPage/products.tsx @@ -5,6 +5,8 @@ import { motion } from "framer-motion"; import { ProductCard } from "../productSection/ProductCard"; import axios from "axios"; import Loading from "../loading"; +import { generateSlug } from "@/lib/slug"; +import { getAllProducts } from "@/lib/api"; const itemVariants = { hidden: { opacity: 0, y: 20 }, @@ -18,14 +20,14 @@ export default function Products() { useEffect(() => { async function getData() { setLoading(true); - await axios - .get("https://admin.promtechno.uz/api/products/") - .then((res) => { - console.log("all data: ", res?.data); - const allData = res?.data || []; - setAllProducts(allData); - setLoading(false); - }); + const products = await getAllProducts(); + setAllProducts( + products.map((product: any) => ({ + ...product, + slug: generateSlug(product.name_uz), + })) + ); + setLoading(false); } getData(); }, []); diff --git a/lib/api.ts b/lib/api.ts new file mode 100644 index 0000000..7dbda45 --- /dev/null +++ b/lib/api.ts @@ -0,0 +1,14 @@ +import { Product } from "@/lib/products"; + +export async function getAllProducts(): Promise { + const res = await fetch("https://admin.promtechno.uz/api/products/", { + cache: "force-cache", // build time uchun + }); + + if (!res.ok) { + console.log("Failed to fetch products"); + return []; + } + + return res.json(); +} diff --git a/lib/generateStaticParam.ts b/lib/generateStaticParam.ts new file mode 100644 index 0000000..cc72b29 --- /dev/null +++ b/lib/generateStaticParam.ts @@ -0,0 +1,17 @@ +import axios from "axios"; +import { generateSlug } from "./slug"; + +export async function generateStaticParams() { + try { + await axios.get("https://admin.promtechno.uz/api/products/").then((res) => { + console.log("all data: ", res?.data); + const allData = res?.data || []; + return allData.map((product: any) => ({ + slug: generateSlug(product.name_uz), + })); + }); + } catch (error) { + console.error("Error in generateStaticParams:", error); + return []; // Xato bo'lsa bo'sh array qaytarish + } +} diff --git a/lib/productZustand.ts b/lib/productZustand.ts index bc9cc79..cc83d3f 100644 --- a/lib/productZustand.ts +++ b/lib/productZustand.ts @@ -9,18 +9,11 @@ interface ProductStore { } export const useProductStore = create()( - devtools( - persist( - (set) => ({ - productName: "", - setProductName: (name: string) => set({ productName: name }), - resetProductName: () => set({ productName: "" }), - }), - { - name: "product-storage", - } - ) - ) + devtools((set) => ({ + productName: "", + setProductName: (name: string) => set({ productName: name }), + resetProductName: () => set({ productName: "" }), + })) ); interface ProductDetail { diff --git a/lib/products.ts b/lib/products.ts index 0480aee..1745d0b 100644 --- a/lib/products.ts +++ b/lib/products.ts @@ -13,4 +13,5 @@ export interface Product { description_ru: string; features:features[]; image:string; + slug?: string; } diff --git a/lib/slug.ts b/lib/slug.ts new file mode 100644 index 0000000..09cde52 --- /dev/null +++ b/lib/slug.ts @@ -0,0 +1,12 @@ +export function generateSlug(productName: string): string { + return productName + .toLowerCase() + .replace(/\s+/g, "-") // Bo'shliqlarni tire bilan almashtirish + .replace(/[()]/g, "") // Qavslarni olib tashlash + .replace(/[–—]/g, "-") // Maxsus tire'larni oddiy tire bilan + .replace(/%/g, "foiz") // % ni foiz deb yozish + .replace(/,/g, "-") // Vergullarni tire bilan + .replace(/\.+/g, "-") // Nuqtalarni tire bilan + .replace(/-+/g, "-") // Bir nechta tire'ni bitta tire bilan + .replace(/^-|-$/g, ""); // Boshi va oxiridagi tire'larni olib tashlash +}