انتقال تمام محتوای متنی سایت به دیتابیس — همه متنهای فرانتاند از GET /api/site خوانده میشوند
This commit is contained in:
@@ -3,20 +3,21 @@ import { ArrowLeftIcon } from 'lucide-react';
|
||||
import { Reveal } from '../Reveal';
|
||||
import { ButtonLink } from '../Button';
|
||||
import { ArticleCard } from '../ArticleCard';
|
||||
import { useSite } from '../../api';
|
||||
import { useSectionHeading, useSite } from '../../api';
|
||||
|
||||
export function Articles() {
|
||||
const { articles } = useSite();
|
||||
const heading = useSectionHeading('articles');
|
||||
return (
|
||||
<section className="bg-char py-24 lg:py-32" aria-labelledby="articles-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<Reveal className="max-w-2xl text-right">
|
||||
<p className="text-sm font-light text-copper">وبلاگ</p>
|
||||
<p className="text-sm font-light text-copper">{heading.label}</p>
|
||||
<h2 id="articles-title" className="mt-5 text-section font-bold text-ivory">
|
||||
آخرین مقالات
|
||||
{heading.title}
|
||||
</h2>
|
||||
<p className="mt-5 text-base font-light leading-9 text-ivory/60">
|
||||
یادداشتهای تیم معماری، اجرا و سرمایهگذاری گروه ساختمانی آرامش درباره آنچه در کارگاه و بازار میگذرد.
|
||||
{heading.lead}
|
||||
</p>
|
||||
<div className="mt-6 h-px w-24 bg-copper/70" />
|
||||
</Reveal>
|
||||
@@ -29,14 +30,16 @@ export function Articles() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Reveal delay={0.1}>
|
||||
<div className="mt-12 flex justify-center">
|
||||
<ButtonLink to="/blog" variant="outline" className="font-light">
|
||||
همه مقالات
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</Reveal>
|
||||
{heading.cta_label && heading.cta_href && (
|
||||
<Reveal delay={0.1}>
|
||||
<div className="mt-12 flex justify-center">
|
||||
<ButtonLink to={heading.cta_href} variant="outline" className="font-light">
|
||||
{heading.cta_label}
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</Reveal>
|
||||
)}
|
||||
</div>
|
||||
</section>);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { PlusIcon } from 'lucide-react'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { SectionHeading } from '../SectionHeading'
|
||||
import { useSite } from '../../api'
|
||||
import { useSectionHeading, useSite } from '../../api'
|
||||
|
||||
type FaqProps = {
|
||||
hideHeading?: boolean;
|
||||
@@ -11,6 +11,7 @@ type FaqProps = {
|
||||
|
||||
export function Faq({ hideHeading = false }: FaqProps) {
|
||||
const { faqs } = useSite()
|
||||
const heading = useSectionHeading('faq')
|
||||
const [open, setOpen] = useState<number | null>(0)
|
||||
|
||||
return (
|
||||
@@ -21,8 +22,8 @@ export function Faq({ hideHeading = false }: FaqProps) {
|
||||
{!hideHeading && (
|
||||
<div className="lg:col-span-4">
|
||||
<SectionHeading
|
||||
title="سوالات متداول"
|
||||
lead="پاسخ کوتاه به پرتکرارترین پرسشهای سرمایهگذاران و خریداران."
|
||||
title={heading.title}
|
||||
lead={heading.lead}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -5,26 +5,25 @@ import { Reveal } from '../Reveal'
|
||||
import { ButtonLink } from '../Button'
|
||||
import { ProjectCard } from '../ProjectCard'
|
||||
import { SectionHeading } from '../SectionHeading'
|
||||
import { useSite } from '../../api'
|
||||
import { useSectionHeading, useSite, useStatusLabel } from '../../api'
|
||||
import type { ProjectStatus } from '../../data/site'
|
||||
|
||||
const FILTERS: { key: ProjectStatus; label: string }[] = [
|
||||
{ key: 'done', label: 'انجام شده' },
|
||||
{ key: 'progress', label: 'در حال انجام' },
|
||||
]
|
||||
|
||||
export function FeaturedProjects() {
|
||||
const { projects } = useSite()
|
||||
const heading = useSectionHeading('featured_projects')
|
||||
const doneLabel = useStatusLabel('done')
|
||||
const progressLabel = useStatusLabel('progress')
|
||||
const FILTERS: { key: ProjectStatus; label: string }[] = [
|
||||
{ key: 'done', label: doneLabel },
|
||||
{ key: 'progress', label: progressLabel },
|
||||
]
|
||||
const [status, setStatus] = useState<ProjectStatus>('done')
|
||||
const list = projects.filter((p) => p.status === status).slice(0, 3)
|
||||
|
||||
return (
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="projects-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<SectionHeading
|
||||
title="پروژههای شاخص"
|
||||
lead="کارهایی که بیشترین چیز را درباره استاندارد ما میگویند؛ چه آنهایی که تحویل شدهاند و چه آنهایی که همین امروز در حال ساختاند."
|
||||
/>
|
||||
<SectionHeading title={heading.title} lead={heading.lead} />
|
||||
|
||||
<div className="mt-10 flex flex-wrap items-center gap-3" role="tablist" aria-label="فیلتر وضعیت پروژه">
|
||||
{FILTERS.map((filter) => (
|
||||
@@ -78,14 +77,16 @@ export function FeaturedProjects() {
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
<Reveal delay={0.1}>
|
||||
<div className="mt-12 flex justify-center">
|
||||
<ButtonLink to="/projects" variant="outline" className="font-light">
|
||||
همه پروژهها
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</Reveal>
|
||||
{heading.cta_label && heading.cta_href && (
|
||||
<Reveal delay={0.1}>
|
||||
<div className="mt-12 flex justify-center">
|
||||
<ButtonLink to={heading.cta_href} variant="outline" className="font-light">
|
||||
{heading.cta_label}
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</Reveal>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -3,17 +3,17 @@ import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { ChevronLeftIcon, ChevronRightIcon, XIcon } from 'lucide-react'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { SectionHeading } from '../SectionHeading'
|
||||
import { IMAGES } from '../../data/site'
|
||||
|
||||
const GRID = [IMAGES[0], IMAGES[1], IMAGES[2], IMAGES[3], IMAGES[4], IMAGES[1]]
|
||||
import { useSectionHeading, useSite } from '../../api'
|
||||
|
||||
export function Gallery() {
|
||||
const { gallery } = useSite()
|
||||
const heading = useSectionHeading('gallery')
|
||||
const [active, setActive] = useState<number | null>(null)
|
||||
|
||||
const close = useCallback(() => setActive(null), [])
|
||||
const step = useCallback((dir: number) => {
|
||||
setActive((prev) => (prev === null ? prev : (prev + dir + GRID.length) % GRID.length))
|
||||
}, [])
|
||||
setActive((prev) => (prev === null ? prev : (prev + dir + gallery.length) % gallery.length))
|
||||
}, [gallery.length])
|
||||
|
||||
useEffect(() => {
|
||||
if (active === null) return
|
||||
@@ -30,14 +30,14 @@ export function Gallery() {
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="gallery-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<SectionHeading
|
||||
title="گالری تصاویر"
|
||||
lead="قابهایی از کارگاهها و پروژههای تحویلشده گروه ساختمانی آرامش."
|
||||
title={heading.title}
|
||||
lead={heading.lead}
|
||||
align="center"
|
||||
/>
|
||||
|
||||
<div className="mt-14 grid grid-cols-2 gap-4 lg:grid-cols-3">
|
||||
{GRID.map((src, i) => (
|
||||
<Reveal key={`${src}-${i}`} delay={0.04 * i}>
|
||||
{gallery.map((image, i) => (
|
||||
<Reveal key={image.id} delay={0.04 * i}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActive(i)}
|
||||
@@ -45,8 +45,8 @@ export function Gallery() {
|
||||
aria-label={`بزرگنمایی تصویر شماره ${(i + 1).toLocaleString('fa-IR')}`}
|
||||
>
|
||||
<img
|
||||
src={src}
|
||||
alt={`تصویری از پروژههای گروه ساختمانی آرامش، شماره ${(i + 1).toLocaleString('fa-IR')}`}
|
||||
src={image.src}
|
||||
alt={image.alt}
|
||||
className="aspect-[4/3] w-full object-cover transition-transform duration-500 ease-lux group-hover:scale-105"
|
||||
/>
|
||||
</button>
|
||||
@@ -102,7 +102,7 @@ export function Gallery() {
|
||||
|
||||
<motion.img
|
||||
key={active}
|
||||
src={GRID[active]}
|
||||
src={gallery[active].src}
|
||||
alt=""
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
|
||||
@@ -5,12 +5,27 @@ import { ButtonLink } from '../Button'
|
||||
import { useSite } from '../../api'
|
||||
|
||||
export function Hero() {
|
||||
const { brand } = useSite()
|
||||
const { brand, hero } = useSite()
|
||||
const { scrollY } = useScroll()
|
||||
const imageY = useTransform(scrollY, [0, 700], [0, 110])
|
||||
const contentY = useTransform(scrollY, [0, 700], [0, -55])
|
||||
const overlay = useTransform(scrollY, [0, 600], [0.5, 0.78])
|
||||
|
||||
// واژه highlight در میانه تیتر با رنگ مسی رندر میشود
|
||||
const renderTitle = () => {
|
||||
if (hero.title_highlight && hero.title.includes(hero.title_highlight)) {
|
||||
const [before, ...rest] = hero.title.split(hero.title_highlight)
|
||||
return (
|
||||
<>
|
||||
{before}
|
||||
<span className="text-copper">{hero.title_highlight}</span>
|
||||
{rest.join(hero.title_highlight)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
return hero.title
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="relative h-screen min-h-[640px] w-full overflow-hidden" aria-label="معرفی گروه ساختمانی آرامش">
|
||||
<motion.img
|
||||
@@ -40,7 +55,7 @@ export function Hero() {
|
||||
transition={{ duration: 0.7, delay: 2.1, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="text-sm font-light text-copper"
|
||||
>
|
||||
از سال ۱۳۸۳ — تهران، اصفهان، سواحل شمال
|
||||
{hero.kicker}
|
||||
</motion.p>
|
||||
|
||||
<motion.h1
|
||||
@@ -49,9 +64,7 @@ export function Hero() {
|
||||
transition={{ duration: 0.85, delay: 2.2, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="mt-6 text-hero font-bold text-ivory"
|
||||
>
|
||||
ساختمانی که آرامش را
|
||||
<span className="text-copper"> معماری </span>
|
||||
میکند
|
||||
{renderTitle()}
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
@@ -60,8 +73,7 @@ export function Hero() {
|
||||
transition={{ duration: 0.85, delay: 2.32, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="mt-7 max-w-xl text-base font-light leading-9 text-ivory/70"
|
||||
>
|
||||
طراحی، ساخت و سرمایهگذاری در پروژههای مسکونی و تجاری لوکس؛ با استانداردی که در جزئیات دیده میشود و در
|
||||
سالها دوام میآورد.
|
||||
{hero.subtitle}
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
@@ -70,12 +82,12 @@ export function Hero() {
|
||||
transition={{ duration: 0.85, delay: 2.44, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="mt-10 flex flex-wrap items-center gap-4"
|
||||
>
|
||||
<ButtonLink to="/projects" variant="copper">
|
||||
مشاهده پروژهها
|
||||
<ButtonLink to={hero.cta_primary_href} variant="copper">
|
||||
{hero.cta_primary_label}
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
<ButtonLink to="/contact" variant="outline" className="font-light">
|
||||
مشاوره سرمایهگذاری
|
||||
<ButtonLink to={hero.cta_secondary_href} variant="outline" className="font-light">
|
||||
{hero.cta_secondary_label}
|
||||
</ButtonLink>
|
||||
</motion.div>
|
||||
</div>
|
||||
@@ -86,10 +98,8 @@ export function Hero() {
|
||||
transition={{ duration: 0.9, delay: 2.6, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="absolute bottom-28 left-6 hidden w-60 border border-ivory/15 bg-char/60 p-6 backdrop-blur-md lg:block lg:left-10"
|
||||
>
|
||||
<p className="text-3xl font-bold text-copper">۴۷</p>
|
||||
<p className="mt-2 text-sm font-light leading-7 text-ivory/70">
|
||||
پروژه تحویلشده در بیستویک سال فعالیت حرفهای
|
||||
</p>
|
||||
<p className="text-3xl font-bold text-copper">{hero.badge_value}</p>
|
||||
<p className="mt-2 text-sm font-light leading-7 text-ivory/70">{hero.badge_label}</p>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
|
||||
@@ -98,7 +108,7 @@ export function Hero() {
|
||||
aria-label="رفتن به بخش بعدی"
|
||||
className="absolute bottom-8 right-1/2 z-10 flex translate-x-1/2 flex-col items-center gap-2 text-ivory/50 transition-colors duration-200 ease-lux hover:text-copper"
|
||||
>
|
||||
<span className="text-xs font-light">اسکرول کنید</span>
|
||||
<span className="text-xs font-light">{hero.scroll_label}</span>
|
||||
<ChevronDownIcon className="h-4 w-4 animate-bounce" />
|
||||
</a>
|
||||
</section>
|
||||
|
||||
@@ -3,9 +3,11 @@ import { CheckIcon, SendIcon } from 'lucide-react'
|
||||
import Swal from 'sweetalert2'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { Button } from '../Button'
|
||||
import { useSite } from '../../api'
|
||||
import { API_URL } from '../../config'
|
||||
|
||||
export function Newsletter() {
|
||||
const { newsletter } = useSite()
|
||||
const [email, setEmail] = useState('')
|
||||
const [status, setStatus] = useState<'idle' | 'error' | 'done'>('idle')
|
||||
const [sending, setSending] = useState(false)
|
||||
@@ -89,10 +91,10 @@ export function Newsletter() {
|
||||
<div className="mx-auto grid max-w-site items-center gap-8 px-6 lg:grid-cols-2 lg:px-10">
|
||||
<Reveal>
|
||||
<h2 id="newsletter-title" className="text-2xl font-bold leading-relaxed text-char md:text-3xl">
|
||||
خبرنامه گروه ساختمانی آرامش
|
||||
{newsletter.title}
|
||||
</h2>
|
||||
<p className="mt-3 max-w-md text-sm font-light leading-8 text-char/75">
|
||||
هر ماه یک ایمیل: پروژههای جدید، فرصتهای سرمایهگذاری و یادداشتهای تیم معماری.
|
||||
{newsletter.lead}
|
||||
</p>
|
||||
</Reveal>
|
||||
|
||||
@@ -100,7 +102,7 @@ export function Newsletter() {
|
||||
<form onSubmit={submit} className="flex flex-col gap-3 sm:flex-row" noValidate>
|
||||
<div className="flex-1">
|
||||
<label htmlFor="newsletter-email" className="sr-only">
|
||||
نشانی ایمیل
|
||||
{newsletter.placeholder}
|
||||
</label>
|
||||
<input
|
||||
id="newsletter-email"
|
||||
@@ -111,21 +113,21 @@ export function Newsletter() {
|
||||
setEmail(e.target.value)
|
||||
if (status !== 'idle') setStatus('idle')
|
||||
}}
|
||||
placeholder="نشانی ایمیل شما"
|
||||
placeholder={newsletter.placeholder}
|
||||
className="w-full border border-transparent bg-char px-5 py-4 text-left text-sm font-light text-ivory placeholder:text-ivory/40 transition-all duration-300 ease-lux focus:border-ivory focus:outline-none"
|
||||
aria-invalid={status === 'error'}
|
||||
/>
|
||||
{status === 'done' && (
|
||||
<p className="mt-2 flex items-center gap-1.5 text-xs font-light text-char" role="status">
|
||||
<CheckIcon className="h-3.5 w-3.5" />
|
||||
عضویت شما ثبت شد.
|
||||
{newsletter.success_text}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
<Button type="submit" variant="dark-ivory" disabled={sending}>
|
||||
<SendIcon className="h-4 w-4" />
|
||||
{sending ? 'در حال ارسال…' : 'عضویت'}
|
||||
{sending ? newsletter.sending_label : newsletter.button_label}
|
||||
</Button>
|
||||
</form>
|
||||
</Reveal>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
import React from 'react'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { ServiceIcon } from '../ServiceIcon'
|
||||
import { useSite } from '../../api'
|
||||
import { useSectionHeading, useSite } from '../../api'
|
||||
|
||||
export function Services() {
|
||||
const { services } = useSite()
|
||||
const heading = useSectionHeading('services')
|
||||
return (
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="services-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<Reveal className="max-w-2xl text-right">
|
||||
|
||||
|
||||
<h2 id="services-title" className="text-section font-bold text-ivory">
|
||||
خدمات
|
||||
{heading.title}
|
||||
</h2>
|
||||
<p className="mt-5 text-base font-light leading-9 text-ivory/60">
|
||||
از نخستین خط روی کاغذ تا تحویل کلید، مدیریت سرمایه و طراحی محوطه؛ همه مراحل در یک مجموعه و زیر یک نظام
|
||||
کنترل کیفیت.
|
||||
{heading.lead}
|
||||
</p>
|
||||
<div className="mt-6 h-px w-24 bg-copper/70" />
|
||||
</Reveal>
|
||||
|
||||
@@ -2,23 +2,18 @@ import React from 'react'
|
||||
import { ArrowLeftIcon } from 'lucide-react'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { ButtonLink } from '../Button'
|
||||
import { IMAGES } from '../../data/site'
|
||||
|
||||
const BENTO = [
|
||||
{ src: IMAGES[0], alt: 'نمای برج مسکونی آرامش در تهران' },
|
||||
{ src: IMAGES[1], alt: 'آتریوم مرکزی مجتمع تجاری پارسه' },
|
||||
{ src: IMAGES[2], alt: 'ویلاهای ساختهشده در سواحل شمال' },
|
||||
{ src: IMAGES[3], alt: 'نمای شبانه برج دوقلوی نیاوران' },
|
||||
]
|
||||
import { useSite } from '../../api'
|
||||
|
||||
type StoryProps = {
|
||||
showLink?: boolean;
|
||||
showLabel?: boolean;
|
||||
showQuote?: boolean;
|
||||
alignTop?: boolean;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Story({ showLink = true, showLabel = true, alignTop = false, children }: StoryProps) {
|
||||
export function Story({ showLink = true, showLabel = true, showQuote = false, alignTop = false, children }: StoryProps) {
|
||||
const { story } = useSite()
|
||||
return (
|
||||
<section id="story" className="bg-ink py-24 lg:py-32" aria-labelledby="story-title">
|
||||
<div className={`mx-auto grid max-w-site gap-14 px-6 lg:grid-cols-2 lg:px-10 ${alignTop ? 'items-start' : 'items-center'}`}>
|
||||
@@ -26,40 +21,46 @@ export function Story({ showLink = true, showLabel = true, alignTop = false, chi
|
||||
{showLabel && (
|
||||
<Reveal>
|
||||
<h2 id="story-title" className="text-section font-bold text-ivory">
|
||||
درباره ی ما
|
||||
{story.title}
|
||||
</h2>
|
||||
<div className="mt-6 h-px w-24 bg-copper/70" />
|
||||
</Reveal>
|
||||
)}
|
||||
|
||||
<Reveal delay={0.08}>
|
||||
<p className="mt-8 text-base font-light leading-9 text-ivory/65">
|
||||
گروه ساختمانی آرامش با یک دفتر کوچک معماری آغاز شد و امروز یکی از مجموعههای شناختهشده ساختوساز لوکس در ایران
|
||||
است. در تمام این سالها یک اصل تغییر نکرده است: ساختمان باید برای زندگی ساخته شود، نه برای فروش سریع.
|
||||
</p>
|
||||
<p className="mt-5 text-base font-light leading-9 text-ivory/65">
|
||||
هر پروژه با مطالعه زمین، نور، اقلیم و الگوی زندگی ساکنان آغاز میشود؛ سپس تیم طراحی، سازه و اجرا در یک
|
||||
فرآیند واحد آن را تا تحویل کلید پیش میبرند.
|
||||
</p>
|
||||
{story.paragraphs.map((paragraph, i) => (
|
||||
<p key={i} className={`text-base font-light leading-9 text-ivory/65 ${i === 0 ? 'mt-8' : 'mt-5'}`}>
|
||||
{paragraph}
|
||||
</p>
|
||||
))}
|
||||
</Reveal>
|
||||
|
||||
<Reveal delay={0.16}>
|
||||
<div className="mt-10 grid grid-cols-2 gap-8 border-t border-ivory/10 pt-8">
|
||||
<div>
|
||||
<p className="text-2xl font-bold text-copper">۹۴٪</p>
|
||||
<p className="mt-2 text-sm font-light text-ivory/55">تحویل بهموقع پروژهها</p>
|
||||
<p className="text-2xl font-bold text-copper">{story.stat1_value}</p>
|
||||
<p className="mt-2 text-sm font-light text-ivory/55">{story.stat1_label}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-2xl font-bold text-copper">۵ سال</p>
|
||||
<p className="mt-2 text-sm font-light text-ivory/55">پشتیبانی پس از تحویل</p>
|
||||
<p className="text-2xl font-bold text-copper">{story.stat2_value}</p>
|
||||
<p className="mt-2 text-sm font-light text-ivory/55">{story.stat2_label}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
|
||||
{showQuote && story.quote && (
|
||||
<Reveal delay={0.2}>
|
||||
<div className="mt-12 border-r-2 border-copper bg-char/40 p-6">
|
||||
<p className="text-sm font-light leading-8 text-ivory/80">{story.quote}</p>
|
||||
<p className="mt-5 text-sm font-bold text-copper">{story.quote_author}</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
)}
|
||||
|
||||
{showLink && (
|
||||
<Reveal delay={0.22}>
|
||||
<ButtonLink to="/about" variant="outline" className="mt-10 font-light">
|
||||
داستان کامل ما
|
||||
<ButtonLink to={story.link_href} variant="outline" className="mt-10 font-light">
|
||||
{story.link_label}
|
||||
<ArrowLeftIcon className="h-4 w-4" />
|
||||
</ButtonLink>
|
||||
</Reveal>
|
||||
@@ -73,7 +74,7 @@ export function Story({ showLink = true, showLabel = true, alignTop = false, chi
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{BENTO.map((item, i) => (
|
||||
{story.bento.map((item, i) => (
|
||||
<Reveal key={item.src + i} delay={0.06 * i}>
|
||||
<div className="overflow-hidden">
|
||||
<img
|
||||
@@ -88,4 +89,4 @@ export function Story({ showLink = true, showLabel = true, alignTop = false, chi
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import React from 'react'
|
||||
import { Reveal } from '../Reveal'
|
||||
import { SectionHeading } from '../SectionHeading'
|
||||
import { useSite } from '../../api'
|
||||
import { useSectionHeading, useSite } from '../../api'
|
||||
|
||||
export function Team() {
|
||||
const { team } = useSite()
|
||||
const heading = useSectionHeading('team')
|
||||
return (
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="team-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<SectionHeading
|
||||
title="تیم ما"
|
||||
lead="کسانی که تصمیمهای اصلی هر پروژه را میگیرند و پاسخگوی آن هستند."
|
||||
title={heading.title}
|
||||
lead={heading.lead}
|
||||
align="center"
|
||||
/>
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@ import React, { useState } from 'react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { ChevronLeftIcon, ChevronRightIcon, QuoteIcon } from 'lucide-react';
|
||||
import { SectionHeading } from '../SectionHeading';
|
||||
import { useSite } from '../../api';
|
||||
import { useSectionHeading, useSite } from '../../api';
|
||||
|
||||
export function Testimonials() {
|
||||
const { testimonials } = useSite();
|
||||
const heading = useSectionHeading('testimonials');
|
||||
const [index, setIndex] = useState(0);
|
||||
const item = testimonials[index];
|
||||
|
||||
@@ -16,7 +17,7 @@ export function Testimonials() {
|
||||
return (
|
||||
<section className="border-y border-ivory/10 bg-char py-24 lg:py-32" aria-labelledby="testimonials-title">
|
||||
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||
<SectionHeading title="نظرات مشتریان" align="center" />
|
||||
<SectionHeading title={heading.title} align="center" />
|
||||
|
||||
<div className="relative mx-auto mt-14 max-w-3xl text-center">
|
||||
<QuoteIcon className="mx-auto h-8 w-8 text-copper/60" aria-hidden="true" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useRef } from 'react'
|
||||
import { motion, useScroll, useTransform, useInView } from 'framer-motion'
|
||||
import { useSite, type TimelineItem } from '../../api'
|
||||
import { useSectionHeading, useSite, type TimelineItem } from '../../api'
|
||||
import { SectionHeading } from '../SectionHeading'
|
||||
|
||||
const ease = [0.23, 1, 0.32, 1] as const
|
||||
@@ -43,6 +43,7 @@ function Item({ item, index }: { item: Milestone; index: number }) {
|
||||
|
||||
export function Timeline() {
|
||||
const { timeline } = useSite()
|
||||
const heading = useSectionHeading('timeline')
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
@@ -56,8 +57,8 @@ export function Timeline() {
|
||||
|
||||
<div className="mb-16">
|
||||
<SectionHeading
|
||||
title="مسیر ما"
|
||||
lead="از یک دفتر چهارنفره تا ۴۷ پروژه. خط زمان پروژههای ما را ببینید."
|
||||
title={heading.title}
|
||||
lead={heading.lead}
|
||||
align="center"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
import React from 'react';
|
||||
import { Reveal } from '../Reveal';
|
||||
import { SectionHeading } from '../SectionHeading';
|
||||
import { IMAGES } from '../../data/site';
|
||||
import { useSite } from '../../api';
|
||||
import { useSectionHeading, useSite } from '../../api';
|
||||
|
||||
export function WhyUs() {
|
||||
const { why_us } = useSite();
|
||||
const heading = useSectionHeading('why_us');
|
||||
return (
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="why-title">
|
||||
<div className="mx-auto grid max-w-site gap-14 px-6 lg:grid-cols-12 lg:px-10">
|
||||
<div className="lg:col-span-5">
|
||||
<SectionHeading
|
||||
title="چرا گروه ساختمانی آرامش"
|
||||
lead="شش دلیلی که کارفرمایان و سرمایهگذاران بار دوم هم به ما برمیگردند." />
|
||||
|
||||
<Reveal delay={0.12}>
|
||||
<img
|
||||
src={IMAGES[3]}
|
||||
alt="نمای شبانه برج دوقلوی نیاوران"
|
||||
className="mt-10 h-80 w-full object-cover" />
|
||||
|
||||
</Reveal>
|
||||
title={heading.title}
|
||||
lead={heading.lead} />
|
||||
|
||||
{heading.image && (
|
||||
<Reveal delay={0.12}>
|
||||
<img
|
||||
src={heading.image}
|
||||
alt={heading.title}
|
||||
className="mt-10 h-80 w-full object-cover" />
|
||||
|
||||
</Reveal>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-7">
|
||||
|
||||
Reference in New Issue
Block a user