feat(hero): نشان «پروژه فعال الیسیان» به صفحهٔ همان پروژه وصل شد (تطبیق دو‌زبانهٔ نام با نگاشت Elysian→الیسیان)

This commit is contained in:
aramesh-site deploy
2026-10-09 18:57:46 +00:00
parent e571b19f4a
commit 1ceb7cb156
2 changed files with 68 additions and 5 deletions
+44 -3
View File
@@ -10,9 +10,46 @@ import { InlineEdit } from '../admin/InlineEdit'
import { HeroProjectBadge } from './HeroProjectBadge' import { HeroProjectBadge } from './HeroProjectBadge'
import { SingletonPill } from '../../admin/Editable' import { SingletonPill } from '../../admin/Editable'
/** واژه‌های عمومی برچسب نشان که در تطبیق نام پروژه نادیده گرفته می‌شوند */
const NOISE_WORDS = ['پروژه', 'فعال', 'آرامش', 'گروه', 'ساختمانی', 'Active', 'Project', 'Aramesh']
/** نام لاتین برچسب انگلیسی → معادل فارسی در عنوان پروژه (پروژه‌های بدون title_en) */
const LATIN_ALIAS: Record<string, string> = { elysian: 'الیسیان' }
export function Hero() { export function Hero() {
const { brand, hero } = useSite() const { brand, hero, projects } = useSite()
const { t, href } = useLang() const { t, href, lang } = useLang()
/**
* پروژهٔ فعال هیرو → صفحهٔ همان پروژه.
* برچسب نشان مثل «پروژه فعال آرامش الیسیان» یا «Elysian Aramesh Active Project»
* است؛ واژه‌های عمومی حذف و نام باقی‌مانده با عنوان پروژه‌ها تطبیق داده می‌شود.
*/
const activeProjectHref = (() => {
const label = hero.badge_label.trim()
if (!label) return undefined
const strip = (text: string) => {
let out = text
NOISE_WORDS.forEach((w) => {
out = out.split(w).join(' ')
})
return out.replace(/[\s\-—_]+/g, '').trim().toLowerCase()
}
let key = strip(label)
// برچسب انگلیسی ممکن است نام لاتین داشته باشد و پروژه فقط عنوان فارسی (Elysian → الیسیان)
if (key.length < 3 || LATIN_ALIAS[key]) key = LATIN_ALIAS[key] ?? key
if (key.length < 3) return undefined
const hit = projects.find((p) => {
const candidates = [p.title, p.title_en ?? ''].filter(Boolean)
return candidates.some((title) => {
const k = strip(title)
return k.length >= 3 && (k.includes(key) || key.includes(k))
})
})
return hit ? `/projects/${hit.slug}` : undefined
})()
const save = useFieldSaver() const save = useFieldSaver()
const { scrollY } = useScroll() const { scrollY } = useScroll()
const imageY = useTransform(scrollY, [0, 700], [0, 110]) const imageY = useTransform(scrollY, [0, 700], [0, 110])
@@ -141,7 +178,11 @@ export function Hero() {
<span aria-hidden="true" className="absolute -top-20 -end-20 h-44 w-44 rounded-full bg-copper/10 blur-3xl transition-colors duration-700 group-hover:bg-copper/20" /> <span aria-hidden="true" className="absolute -top-20 -end-20 h-44 w-44 rounded-full bg-copper/10 blur-3xl transition-colors duration-700 group-hover:bg-copper/20" />
<div className="relative"> <div className="relative">
<HeroProjectBadge value={String(hero.badge_value ?? '').trim()} label={hero.badge_label} /> <HeroProjectBadge
value={String(hero.badge_value ?? '').trim()}
label={hero.badge_label}
href={activeProjectHref ? href(activeProjectHref) : undefined}
/>
</div> </div>
</div> </div>
</motion.div> </motion.div>
@@ -1,12 +1,22 @@
import { Building2Icon } from 'lucide-react' import { Building2Icon } from 'lucide-react'
import { Link } from 'react-router-dom'
/** /**
* نشان «پروژه فعال» هیرو — آیکون و متن کنار هم، با فاصلهٔ یکنواخت و * نشان «پروژه فعال» هیرو — آیکون و متن کنار هم، با فاصلهٔ یکنواخت و
* چیدمان وسط‌چین. فلکس‌باکس ستون را از بین می‌برد تا فاصلهٔ آیکون و متن * چیدمان وسط‌چین. فلکس‌باکس ستون را از بین می‌برد تا فاصلهٔ آیکون و متن
* یکدست بماند و آیکون درست با متن هم‌تراز شود. * یکدست بماند و آیکون درست با متن هم‌تراز شود.
*/ */
export function HeroProjectBadge({ value, label }: { value: string; label: string }) { export function HeroProjectBadge({
return ( value,
label,
href,
}: {
value: string
label: string
/** نشانی صفحهٔ پروژهٔ فعال — نشان کلیک‌پذیر می‌شود و به همان پروژه می‌رود */
href?: string
}) {
const body = (
<div className="flex items-center gap-3.5 text-center"> <div className="flex items-center gap-3.5 text-center">
{/* آیکون در کادر مربعی با نشان زندهٔ مسی */} {/* آیکون در کادر مربعی با نشان زندهٔ مسی */}
<span className="relative flex h-14 w-14 shrink-0 items-center justify-center rounded-xl border border-copper/40 bg-copper/10"> <span className="relative flex h-14 w-14 shrink-0 items-center justify-center rounded-xl border border-copper/40 bg-copper/10">
@@ -23,4 +33,16 @@ export function HeroProjectBadge({ value, label }: { value: string; label: strin
</div> </div>
</div> </div>
) )
// بدون نشانی همان ظرف ساده می‌ماند؛ با نشانی، کل نشان یک پیوند به پروژه است
if (!href) return body
return (
<Link
to={href}
className="block rounded-2xl transition-transform duration-300 ease-lux hover:-translate-y-0.5 focus:outline-none focus-visible:ring-2 focus-visible:ring-copper"
aria-label={label}
>
{body}
</Link>
)
} }