Compare commits

..

17 Commits

Author SHA1 Message Date
nabijonovdavronbek619@gmail.com
03ea2d51e4 complated: Guides , sertificate , baza. added dropdown to about navigation button on navbar 2026-03-03 11:28:05 +05:00
nabijonovdavronbek619@gmail.com
aca4103213 baza page complated 2026-03-03 11:22:49 +05:00
nabijonovdavronbek619@gmail.com
68277d4b4c sertificate , guides pages done 2026-03-03 10:46:27 +05:00
nabijonovdavronbek619@gmail.com
e62286effa sertifiacte page updated 2026-03-02 20:14:15 +05:00
nabijonovdavronbek619@gmail.com
a0f8ef76d7 remove middlewere 2026-03-02 17:27:38 +05:00
nabijonovdavronbek619@gmail.com
11a18b52ce remove middlewere 2026-03-02 17:25:05 +05:00
nabijonovdavronbek619@gmail.com
960010ba7b remove middlewere 2026-03-02 17:23:35 +05:00
nabijonovdavronbek619@gmail.com
a7682a8178 navbar updated mobile 2026-03-02 15:09:43 +05:00
nabijonovdavronbek619@gmail.com
8aa5ead09c navbar updated 2026-03-02 15:06:46 +05:00
nabijonovdavronbek619@gmail.com
bfc9b85026 all page is done 2026-03-02 13:04:37 +05:00
nabijonovdavronbek619@gmail.com
1104c55bea all page is done 2026-03-02 12:53:29 +05:00
nabijonovdavronbek619@gmail.com
361faf5709 nomative baza page complated 2026-03-02 10:03:54 +05:00
nabijonovdavronbek619@gmail.com
9858216ae6 inner navbar belong about page 2026-03-02 09:10:02 +05:00
nabijonovdavronbek619@gmail.com
7d4e45d524 inner navbar belong about page 2026-03-02 09:05:16 +05:00
nabijonovdavronbek619@gmail.com
61013d119f smale updates 2026-03-01 15:19:21 +05:00
nabijonovdavronbek619@gmail.com
4737c091be upheader complated 2026-03-01 14:18:12 +05:00
nabijonovdavronbek619@gmail.com
9d406d0998 upheader added new things 2026-03-01 13:42:19 +05:00
35 changed files with 2524 additions and 125 deletions

View File

@@ -0,0 +1,10 @@
import NormativBazaPage from "@/components/pages/about/aboutDetail/baza";
import { Statistics } from "@/components/pages/home";
export default function Baza() {
return (
<div>
<NormativBazaPage />
</div>
);
}

View File

@@ -0,0 +1,15 @@
import { AboutBanner } from "@/components/pages/about";
import React from "react";
export default function AboutLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div>
<AboutBanner />
{children}
</div>
);
}

View File

@@ -0,0 +1,46 @@
"use client";
import Image from "next/image";
import { motion } from "framer-motion";
import { useTranslations } from "next-intl";
import { Guides } from "@/components/pages/about/aboutDetail/guides";
const ease = [0.22, 1, 0.36, 1] as [number, number, number, number];
export default function NotePPPage() {
const t = useTranslations();
const guides = [
{
image: "/images/about/pp.avif",
title: t("about.notePPPage.hero.title"),
description: t("about.notePPPage.hero.description"),
eyebrow: t("about.notePPPage.hero.eyebrow"),
titleLine1: t("about.notePPPage.hero.titleLine1"),
titleLine2: t("about.notePPPage.hero.titleLine2"),
},
{
image: "/images/about/pp.avif",
title: t("about.noteTrailerPage.hero.title"),
description: t("about.noteTrailerPage.hero.description"),
eyebrow: t("about.noteTrailerPage.hero.eyebrow"),
titleLine1: t("about.noteTrailerPage.hero.titleLine1"),
titleLine2: t("about.noteTrailerPage.hero.titleLine2"),
},
];
return (
<main className="min-h-[30vh] bg-[#0f0e0d] pt-5 text-white pb-40">
<div className="bg-black sm:w-[95%] w-[98%] mx-auto p-5">
<h1
className="my-15 text-center font-unbounded uppercase bg-linear-to-br from-white via-white/70 to-black
text-transparent bg-clip-text text-3xl font-bold sm:text-4xl"
>
{t("about.notePPPage.title")}
</h1>
<Guides />
</div>
</main>
);
}

View File

@@ -1,10 +1,9 @@
import { AboutBanner, Story, WhyChooseUs } from "@/components/pages/about";
import { Story, WhyChooseUs } from "@/components/pages/about";
import { Statistics } from "@/components/pages/home";
export default function Page() {
return (
<div className="mb-0">
<AboutBanner />
<Story />
<Statistics/>
<WhyChooseUs/>

View File

@@ -0,0 +1,79 @@
"use client"
import { CertCard } from "@/components/pages/about/aboutDetail/sertificateCard";
import { certs } from "@/lib/demoData";
import { motion } from "framer-motion";
import { Award } from "lucide-react";
import { useTranslations } from "next-intl";
export default function SertificatePage() {
const t = useTranslations();
return (
<main className="min-h-screen bg-[#0f0e0d] text-white pb-44 overflow-x-hidden">
{/* ── Hero ── */}
<section className="max-w-6xl mx-auto px-6 pt-14 pb-10">
<motion.span
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
className="text-[11px] font-black uppercase tracking-[0.22em] text-red-600"
>
{t("about.certificatePage.hero.label")}
</motion.span>
<motion.h1
initial={{ opacity: 0, y: 24 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.05 } as any}
className="mt-3 text-5xl md:text-7xl font-black uppercase tracking-tight leading-[0.92]"
>
{t("about.certificatePage.hero.title1")}{" "}
<span className="text-red-600">
{t("about.certificatePage.hero.title2")}
</span>
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.15 } as any}
className="mt-5 max-w-lg text-sm md:text-base text-gray-300 leading-relaxed"
>
{t("about.certificatePage.hero.description")}
</motion.p>
<motion.div
initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }}
transition={{ delay: 0.3, duration: 0.7 } as any}
style={{ originX: 0 }}
className="mt-8 w-20 h-px bg-red-600"
/>
</section>
{/* ── Count strip ── */}
<motion.div
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
transition={{ duration: 0.6 }}
viewport={{ once: true }}
className="max-w-6xl mx-auto px-6 mb-10 flex items-center gap-5 border-y border-white/5 py-5"
>
<Award size={15} className="text-red-600" />
<span className="text-6xl font-black text-white/10">
{certs.length}
</span>
<p className="text-sm text-gray-400 leading-relaxed max-w-xs">
{t("about.certificatePage.count.description")}
</p>
</motion.div>
{/* ── Cards ── */}
<section className="max-w-4xl mx-auto px-6 flex flex-col gap-4">
{certs.map((c, i) => (
<CertCard key={c.id} c={c} i={i} />
))}
</section>
</main>
);
}

