Compare commits
4 Commits
f840063827
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c5499dac1 | ||
|
|
e94b7678f3 | ||
|
|
9e9a2f79c4 | ||
|
|
3de1299af8 |
@@ -1,38 +0,0 @@
|
|||||||
import { ContactForm } from "@/components/ContactForm";
|
|
||||||
import DetailInfo from "@/components/detailPage/detailInfo";
|
|
||||||
import { getAllProducts } from "@/lib/api";
|
|
||||||
import { Product } from "@/lib/products";
|
|
||||||
import { generateSlug } from "@/lib/slug";
|
|
||||||
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<Product | undefined> {
|
|
||||||
const products = await getAllProducts();
|
|
||||||
console.log("slug: ", slug);
|
|
||||||
|
|
||||||
return products.find((product) => generateSlug(product.name_uz) === slug);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Page({ params }: { params: { slug: string } }) {
|
|
||||||
const product = await getProduct(params.slug);
|
|
||||||
|
|
||||||
if (!product) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<DetailInfo product={product} />
|
|
||||||
<section id="contact">
|
|
||||||
<ContactForm />
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -33,6 +33,14 @@ export const metadata: Metadata = {
|
|||||||
siteName: "Texnik Uskunalar",
|
siteName: "Texnik Uskunalar",
|
||||||
locale: "uz_UZ",
|
locale: "uz_UZ",
|
||||||
type: "website",
|
type: "website",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: "/logo.jpg",
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: "Texnik Uskunalar Logo",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// Product detail pages - based on actual product slugs from API
|
// Product detail pages - based on actual product slugs from API github
|
||||||
const productSlugs = [
|
const productSlugs = [
|
||||||
"ppv-100-1-6-su-6-0-60-0-5foiz-mexanik-suyuqlik-hisoblagichi",
|
"ppv-100-1-6-su-6-0-60-0-5foiz-mexanik-suyuqlik-hisoblagichi",
|
||||||
"ppo-40-0-6-su-suyuqlik-hisoblagichi",
|
"ppo-40-0-6-su-suyuqlik-hisoblagichi",
|
||||||
@@ -33,7 +33,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const productPages = productSlugs.map((slug) => ({
|
const productPages = productSlugs.map((slug) => ({
|
||||||
url: `${baseUrl}/detail/${slug}`,
|
url: `${baseUrl}/product/${slug}`,
|
||||||
lastModified: new Date(),
|
lastModified: new Date(),
|
||||||
changeFrequency: "monthly" as const,
|
changeFrequency: "monthly" as const,
|
||||||
priority: 0.8,
|
priority: 0.8,
|
||||||
|
|||||||
@@ -1,39 +1,55 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import { useLanguage } from "@/context/language-context";
|
import { useLanguage } from "@/context/language-context";
|
||||||
|
import { getAllFaq } from "@/lib/api";
|
||||||
|
|
||||||
interface FaqItem {
|
interface FaqItem {
|
||||||
questionKey: string;
|
questionKey: string;
|
||||||
answerKey: string;
|
answerKey: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FaqProps {
|
export interface FaqBackendItem {
|
||||||
items?: FaqItem[];
|
id: number;
|
||||||
|
question_ru: string;
|
||||||
|
question_uz: string;
|
||||||
|
answer_ru: string;
|
||||||
|
answer_uz: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FAQ({ items }: FaqProps) {
|
const defaultItems: FaqItem[] = [
|
||||||
const {t} = useLanguage();
|
{
|
||||||
|
questionKey: "faq.items.0.question",
|
||||||
|
answerKey: "faq.items.0.answer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: "faq.items.1.question",
|
||||||
|
answerKey: "faq.items.1.answer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
questionKey: "faq.items.2.question",
|
||||||
|
answerKey: "faq.items.2.answer",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function FAQ() {
|
||||||
|
const { t, language } = useLanguage();
|
||||||
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
||||||
|
const [faqItems, setFaqItems] = useState<FaqItem[]>([]);
|
||||||
|
|
||||||
const defaultItems: FaqItem[] = [
|
useEffect(() => {
|
||||||
{
|
async function fetchFaq() {
|
||||||
questionKey: "faq.items.0.question",
|
const allFaq = await getAllFaq();
|
||||||
answerKey: "faq.items.0.answer",
|
const faqItems: FaqItem[] = allFaq.map((item) => ({
|
||||||
},
|
questionKey: language === "uz" ? item.question_uz : item.question_ru,
|
||||||
{
|
answerKey: language === "uz" ? item.answer_uz : item.answer_ru,
|
||||||
questionKey: "faq.items.1.question",
|
}));
|
||||||
answerKey: "faq.items.1.answer",
|
faqItems.length === 0 ? setFaqItems(defaultItems) : setFaqItems(faqItems);
|
||||||
},
|
}
|
||||||
{
|
fetchFaq();
|
||||||
questionKey: "faq.items.2.question",
|
}, [language]);
|
||||||
answerKey: "faq.items.2.answer",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const faqItems = items || defaultItems;
|
|
||||||
|
|
||||||
const containerVariants = {
|
const containerVariants = {
|
||||||
hidden: { opacity: 0 },
|
hidden: { opacity: 0 },
|
||||||
@@ -81,7 +97,7 @@ export function FAQ({ items }: FaqProps) {
|
|||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<h3 className="text-lg font-semibold text-gray-900 flex-1">
|
<h3 className="text-lg font-semibold text-gray-900 flex-1">
|
||||||
{t.faq.items[idx].question}
|
{item.questionKey}
|
||||||
</h3>
|
</h3>
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={{ rotate: openIndex === idx ? 180 : 0 }}
|
animate={{ rotate: openIndex === idx ? 180 : 0 }}
|
||||||
@@ -104,7 +120,7 @@ export function FAQ({ items }: FaqProps) {
|
|||||||
>
|
>
|
||||||
<div className="bg-primary/20 p-6 rounded-b-lg border-t border-gray-200">
|
<div className="bg-primary/20 p-6 rounded-b-lg border-t border-gray-200">
|
||||||
<p className="text-gray-700 leading-relaxed">
|
<p className="text-gray-700 leading-relaxed">
|
||||||
{t.faq.items[idx].answer}
|
{item.answerKey}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export function Navbar({ logoText = "FIRMA" }: NavbarProps) {
|
|||||||
|
|
||||||
const navLinks: NavLink[] = [
|
const navLinks: NavLink[] = [
|
||||||
{ id: "about", labelKey: t.nav.about, href: "#about" },
|
{ id: "about", labelKey: t.nav.about, href: "#about" },
|
||||||
{ id: "products", labelKey: t.nav.products, href: "#products" },
|
{ id: "products", labelKey: t.nav.products, href: "/product" },
|
||||||
{ id: "faq", labelKey: t.nav.faq, href: "#faq" },
|
{ id: "faq", labelKey: t.nav.faq, href: "#faq" },
|
||||||
{ id: "contact", labelKey: t.nav.contact, href: "#contact" },
|
{ id: "contact", labelKey: t.nav.contact, href: "#contact" },
|
||||||
];
|
];
|
||||||
@@ -64,16 +64,26 @@ export function Navbar({ logoText = "FIRMA" }: NavbarProps) {
|
|||||||
|
|
||||||
{/* Desktop Menu */}
|
{/* Desktop Menu */}
|
||||||
<div className="hidden md:flex items-center gap-8">
|
<div className="hidden md:flex items-center gap-8">
|
||||||
{navLinks.map((link) => (
|
{navLinks.map((link) =>
|
||||||
<motion.button
|
link.id === "products" ? (
|
||||||
key={link.id}
|
<Link
|
||||||
whileHover={{ color: "#2563eb" }}
|
key={link.id}
|
||||||
onClick={() => handleScroll(link.href)}
|
href={link.href}
|
||||||
className="text-[#468965] hover:text-[#468965] transition-colors hover:cursor-pointer"
|
className="text-[#468965] hover:text-[#468965] transition-colors hover:cursor-pointer"
|
||||||
>
|
>
|
||||||
{link.labelKey}
|
{link.labelKey}
|
||||||
</motion.button>
|
</Link>
|
||||||
))}
|
) : (
|
||||||
|
<motion.button
|
||||||
|
key={link.id}
|
||||||
|
whileHover={{ color: "#2563eb" }}
|
||||||
|
onClick={() => handleScroll(link.href)}
|
||||||
|
className="text-[#468965] hover:text-[#468965] transition-colors hover:cursor-pointer"
|
||||||
|
>
|
||||||
|
{link.labelKey}
|
||||||
|
</motion.button>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Language & Mobile Menu */}
|
{/* Language & Mobile Menu */}
|
||||||
|
|||||||
12
lib/api.ts
12
lib/api.ts
@@ -1,3 +1,4 @@
|
|||||||
|
import { FaqBackendItem } from "@/components/FAQ";
|
||||||
import { Product } from "@/lib/products";
|
import { Product } from "@/lib/products";
|
||||||
|
|
||||||
export async function getAllProducts(): Promise<Product[]> {
|
export async function getAllProducts(): Promise<Product[]> {
|
||||||
@@ -12,3 +13,14 @@ export async function getAllProducts(): Promise<Product[]> {
|
|||||||
|
|
||||||
return res.json();
|
return res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAllFaq(): Promise<FaqBackendItem[]> {
|
||||||
|
const res = await fetch("https://admin.promtechno.uz/api/faqs/");
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
console.log("Failed to fetch faqs");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user