Rebuild Docker setup with simplified nginx-only Dockerfile (build runs on host). Add category detail pages, IRANSans font files, product images, and update all landing sections. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
182 lines
7.0 KiB
Vue
182 lines
7.0 KiB
Vue
<script setup lang="ts">
|
|
import { ArrowLeft, BadgeCheck, PackageCheck, ShieldCheck } from "lucide-vue-next";
|
|
import { CATEGORIES } from "#shared/data/categories";
|
|
import Reveal from "~/components/ui/Reveal.vue";
|
|
import SectionHead from "~/components/ui/SectionHead.vue";
|
|
|
|
const CATEGORY_IMAGES: Record<string, string> = {
|
|
workwear: "/images/products/vest.jpg",
|
|
"safety-footwear": "/images/products/shoes.jpg",
|
|
"protective-gloves": "/images/products/gloves.jpg",
|
|
"safety-helmets": "/images/products/helmet.jpg",
|
|
"eye-protection": "/images/products/goggles.jpg",
|
|
"hearing-protection": "/images/products/hearing.jpg",
|
|
"respiratory-protection": "/images/products/respiratory.jpg",
|
|
"traffic-equipment": "/images/products/traffic.jpg",
|
|
};
|
|
|
|
const TRUST = [
|
|
{
|
|
icon: BadgeCheck,
|
|
title: "گارانتی اصالت کالا",
|
|
desc: "تمام محصولات با فاکتور رسمی و کد رهگیری اصالت",
|
|
},
|
|
{
|
|
icon: PackageCheck,
|
|
title: "تست نمونه قبل از خرید",
|
|
desc: "نمونههای نمایشی برای ارزیابی تیم HSE شما",
|
|
},
|
|
{
|
|
icon: ShieldCheck,
|
|
title: "امکان قرارداد سالانه",
|
|
desc: "قیمت ثابت و تأمین مستمر برای سازمانهای بزرگ",
|
|
},
|
|
];
|
|
|
|
const active = ref(0);
|
|
</script>
|
|
|
|
<template>
|
|
<section id="products" aria-labelledby="products-title" class="relative py-24 md:py-32">
|
|
<div class="mx-auto max-w-[1480px] px-5 md:px-8">
|
|
<div class="flex flex-wrap items-end justify-between gap-6">
|
|
<SectionHead
|
|
heading-id="products-title"
|
|
tag="۰۱ — محصولات"
|
|
desc="هر محصول پیش از عرضه، زیر ذرهبین کنترل کیفیت و تطبیق با استانداردهای جهانی قرار میگیرد."
|
|
>
|
|
<template #title>
|
|
خط اول دفاع،
|
|
<br />
|
|
تجهیزات ماست
|
|
</template>
|
|
</SectionHead>
|
|
<Reveal :delay="0.2" class="hidden md:block">
|
|
<a
|
|
href="/#contact"
|
|
class="group inline-flex items-center gap-2 rounded-full border border-line px-6 py-3 text-sm text-muted transition-colors hover:border-accent hover:text-accent"
|
|
>
|
|
دریافت کاتالوگ کامل
|
|
<ArrowLeft class="size-4 transition-transform group-hover:-translate-x-1" />
|
|
</a>
|
|
</Reveal>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ---- mobile: card grid ---- -->
|
|
<div class="mx-auto mt-14 max-w-[1480px] px-5 md:hidden">
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<Reveal v-for="(cat, i) in CATEGORIES" :key="cat.code" :delay="i * 0.05">
|
|
<NuxtLink
|
|
:to="`/categories/${cat.slug}`"
|
|
class="group relative block aspect-[3/4] overflow-hidden rounded-xl border border-line transition-colors duration-300 active:border-accent/60"
|
|
>
|
|
<img
|
|
v-if="CATEGORY_IMAGES[cat.slug]"
|
|
:src="CATEGORY_IMAGES[cat.slug]"
|
|
:alt="cat.name"
|
|
loading="lazy"
|
|
decoding="async"
|
|
class="absolute inset-0 size-full object-cover transition-transform duration-500 group-active:scale-105"
|
|
/>
|
|
<div v-else class="absolute inset-0 bg-panel" />
|
|
<div class="absolute inset-0 bg-gradient-to-t from-bg via-bg/50 to-transparent" />
|
|
|
|
<div class="absolute inset-x-0 top-0 flex items-center justify-between p-3">
|
|
<span class="font-mono text-[9px] tracking-widest text-ink/50">{{ cat.code }}</span>
|
|
<span class="font-mono text-[10px] text-accent">
|
|
{{ faNum(String(i + 1).padStart(2, "0")) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="absolute inset-x-0 bottom-0 p-3">
|
|
<h3 class="text-sm font-black leading-6">{{ cat.name }}</h3>
|
|
<p class="mt-0.5 font-mono text-[8px] tracking-[0.15em] text-muted">{{ cat.en }}</p>
|
|
</div>
|
|
</NuxtLink>
|
|
</Reveal>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ---- desktop: accordion panels ---- -->
|
|
<div class="mx-auto mt-14 hidden max-w-[1480px] px-8 md:block">
|
|
<div class="flex h-[74vh] min-h-[580px] flex-row gap-2">
|
|
<NuxtLink
|
|
v-for="(cat, i) in CATEGORIES"
|
|
:key="cat.code"
|
|
:to="`/categories/${cat.slug}`"
|
|
data-hover
|
|
:class="`group relative cursor-pointer overflow-hidden rounded-xl border transition-all duration-500 ease-[cubic-bezier(0.22,1,0.36,1)] ${
|
|
active === i
|
|
? 'flex-[3.4] border-accent/60'
|
|
: 'flex-[1] border-line hover:border-ink/30'
|
|
}`"
|
|
@mouseenter="active = i"
|
|
>
|
|
<img
|
|
v-if="CATEGORY_IMAGES[cat.slug]"
|
|
:src="CATEGORY_IMAGES[cat.slug]"
|
|
:alt="cat.name"
|
|
loading="lazy"
|
|
decoding="async"
|
|
:class="`absolute inset-0 size-full object-cover transition-all duration-700 ${
|
|
active === i ? 'scale-100 grayscale-0' : 'scale-110 grayscale group-hover:grayscale-0'
|
|
}`"
|
|
/>
|
|
<div
|
|
v-else
|
|
class="absolute inset-0 bg-panel"
|
|
/>
|
|
<div
|
|
:class="`absolute inset-0 transition-opacity duration-700 ${
|
|
active === i
|
|
? 'bg-gradient-to-t from-bg via-bg/35 to-bg/5'
|
|
: 'bg-gradient-to-t from-bg/95 via-bg/30 to-transparent'
|
|
}`"
|
|
/>
|
|
|
|
<div class="absolute inset-x-0 top-0 flex items-center justify-between p-5">
|
|
<span class="font-mono text-[10px] tracking-widest text-ink/60">{{ cat.code }}</span>
|
|
<span
|
|
:class="`font-mono text-xs transition-colors duration-500 ${
|
|
active === i ? 'text-accent' : 'text-ink/40'
|
|
}`"
|
|
>
|
|
{{ faNum(String(i + 1).padStart(2, "0")) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div class="absolute inset-x-0 bottom-0 p-6">
|
|
<h3
|
|
:class="`font-black transition-all duration-500 ${
|
|
active === i ? 'text-2xl' : 'text-sm'
|
|
}`"
|
|
>
|
|
{{ cat.name }}
|
|
</h3>
|
|
<p class="mt-1 font-mono text-[10px] tracking-[0.2em] text-muted">{{ cat.en }}</p>
|
|
</div>
|
|
</NuxtLink>
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
class="mx-auto mt-10 grid max-w-[1480px] gap-px overflow-hidden rounded-xl border border-line bg-line md:grid-cols-3"
|
|
>
|
|
<Reveal v-for="(t, i) in TRUST" :key="t.title" :delay="i * 0.08">
|
|
<div class="flex h-full items-center gap-4 bg-panel/60 p-6">
|
|
<span
|
|
class="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10 text-accent"
|
|
>
|
|
<component :is="t.icon" class="size-6" />
|
|
</span>
|
|
<div>
|
|
<div class="font-bold">{{ t.title }}</div>
|
|
<div class="mt-1 text-xs leading-6 text-muted">{{ t.desc }}</div>
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
</section>
|
|
</template>
|