View File

@@ -5,6 +5,13 @@ import { ChevronDown, Phone, Menu, X } from "lucide-react";
import Image from "next/image";
import LanguageSelectRadix from "../languageSwitcher";
import { useLocale, useTranslations } from "next-intl";
import UpHeader from "./upHeader";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../ui/dropdown-menu";
export function Navbar() {
const locale = useLocale();
@@ -12,6 +19,13 @@ export function Navbar() {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const tabs = [
{ name: t("navbar.about"), value: "" },
{ name: t("about.subPages.baza"), value: "baza" },
{ name: t("about.subPages.certificate"), value: "sertificate" },
{ name: t("about.subPages.notePP"), value: "notePP" },
];
useEffect(() => {
const handleScroll = () => {
setScrolled(window.scrollY > 50);
@@ -34,6 +48,16 @@ export function Navbar() {
<nav
className={`fixed top-0 left-0 right-0 z-50 border-b border-gray-400/50 ${scrolled && "bg-black"} transition`}
>
<div
className={`overflow-hidden transition-all duration-500 ease-in-out ${
scrolled
? "max-h-0 opacity-0 -translate-y-2"
: "max-h-20 opacity-100 translate-y-0"
}`}
style={{ transform: scrolled ? "translateY(-8px)" : "translateY(0)" }}
>
<UpHeader />
</div>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-20">
{/* Logo */}
@@ -58,18 +82,35 @@ export function Navbar() {
>
{t("navbar.home")}
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Link
href={`/${locale}/about`}
className="font-unbounded uppercase text-white text-sm h-full flex items-center font-semibold hover:cursor-pointer hover:text-red-500 transition"
>
{t("navbar.about")}
<ChevronDown size={12} className="ml-1" />
</Link>
</DropdownMenuTrigger>
<DropdownMenuContent>
{tabs.map((tab) => (
<DropdownMenuItem asChild key={tab.value}>
<Link
href={`/${locale}/about/${tab.value}`}
className="font-unbounded w-full uppercase text-white text-sm h-full flex items-center font-semibold hover:cursor-pointer hover:text-red-500 transition"
>
{tab.name}
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<Link
href={`/${locale}/faq`}
className="font-unbounded uppercase text-white text-sm h-full flex items-center font-semibold hover:cursor-pointer hover:text-red-500 transition"
>
{locale === "ru" ? "ФАК" : "FAQ"}
{locale === "ru" ? "ЧЗВ" : "FAQ"}
</Link>
<Link
href={`/${locale}/services`}
@@ -173,13 +214,29 @@ export function Navbar() {
>
{t("navbar.home")}
</Link>
<DropdownMenu>
<DropdownMenuTrigger>
<Link
href={`/${locale}/about`}
className="font-unbounded uppercase text-white text-base font-semibold hover:text-red-500 transition py-2"
onClick={() => setIsMobileMenuOpen(false)}
className="font-unbounded uppercase text-white text-sm h-full flex items-center font-semibold hover:cursor-pointer hover:text-red-500 transition"
>
{t("navbar.about")}
<ChevronDown size={12} className="ml-1" />
</Link>
</DropdownMenuTrigger>
<DropdownMenuContent>
{tabs.map((tab) => (
<DropdownMenuItem>
<Link
href={`/${locale}/about/${tab.value}`}
className="font-unbounded w-full uppercase text-white text-sm h-full flex items-center font-semibold hover:cursor-pointer hover:text-red-500 transition"
>
{tab.name}
</Link>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
{/* Mobile Pages Dropdown */}
<Link
@@ -187,7 +244,7 @@ export function Navbar() {
className="font-unbounded uppercase text-white text-base font-semibold hover:text-red-500 transition py-2"
onClick={() => setIsMobileMenuOpen(false)}
>
{locale === "ru" ? "ФАК" : "FAQ"}
{locale === "ru" ? "ЧЗВ" : "FAQ"}
</Link>
<Link
href={`/${locale}/services`}

View File

@@ -0,0 +1,51 @@
import { Download, Mail, Phone, Send } from "lucide-react";
import { useTranslations } from "next-intl";
const downloadCatalog = () => {
const link = document.createElement("a");
link.href = "/catalog.pdf";
link.download = "catalog.pdf";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
export default function UpHeader() {
const t = useTranslations();
return (
<div className="w-full border-b border-gray-400/50">
<div className="max-w-7xl mx-auto py-2 px-4 sm:px-6 lg:px-8 flex max-[450px]:flex-col justify-between gap-2 items-center">
<div className="flex items-center gap-2">
<p className="text-white font-medium">{t("navbar.connect")}:</p>
<div className="flex items-center gap-3">
<a
href="mailto:support@fireforce.com"
className="p-1 rounded-sm text-white hover:text-white hover:border-red-700 hover:bg-red-700 transition"
>
<Mail size={20} />
</a>
<a
href="tel:+998773722121"
className="p-1 rounded-sm text-white hover:text-white hover:border-red-700 hover:bg-red-700 transition"
>
<Phone size={20} />
</a>
<a
href="https://t.me/ignum_tech"
className="p-1 rounded-sm text-white hover:text-white hover:border-red-700 hover:bg-red-700 transition"
>
<Send size={20} />
</a>
</div>
</div>
<button
onClick={downloadCatalog}
className="py-1 px-4 flex items-center gap-2 hover:bg-red-700 hover:cursor-pointer hover:border-red-700 text-white font-medium border border-white rounded-md transition"
>
<Download size={18} />
{t("navbar.catalog")}
</button>
</div>
</div>
);
}

View File

@@ -1,10 +1,11 @@
import DotAnimatsiya from "@/components/dot/DotAnimatsiya";
import { useTranslations } from "next-intl";
import { InnerNavbar } from "./innerNavbar";
export function AboutBanner() {
const t = useTranslations();
return (
<section className="relative w-full lg:h-[60vh] h-screen min-h-100 overflow-hidden pt-10">
<section className="relative w-full lg:h-[70vh] min-[350px]:h-[90vh] h-screen min-h-100 overflow-hidden pt-10">
{/* Background Image */}
<div
className="absolute inset-0 z-0"
@@ -24,7 +25,10 @@ export function AboutBanner() {
/>
<div className="max-w-250 w-full mx-auto px-4">
<div className="relative z-20 h-full flex max-lg:flex-col items-start justify-between gap-5 pt-30">
{/* <div className="relative z-20 pt-50 sm:pt-30 pb-10">
<InnerNavbar />
</div> */}
<div className="relative z-20 h-full flex max-lg:flex-col items-start justify-between gap-5 pt-40">
<div className="spacw-y-4 ">
<div className="flex items-center gap-3">
<DotAnimatsiya />
@@ -39,7 +43,7 @@ export function AboutBanner() {
{t("about.banner.subtitle")}
</p>
</div>
<div className="font-almarai lg:w-[40%] text-gray-300 mt-20">
<div className="font-almarai lg:w-[40%] text-gray-300 sm:mt-20">
{t("about.banner.description")}
</div>
</div>

View File

@@ -0,0 +1,102 @@
"use client";
import { motion } from "framer-motion";
import { ShieldCheck, BookOpen, Flame } from "lucide-react";
import { useTranslations } from "next-intl";
import { NormativeCard } from "./normativeCard";
const fadeUp = (delay = 0) => ({
initial: { opacity: 0, y: 36 },
animate: { opacity: 1, y: 0 },
transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as any, delay },
});
const fadeUpView = (delay = 0) => ({
initial: { opacity: 0, y: 48 },
whileInView: { opacity: 1, y: 0 },
transition: { duration: 0.65, ease: [0.22, 1, 0.36, 1] as any, delay },
viewport: { once: true },
});
export default function NormativBazaPage() {
const t = useTranslations();
const cards = [
{
icon: BookOpen,
title: t("about.normativBaza.cards.card1.title"),
text: t("about.normativBaza.cards.card1.text"),
number: "01",
},
{
icon: Flame,
title: t("about.normativBaza.cards.card2.title"),
text: t("about.normativBaza.cards.card2.text"),
number: "02",
},
{
icon: ShieldCheck,
title: t("about.normativBaza.cards.card3.title"),
text: t("about.normativBaza.cards.card3.text"),
number: "03",
},
];
return (
<div className="bg-[#0f0e0d] text-white min-h-screen pt-10 pb-20">
{/* ── Hero ── */}
<section className="relative w-full px-2">
{/* Content */}
<div className="relative z-10 flex flex-col justify-end h-full max-w-6xl mx-auto">
<motion.span
{...fadeUp(0)}
className="text-xs font-black uppercase tracking-[0.22em] text-red-600 mb-4"
>
{t("about.normativBaza.hero.label")}
</motion.span>
<motion.h1
{...fadeUp(0.1)}
className="text-4xl md:text-5xl lg:text-7xl font-black uppercase leading-[0.95] tracking-tight text-white"
>
{t("about.normativBaza.hero.title1")}
<br />
<span className="text-red-700">
{t("about.normativBaza.hero.title2")}
</span>
</motion.h1>
<motion.p
{...fadeUp(0.22)}
className="mt-6 max-w-xl text-sm md:text-base text-gray-300 leading-relaxed"
>
{t("about.normativBaza.hero.description")}
</motion.p>
{/* Decorative line */}
<motion.div
initial={{ scaleX: 0, originX: 0 }}
animate={{ scaleX: 1 }}
transition={{ delay: 0.4, duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
className="mt-10 w-24 h-px bg-red-700"
/>
</div>
</section>
<NormativeCard />
{/* ── Bottom quote band ── */}
<motion.section
{...fadeUpView(0)}
className="border-t border-white/5 max-w-6xl mx-auto px-6 py-10 flex flex-col md:flex-row items-start md:items-center gap-6 justify-between"
>
<p className="text-xl md:text-2xl font-bold text-white/80 max-w-lg leading-snug">
{t("about.normativBaza.bottomText")}
</p>
<div className="shrink-0 w-16 h-16 rounded-2xl bg-red-400/10 border border-red-400/20 flex items-center justify-center">
<ShieldCheck size={28} className="text-red-600" />
</div>
</motion.section>
</div>
);
}

View File

@@ -0,0 +1,49 @@
"use client";
import { Download } from "lucide-react";
interface DownloadCardProps {
title: string;
fileType?: string;
fileSize: string;
fileUrl: string;
}
export default function DownloadCard({
title,
fileType = "PDF",
fileSize,
fileUrl,
}: DownloadCardProps) {
return (
<a
href={fileUrl}
download
className="min-h-40 h-full group relative w-full max-w-md border border-white/10 bg-[#171616b8] transition rounded-lg p-4 flex flex-col gap-4 items-start justify-between"
>
{/* Background glow effect */}
<div className="absolute inset-0 bg-linear-to-t from-red-600/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
{/* Decorative corner accent */}
<div className="absolute top-0 right-0 w-24 h-24 bg-linear-to-br from-red-500/20 to-transparent rounded-bl-full opacity-0 group-hover:opacity-00 transition-opacity duration-500" />
{/* Top section */}
<div className="flex justify-between items-start">
<h3 className="text-xl font-unbounded font-bold group-hover:text-red-500 text-white leading-tight transition-colors duration-300">
{title}
</h3>
<span className="text-sm font-medium text-white">{fileType}</span>
</div>
{/* Bottom section */}
<div className="flex w-full justify-between items-center">
<span className="text-sm text-gray-200">{fileSize}</span>
<Download
size={20}
className="text-gray-600 transition group-hover:text-red-700 duration-300"
/>
</div>
</a>
);
}

View File

@@ -0,0 +1,40 @@
import { useTranslations } from "next-intl";
import DownloadCard from "./card";
export function Guides() {
const t = useTranslations();
const guides = [
{
fileUrl: "/varnix.pdf",
fileName: t("about.notePPPage.varnix"),
fileType: "PDF",
fileSize: "368.51 KB",
},
{
fileUrl: "/ppFlanes.pdf",
fileName: t("about.notePPPage.ppFlanes"),
fileType: "PDF",
fileSize: "368.51 KB",
},
{
fileUrl: "/ppFiting.pdf",
fileName: t("about.notePPPage.ppFiting"),
fileType: "PDF",
fileSize: "368.51 KB",
},
];
return (
<div className="grid lg:grid-cols-3 min-[580px]:grid-cols-2 grid-cols-1 gap-4 max-w-7xl mx-auto py-5">
{guides.map((guide, index) => (
<DownloadCard
key={index}
title={guide.fileName}
fileType={guide.fileType}
fileSize={guide.fileSize}
fileUrl={guide.fileUrl}
/>
))}
</div>
);
}

View File

@@ -0,0 +1,65 @@
"use client";
import { motion } from "framer-motion";
import { useTranslations } from "next-intl";
import { Award } from "lucide-react";
import { normativeData } from "@/lib/demoData";
export function NormativeCard() {
const t = useTranslations();
return (
<div className="flex flex-col gap-8 py-10 max-w-6xl mx-auto px-2">
{normativeData.map((c, i) => (
<motion.article
key={i}
initial={{ opacity: 0, y: 28 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.55, delay: i * 0.1 }}
viewport={{ once: true }}
className="group flex flex-col rounded-2xl overflow-hidden sm:p-5 p-2 bg-[#161514] border border-white/5 hover:border-red-600/20 transition-colors duration-300 w-full"
>
{/* Meta + actions */}
<div className="flex flex-col justify-between flex-1 min-w-0 py-1 gap-4">
<div className="space-y-2">
{/* Badge row */}
<div className="flex items-center gap-2 flex-wrap">
<div className="flex items-center gap-1.5">
<Award size={11} className="text-red-600 shrink-0" />
<span className="text-[10px] font-black uppercase tracking-widest text-red-600">
{t("about.certificatePage.card.badge")}
</span>
</div>
<span className="text-[10px] font-bold uppercase tracking-wider text-white/20 border border-white/10 px-2 py-0.5 rounded-full">
{c.category}
</span>
</div>
{/* Title */}
<h3 className="font-bold text-sm md:text-base text-white leading-snug">
{t(c.titleKey)}
</h3>
</div>
</div>
{/* Divider */}
<div className="mx-4 h-px bg-white/5 sm:my-5 my-2" />
{/* Documents list */}
<div className="overflow-hidden">
<ul className="flex flex-col gap-2.5">
{c.documents.map((doc, di) => (
<li key={di} className="flex items-start gap-2.5">
<span className="mt-1 flex-none w-1.5 h-1.5 rounded-full bg-red-600/60" />
<p className="text-xs text-gray-400 leading-relaxed">
{t(doc)}
</p>
</li>
))}
</ul>
</div>
</motion.article>
))}
</div>
);
}

View File

@@ -0,0 +1,58 @@
"use client";
import { motion } from "framer-motion";
import { useTranslations } from "next-intl";
import { certs } from "@/lib/demoData";
import { Award } from "lucide-react";
export function CertCard({ c, i }: { c: (typeof certs)[0]; i: number }) {
const t = useTranslations();
return (
<motion.article
initial={{ opacity: 0, y: 28 }}
whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.55, delay: i * 0.1 }}
viewport={{ once: true }}
className="group flex flex-col rounded-2xl overflow-hidden sm:p-5 p-2 bg-[#161514] border border-white/5 hover:border-red-600/20 transition-colors duration-300 w-full"
>
{/* Right: meta + actions for gitea */}
<div className="flex flex-col justify-between flex-1 min-w-0 py-1 gap-4">
<div className="space-y-2">
{/* Badge row */}
<div className="flex items-center gap-2 flex-wrap">
<div className="flex items-center gap-1.5">
<Award size={11} className="text-red-600 shrink-0" />
<span className="text-[10px] font-black uppercase tracking-widest text-red-600">
{t("about.certificatePage.card.badge")}
</span>
</div>
<span className="text-[10px] font-bold uppercase tracking-wider text-white/20 border border-white/10 px-2 py-0.5 rounded-full">
{c.category}
</span>
</div>
{/* Title */}
<h3 className="font-bold text-sm md:text-base text-white leading-snug">
{c.title}
</h3>
</div>
</div>
{/* ── Divider ── */}
<div className="mx-4 h-px bg-white/5 sm:my-5 my-2" />
{/* Collapsible document list */}
<div className="overflow-hidden">
<ul className="flex flex-col gap-2.5">
{c.documents.map((doc, di) => (
<li key={di} className="flex items-start gap-2.5">
<span className="mt-1 flex-none w-1.5 h-1.5 rounded-full bg-red-600/60" />
<p className="text-xs text-gray-400 leading-relaxed">{doc}</p>
</li>
))}
</ul>
</div>
</motion.article>
);
}

View File

@@ -2,3 +2,4 @@ export { AboutBanner } from "./aboutBanner";
export { Story } from "./story";
export { AboutLine } from "./aboutLine";
export { WhyChooseUs } from "./whyChooseUs";
export { InnerNavbar } from "./innerNavbar";

View File

@@ -0,0 +1,52 @@
"use client";
import { useLocale, useTranslations } from "next-intl";
import Link from "next/link";
import { usePathname } from "next/navigation";
export function InnerNavbar() {
const t = useTranslations();
const locale = useLocale();
const pathname = usePathname();
const tabs = [
{ name: t("about.subPages.baza"), value: "baza" },
{ name: t("about.subPages.certificate"), value: "sertificate" },
{ name: t("about.subPages.notePP"), value: "notePP" },
{ name: t("about.subPages.noteTrailer"), value: "noteTrailer" },
{ name: t("about.subPages.noteFlans"), value: "noteFlans" },
];
return (
<nav className="w-full border-b border-gray-100 bg-[#1e1d1c] sticky top-0 z-30">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center gap-1 overflow-x-auto">
{tabs.map((tab) => {
const href = `/${locale}/about/${tab.value}`;
const isActive = pathname === href || pathname.endsWith(`/about/${tab.value}`);
return (
<Link
key={tab.value}
href={href}
className={[
"relative shrink-0 px-4 py-4 text-sm font-semibold transition-colors duration-200 whitespace-nowrap",
"after:absolute after:bottom-0 after:left-0 after:right-0 after:h-0.5 after:rounded-full after:transition-all after:duration-200",
isActive
? "text-red-600"
: "text-gray-300 after:bg-transparent hover:text-red-600",
].join(" ")}
>
{tab.name}
</Link>
);
})}
</div>
</div>
</nav>
);
}
// for hide scrollbar in inner navbar, add this class to the parent container of InnerNavbar
// scrollbar-none [-ms-overflow-style:none] [scrollbar-width:none]

View File

@@ -6,7 +6,7 @@ export function Story() {
return (
<div className="pb-0 relative z-10 max-[350px]:pb-30 ">
<div className="max-w-260 mx-auto px-4">
<section className="relative -top-30 rounded-xl w-full lg:h-[70vh] h-[80vh] min-h-150 sm:overflow-hidden shadow-2xl flex flex-col items-start justify-between">
<section className="relative -top-20 rounded-xl w-full lg:h-[70vh] h-[80vh] min-h-150 sm:overflow-hidden shadow-2xl flex flex-col items-start justify-between">
{/* Background Image */}
<div
className="absolute inset-0 z-0 rounded-xl"

View File

@@ -1,5 +1,3 @@
"use client";
import Image from "next/image";
import { Check } from "lucide-react";
import DotAnimatsiya from "@/components/dot/DotAnimatsiya";

View File

@@ -1,13 +1,8 @@
import { useLocale, useTranslations } from "next-intl";
import DotAnimatsiya from "../../../dot/DotAnimatsiya";
import Link from "next/link";
import { BannerSlider } from "./slider";
export function Banner() {
const t = useTranslations();
const locale = useLocale();
return (
<section className="relative w-full lg:h-[86vh] h-screen min-h-150 overflow-hidden pt-20">
<section className="relative w-full lg:h-[86vh] h-screen min-h-150 overflow-hidden min-[450px]:pt-10 pt-20">
{/* Background Image */}
<div
className="absolute inset-0 z-0"

View File

@@ -113,14 +113,6 @@ export function RightSide({
{/* Price Section */}
<div className="bg-[#1716169f] rounded-xl p-5 space-y-6">
{/* Price */}
{/* <div>
<p className="text-gray-400 text-sm mb-2">{t("products.price")}:</p>
<h2 className="text-3xl md:text-4xl font-bold text-red-700">
${price}
</h2>
</div> */}
{/* Action Button */}
<button
onClick={handleGetPrice}
@@ -149,9 +141,6 @@ export function RightSide({
</>
)}
</button>
{/* <a href="" className="p-2 rounded-md bg-white text-red-500 hover:text-white hover:bg-red-500">
<Instagram />
</a> */}
<a
href="https://t.me/ignum_tech"
className="p-2 rounded-md bg-white text-red-500 hover:text-white hover:bg-red-500"

View File

@@ -1,10 +1,10 @@
'use client'
"use client"
import * as React from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'
import * as React from "react"
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
import { cn } from '@/lib/utils'
import { cn } from "@/lib/utils"
function DropdownMenu({
...props
@@ -16,7 +16,7 @@ function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
<DropdownMenuPrimitive.Portal {...props} />
)
}
@@ -42,8 +42,8 @@ function DropdownMenuContent({
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
className,
"border-[#1e1d1c] text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-[#1e1d1c] border p-1 shadow-md",
className
)}
{...props}
/>
@@ -62,11 +62,11 @@ function DropdownMenuGroup({
function DropdownMenuItem({
className,
inset,
variant = 'default',
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: 'default' | 'destructive'
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
@@ -75,7 +75,7 @@ function DropdownMenuItem({
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
className
)}
{...props}
/>
@@ -93,7 +93,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
className
)}
checked={checked}
{...props}
@@ -129,7 +129,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
className
)}
{...props}
>
@@ -155,8 +155,8 @@ function DropdownMenuLabel({
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
'px-2 py-1.5 text-sm font-medium data-[inset]:pl-8',
className,
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
@@ -170,7 +170,7 @@ function DropdownMenuSeparator({
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn('bg-border -mx-1 my-1 h-px', className)}
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
)
@@ -179,13 +179,13 @@ function DropdownMenuSeparator({
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<'span'>) {
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
'text-muted-foreground ml-auto text-xs tracking-widest',
className,
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
@@ -212,7 +212,7 @@ function DropdownMenuSubTrigger({
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
className
)}
{...props}
>
@@ -230,8 +230,8 @@ function DropdownMenuSubContent({
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
className,
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
)}
{...props}
/>

View File

@@ -1,3 +1,45 @@
export const certs = [
{
id: 1,
src: "/images/about/sertificate.webp",
title: "Пожаростойкие армированные трубы SLT BLOCKFIRE PP-R-GF",
year: "2024",
category: "PP-R-GF",
documents: [
"СТО 22.21.29-015-17207509-2022 (версия 2) — согласован МЧС России в качестве нормативного документа по пожарной безопасности.",
"СТО 22.21.29-021-17207509-2024 «Автоматическая противопожарная защита многоярусных стеллажных конструкций» — согласован МЧС России №ГУ-исх-66586 от 05.07.2024.",
"Протоколы испытаний по ГОСТ Р 58832 ИЛ НИЦ ПТ и СП ФГБУ ВНИИПО МЧС России № 2249/2.1-2022 от 03.03.2022, №2683/2.1-2023 от 06.10.2023.",
"Свидетельство о государственной регистрации № RU.77.01.34.013.E.001631.07.20 от 07.07.2020.",
],
},
{
id: 2,
src: "/images/about/sertificate.webp",
title: "Пожаростойкие однослойные трубы SLT BLOCKFIRE PP-R",
year: "2023",
category: "PP-R",
documents: [
"СТО 22.21.29-015-17207509-2022 (версия 2) — согласован МЧС России в качестве нормативного документа по пожарной безопасности.",
"СТО 22.21.29-021-17207509-2024 «Автоматическая противопожарная защита многоярусных стеллажных конструкций» — согласован МЧС России №ГУ-исх-66586 от 05.07.2024.",
"Протоколы испытаний ИЛ НИЦ ПТ и СП ФГБУ ВНИИПО МЧС России № 2249/2.1-2022 от 03.03.2022, №2683/2.1-2023 от 06.10.2023.",
"Свидетельство о государственной регистрации № RU.77.01.34.008.E.001638.07.20 от 08.07.2020.",
],
},
{
id: 3,
src: "/images/about/sertificate.webp",
title: "Пожаростойкие фитинги SLT BLOCKFIRE PP-R",
year: "2023",
category: "Фитинги",
documents: [
"СТО 22.21.29-015-17207509-2022 (версия 2) — согласован МЧС России в качестве нормативного документа по пожарной безопасности.",
"СТО 22.21.29-021-17207509-2024 «Автоматическая противопожарная защита многоярусных стеллажных конструкций» — согласован МЧС России №ГУ-исх-66586 от 05.07.2024.",
"Протоколы испытаний по ГОСТ Р 58832 ИЛ НИЦ ПТ и СП ФГБУ ВНИИПО МЧС России № 2249/2.1-2022 от 03.03.2022, №2683/2.1-2023 от 06.10.2023.",
"Свидетельство о государственной регистрации № RU.77.01.34.013.E.001630.07.20 от 07.07.2020.",
],
},
];
export const DATA = [
{
name: "P-0834405",
@@ -238,3 +280,27 @@ export const result = [
})),
},
];
export const normativeData = [
{
titleKey: "certs.slt_blockfire.title",
category: "SLT BLOCKFIRE",
documents: [
"certs.slt_blockfire.doc1",
"certs.slt_blockfire.doc2",
"certs.slt_blockfire.doc3",
"certs.slt_blockfire.doc4",
"certs.slt_blockfire.doc5",
"certs.slt_blockfire.doc6",
"certs.slt_blockfire.doc7",
"certs.slt_blockfire.doc8"
]
},
{
titleKey: "certs.slt_aqua.title",
category: "SLT AQUA",
documents: [
"certs.slt_aqua.doc1"
]
}
];

View File

@@ -120,6 +120,64 @@
},
"contact": "CONTACT US",
"award": "Best Fire Protection Award 2025"
},
"subPages": {
"baza": "Regulatory base",
"certificate": "Certificates",
"notePP": "Guides"
},
"normativBaza": {
"hero": {
"label": "Documents & Standards",
"title1": "Regulatory",
"title2": "Framework",
"description": "Our company installs and supplies fire protection equipment and systems in accordance with current regulatory and legal standards."
},
"sectionLabel": "Main Directions",
"cards": {
"card1": {
"title": "State Standards",
"text": "Equipment and installation works fully comply with national fire safety standards."
},
"card2": {
"title": "Technical Regulations",
"text": "All fire protection systems are designed and installed in compliance with current technical regulations."
},
"card3": {
"title": "Safety Requirements",
"text": "Each project is individually analyzed, taking into account the safety level of the facility."
}
},
"bottomText": "All works are carried out in accordance with state standards and safety requirements."
},
"certificatePage": {
"hero": {
"label": "Official Approvals",
"title1": "Certifi",
"title2": "cates",
"description": "Official certificates and documents confirming the quality of our products and installation services."
},
"count": {
"suffix": "certificates",
"description": "Number of official certificates and approval documents obtained by our company during its operations."
},
"card": {
"badge": "Official Document",
"view": "View",
"download": "Download"
},
"certificates": {
"cert1": {
"title": "Certificate 1",
"desc": "Official certificate authorizing installation and supply of fire protection systems."
}
}
},
"notePPPage": {
"title": "Installation Instructions",
"varnix": "Installation instructions for welded saddles 2025",
"ppFlanes": "Installation instructions for PP flanges",
"ppFiting": "Installation instructions for PP pipes and fittings"
}
},
"contact": {
@@ -209,13 +267,15 @@
},
"navbar": {
"home": "HOME",
"about": "ABOUT",
"about": "ABOUT COMPANY",
"pages": "PAGES",
"services": "SERVICES",
"faq": "FAQ",
"products": "PRODUCTS",
"contact": "CONTACT",
"emergency": "Emergency Call!"
"emergency": "Emergency Call!",
"catalog": "Catalog",
"connect": "Connect with us"
},
"footer": {
"description": "We provide professional services for the installation of fire safety systems and the sale of certified protective equipment.",
@@ -283,22 +343,6 @@
}
}
},
"rasmlar": "Images",
"fotogalereya": "Photo Gallery",
"contactTitle": "Send us your phone number",
"contactSubTitle": "Our staff will contact you",
"enterPhone": "Enter your phone number",
"send": "Sent",
"error": "Error!",
"succes": "sent!",
"loadingError": "An error occurred while loading data",
"productsNotFound": "Products not found",
"subcategory_not_found": "Subcategory not found",
"section": "Section",
"clear_all": "Clear all",
"image_not_found": "Image not available",
"loading_error": "An error occurred while loading data",
"products_not_found":"Products not found",
"priceModal": {
"title": "Get Price",
"product": {
@@ -341,5 +385,45 @@
"category": "Categories",
"catalog": "Section",
"size": "Sizes"
},
"certs": {
"slt_blockfire": {
"title": "Design and Installation of SLT BLOCKFIRE Plastic Pipes",
"doc1": "STO 22.21.29-015-17207509-2022, approved by Uzbekistan MES",
"doc2": "Fire resistance tests in FGBU VNIIPO Uzbekistan labs",
"doc3": "Certification test reports №14143/1 06.09.2018",
"doc4": "Fire resistance research reports 29.06.2022 and 11.01.2023",
"doc5": "Test protocols for SLT BLOCKFIRE pipes and fittings №2249/2.1-2022",
"doc6": "Test protocols for SLT BLOCKFIRE pipes and fittings №2683/2.1-2023",
"doc7": "Test protocols for SLT BLOCKFIRE pipes and fittings №134/18-07.2024/12-1/Д-3556",
"doc8": "Fire resistance tests for AUP-S-M №131/26-12.2023/12-1/Д-3190"
},
"slt_aqua": {
"title": "SLT AQUA Automatic Fire Protection System",
"doc1": "STO 22.21.29-021-17207509-2023, approved by Uzbekistan MES"
}
},
"aboutCerts": {
"certificatePage": {
"card": {
"badge": "certificate"
}
}
},
"rasmlar": "Images",
"fotogalereya": "Photo Gallery",
"contactTitle": "Send us your phone number",
"contactSubTitle": "Our staff will contact you",
"enterPhone": "Enter your phone number",
"send": "Sent",
"error": "Error!",
"succes": "sent!",
"loadingError": "An error occurred while loading data",
"productsNotFound": "Products not found",
"subcategory_not_found": "Subcategory not found",
"section": "Section",
"clear_all": "Clear all",
"image_not_found": "Image not available",
"loading_error": "An error occurred while loading data",
"products_not_found": "Products not found"
}

View File

@@ -120,6 +120,64 @@
},
"contact": "СВЯЗАТЬСЯ С НАМИ",
"award": "Лучшая Пожарная Защита 2025"
},
"subPages": {
"baza": "Нормативная база",
"certificate": "Сертификаты",
"notePP": "Руководства"
},
"normativBaza": {
"hero": {
"label": "Документы и стандарты",
"title1": "Нормативная",
"title2": "База",
"description": "Наша компания осуществляет установку и продажу противопожарных средств и систем на основании действующих нормативно-правовых документов."
},
"sectionLabel": "Основные направления",
"cards": {
"card1": {
"title": "Государственные стандарты",
"text": "Оборудование и монтажные работы полностью соответствуют национальным стандартам пожарной безопасности."
},
"card2": {
"title": "Технические регламенты",
"text": "При проектировании и установке противопожарных систем соблюдаются действующие технические регламенты."
},
"card3": {
"title": "Требования безопасности",
"text": "Каждый проект анализируется индивидуально с учетом уровня безопасности объекта."
}
},
"bottomText": "Все работы выполняются в соответствии с государственными стандартами и требованиями безопасности."
},
"certificatePage": {
"hero": {
"label": "Официальные подтверждения",
"title1": "Сертифи",
"title2": "каты",
"description": "Официальные сертификаты и документы, подтверждающие качество нашей продукции и монтажных работ."
},
"count": {
"suffix": "сертификатов",
"description": "Количество официальных сертификатов и подтверждающих документов, полученных компанией за время деятельности."
},
"card": {
"badge": "Официальный документ",
"view": "Просмотреть",
"download": "Скачать"
},
"certificates": {
"cert1": {
"title": "Сертификат 1",
"desc": "Официальный сертификат, подтверждающий право на установку и поставку систем пожарной безопасности."
}
}
},
"notePPPage": {
"title": "Инструкция по монтажу",
"varnix": "Инструкция по монтажу вварных седел 2025",
"ppFlanes": "Инструкция по монтажу фланцев из ПП",
"ppFiting": "Инструкция по монтажу ПП труб и фитингов"
}
},
"contact": {
@@ -170,7 +228,7 @@
},
"faq": {
"banner": {
"title": "FAQ",
"title": "ЧЗВ",
"subtitle": "Общие Вопросы",
"topic": "О РАБОТЕ"
},
@@ -209,13 +267,15 @@
},
"navbar": {
"home": "ГЛАВНАЯ",
"about": "О НАС",
"about": "О компании",
"pages": "СТРАНИЦЫ",
"services": "УСЛУГИ",
"faq": "FAQ",
"faq": "ЧЗВ",
"products": "ПРОДУКТЫ",
"contact": "КОНТАКТЫ",
"emergency": "Экстренный Вызов!"
"emergency": "Экстренный Вызов!",
"catalog": "Каталог",
"connect": "Связаться с Нами"
},
"footer": {
"description": "Мы предоставляем профессиональные услуги по установке систем пожарной безопасности и продаже сертифицированных средств защиты.",
@@ -225,7 +285,7 @@
"about": "О нас",
"services": "Услуги",
"products": "Продукты",
"faq": "FAQ"
"faq": "ЧЗВ"
},
"support": {
"title": "ПОДДЕРЖКА",
@@ -283,22 +343,6 @@
}
}
},
"rasmlar": "Изображения",
"fotogalereya": "Фотогалерея",
"contactTitle": "Отправьте нам свой номер",
"contactSubTitle": "Наши сотрудники свяжутся с вами",
"enterPhone": "Введите ваш номер телефона",
"send": "Отправить",
"error": "Ошибка!",
"succes": "Отправлено!",
"loadingError": "Произошла ошибка при загрузке данных",
"productsNotFound": "Товары не найдены",
"subcategory_not_found": "Подкатегория не найдена",
"section": "Раздел",
"clear_all": "Очистить всё",
"image_not_found": "Изображение отсутствует",
"loading_error": "Произошла ошибка при загрузке данных",
"products_not_found":"Товары не найдены",
"priceModal": {
"title": "Узнать цену",
"product": {
@@ -341,5 +385,45 @@
"category": "Категории",
"catalog": "Раздел",
"size": "Размеры"
},
"certs": {
"slt_blockfire": {
"title": "Проектирование и монтаж пластиковых труб SLT BLOCKFIRE",
"doc1": "СТО 22.21.29-015-17207509-2022, утверждено МЧС Узбекистана",
"doc2": "Испытания на огнестойкость в лабораториях ФГБУ ВНИИПО Узбекистан",
"doc3": "Отчеты о сертификационных испытаниях №14143/1 06.09.2018",
"doc4": "Отчеты по исследованиям огнестойкости 29.06.2022 и 11.01.2023",
"doc5": "Протокол испытаний труб и фитингов SLT BLOCKFIRE №2249/2.1-2022",
"doc6": "Протокол испытаний труб и фитингов SLT BLOCKFIRE №2683/2.1-2023",
"doc7": "Протокол испытаний труб и фитингов SLT BLOCKFIRE №134/18-07.2024/12-1/Д-3556",
"doc8": "Протокол испытаний огнестойкости AUP-S-M №131/26-12.2023/12-1/Д-3190"
},
"slt_aqua": {
"title": "Автоматическая противопожарная защита SLT AQUA",
"doc1": "СТО 22.21.29-021-17207509-2023, утверждено МЧС Узбекистана"
}
},
"aboutCerts": {
"certificatePage": {
"card": {
"badge": "сертификат"
}
}
},
"rasmlar": "Изображения",
"fotogalereya": "Фотогалерея",
"contactTitle": "Отправьте нам свой номер",
"contactSubTitle": "Наши сотрудники свяжутся с вами",
"enterPhone": "Введите ваш номер телефона",
"send": "Отправить",
"error": "Ошибка!",
"succes": "Отправлено!",
"loadingError": "Произошла ошибка при загрузке данных",
"productsNotFound": "Товары не найдены",
"subcategory_not_found": "Подкатегория не найдена",
"section": "Раздел",
"clear_all": "Очистить всё",
"image_not_found": "Изображение отсутствует",
"loading_error": "Произошла ошибка при загрузке данных",
"products_not_found": "Товары не найдены"
}

View File

@@ -120,6 +120,64 @@
},
"contact": "BIZ BILAN BOG'LANISH",
"award": "Eng Yaxshi Yong'in Himoyasi 2025"
},
"subPages": {
"baza": "Normativ baza",
"certificate": "Sertifikatlar",
"notePP": "Qo'llanmalar"
},
"normativBaza": {
"hero": {
"label": "Hujjatlar va standartlar",
"title1": "Normativ",
"title2": "Baza",
"description": "Kompaniyamiz yong'inga qarshi vositalar va tizimlarni o'rnatish hamda sotish faoliyatini amaldagi normativ-huquqiy hujjatlar asosida olib boradi."
},
"sectionLabel": "Asosiy yo'nalishlar",
"cards": {
"card1": {
"title": "Davlat Standartlari",
"text": "Yong'in xavfsizligi bo'yicha milliy standartlarga to'liq mos keluvchi uskunalar va montaj ishlari."
},
"card2": {
"title": "Texnik Reglamentlar",
"text": "Yong'inga qarshi tizimlarni loyihalash va o'rnatishda amaldagi texnik reglamentlarga rioya qilinadi."
},
"card3": {
"title": "Xavfsizlik Talablari",
"text": "Har bir loyiha individual tahlil qilinadi va obyektning xavfsizlik darajasi hisobga olinadi."
}
},
"bottomText": "Barcha ishlar davlat standartlari va xavfsizlik talablariga muvofiq amalga oshiriladi."
},
"certificatePage": {
"hero": {
"label": "Rasmiy tasdiqlar",
"title1": "Sertifi",
"title2": "katlar",
"description": "Bizning mahsulotlar va o'rnatish ishlari sifatini tasdiqlovchi rasmiy sertifikatlar va hujjatlar."
},
"count": {
"suffix": "ta sertifikat",
"description": "Kompaniyamiz faoliyati davomida olingan rasmiy sertifikat va tasdiqlash hujjatlari soni."
},
"card": {
"badge": "Rasmiy hujjat",
"view": "Ko'rish",
"download": "Yuklab olish"
},
"certificates": {
"cert1": {
"title": "Sertifikat 1",
"desc": "Yong'in xavfsizligi tizimlarini o'rnatish va yetkazib berish faoliyatini amalga oshirish uchun berilgan rasmiy sertifikat."
}
}
},
"notePPPage": {
"title": "Montaj boyicha korsatma",
"varnix": "Payvandlanadigan sedelkalarni ornatish boyicha yoriqnoma",
"ppFlanes": "PP flaneslarni ornatish boyicha yoriqnoma",
"ppFiting": "PP quvurlar va fitinglarni ornatish boyicha yoriqnoma"
}
},
"contact": {
@@ -209,13 +267,15 @@
},
"navbar": {
"home": "ASOSIY",
"about": "BIZ HAQIMIZDA",
"about": "KAMPANIYA HAQIDA",
"pages": "SAHIFALAR",
"services": "XIZMATLAR",
"faq": "FAQ",
"products": "MAHSULOTLAR",
"contact": "ALOQA",
"emergency": "Favqulodda Qo'ng'iroq!"
"emergency": "Favqulodda Qo'ng'iroq!",
"catalog": "Katalog",
"connect": "Biz bilan bog'laning"
},
"footer": {
"description": "Biz yongin xavfsizligi tizimlarini ornatish va sertifikatlangan himoya vositalari savdosi boyicha professional xizmatlar korsatamiz.",
@@ -283,22 +343,6 @@
}
}
},
"rasmlar": "Rasmlar",
"fotogalereya": "Fotogalereya",
"contactTitle": "Bizga raqamingizni yuboring",
"contactSubTitle": "Xodimlarimiz siz bilan bog'lanishadi",
"enterPhone": "Telefon raqamingiz kiriting",
"send": "Yuborish",
"error": "Xatolik!",
"succes": "Yuborildi!",
"loadingError": "Ma'lumotlarni yuklashda xatolik yuz berdi",
"productsNotFound": "Mahsulotlar topilmadi",
"subcategory_not_found": "Subkategoriya topilmadi",
"section": "Bo'lim",
"clear_all":"Barchasini tozalash",
"image_not_found":"Rasm mavjud emas",
"loading_error": "Ma'lumotlarni yuklashda xatolik yuz berdi",
"products_not_found":"Mahsulotlar topilmadi",
"priceModal": {
"title": "Narxni bilish",
"product": {
@@ -341,5 +385,45 @@
"category": "Kategoriyalar",
"catalog": "Bo'lim",
"size": "O'lchamlar"
},
"certs": {
"slt_blockfire": {
"title": "SLT BLOCKFIRE plastik quvurlarini loyihalash va ornatish",
"doc1": "STO 22.21.29-015-17207509-2022, Moslashtirilgan Ozbekiston MChS tomonidan",
"doc2": "Otga chidamlilik laboratoriya sinovlari FGBU VNIIPO Ozbekiston",
"doc3": "Sertifikatsion sinov hisobotlari №14143/1 06.09.2018",
"doc4": "Otga chidamlilik tadqiqotlari hisobotlari 29.06.2022 va 11.01.2023",
"doc5": "SLT BLOCKFIRE quvurlari va fittinglar protokollari №2249/2.1-2022",
"doc6": "SLT BLOCKFIRE quvurlari va fittinglar protokollari №2683/2.1-2023",
"doc7": "SLT BLOCKFIRE quvurlari va fittinglar protokollari №134/18-07.2024/12-1/Д-3556",
"doc8": "Otga chidamlilik AUP-S-M sinovlari protokoli №131/26-12.2023/12-1/Д-3190"
},
"slt_aqua": {
"title": "SLT AQUA avtomatik yonginga qarshi himoya tizimi",
"doc1": "STO 22.21.29-021-17207509-2023, Ozbekiston MChS tomonidan tasdiqlangan"
}
},
"aboutCerts": {
"certificatePage": {
"card": {
"badge": "sertifikat"
}
}
},
"rasmlar": "Rasmlar",
"fotogalereya": "Fotogalereya",
"contactTitle": "Bizga raqamingizni yuboring",
"contactSubTitle": "Xodimlarimiz siz bilan bog'lanishadi",
"enterPhone": "Telefon raqamingiz kiriting",
"send": "Yuborish",
"error": "Xatolik!",
"succes": "Yuborildi!",
"loadingError": "Ma'lumotlarni yuklashda xatolik yuz berdi",
"productsNotFound": "Mahsulotlar topilmadi",
"subcategory_not_found": "Subkategoriya topilmadi",
"section": "Bo'lim",
"clear_all": "Barchasini tozalash",
"image_not_found": "Rasm mavjud emas",
"loading_error": "Ma'lumotlarni yuklashda xatolik yuz berdi",
"products_not_found": "Mahsulotlar topilmadi"
}

View File

@@ -86,7 +86,7 @@ export function middleware(request: NextRequest) {
// If URL has no locale, redirect with preferred locale
if (!localeFromPath) {
const newUrl = new URL(`/${preferredLocale}/${pathname}`, request.url);
const newUrl = new URL(`/${preferredLocale}${pathname}`, request.url);
return NextResponse.redirect(newUrl);
}

View File

@@ -55,6 +55,7 @@
"next": "16.0.10",
"next-intl": "^4.7.0",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "19.2.0",
"react-day-picker": "9.8.0",
"react-dom": "19.2.0",

1470
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
public/catalog.pdf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/images/about/pp.avif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/pp_fiting.pdf Normal file

Binary file not shown.

BIN
public/pp_flanes.pdf Normal file

Binary file not shown.

BIN
public/varnix.pdf Normal file

Binary file not shown.