revert(ui): بازگردانی سه بخش به حالت پیش از UI v1 (کامیت 853ad67)
- دکمهٔ تغییر زبان: کپسول بزرگ دوزبانه → دکمهٔ کوچک آیکون+متن (طرح قبلی) - ساختار صفحهٔ جزئیات پروژه: بازگشت به چیدمان قبلی (دیتابیسمحور بود و میماند) - قالب پرسشوپاسخ: بازگشت به چیدمان دوستونه (تیتر کنار آکاردئون) توجه: بخشی از UI v1 (fatemeh) عمداً برگردانده شده به درخواست تیم UI؛ بقیهٔ تغییرات آن کامیت سر جایش است. ویرایشپذیری دیتابیسمحور پروژهها و دکمهٔ ویرایش، روی همین ساختار قدیمی دوباره اعمال شدهاند.
This commit is contained in:
@@ -1,18 +1,6 @@
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { LanguagesIcon } from 'lucide-react'
|
||||
import { counterpartPath, useLang, writeLangCookie } from '../i18n/lang'
|
||||
import { track } from '../analytics'
|
||||
|
||||
/**
|
||||
* تنظیمات ثابت هر زبان.
|
||||
* ترتیب اینجا = ترتیب نمایش در کپسول (فارسی سمت چپ، English سمت راست).
|
||||
* بجای مقادیر پیکسلی، از translate-x-full استفاده شده تا دقیقاً به اندازه عرض خودش (50%) جابجا شود.
|
||||
*/
|
||||
const LANGS = {
|
||||
fa: { label: 'فارسی', sliderTranslate: 'translate-x-0' },
|
||||
en: { label: 'English', sliderTranslate: 'translate-x-full' },
|
||||
} as const
|
||||
|
||||
type LangKey = keyof typeof LANGS
|
||||
|
||||
/** دکمه تغییر زبان — بین نسخه فارسی و /en انگلیسی همان صفحه جابهجا میشود */
|
||||
export function LangToggle({ className = '' }: { className?: string }) {
|
||||
@@ -22,9 +10,7 @@ export function LangToggle({ className = '' }: { className?: string }) {
|
||||
const target = counterpartPath(pathname, lang)
|
||||
|
||||
const switchLang = () => {
|
||||
const next = lang === 'fa' ? 'en' : 'fa'
|
||||
track('lang-switch', { to: next })
|
||||
writeLangCookie(next)
|
||||
writeLangCookie(lang === 'fa' ? 'en' : 'fa')
|
||||
// پرش خودکار بازدید بعدی با انتخاب دستی همینجا گره نمیخورد
|
||||
sessionStorage.setItem('langAutoRedirect', '1')
|
||||
navigate(target)
|
||||
@@ -36,61 +22,10 @@ export function LangToggle({ className = '' }: { className?: string }) {
|
||||
onClick={switchLang}
|
||||
aria-label={t('switchLangAria')}
|
||||
title={t('switchLangAria')}
|
||||
dir="ltr"
|
||||
className={[
|
||||
// ── Capsule shell ──────────────────────────────────────────────
|
||||
'relative flex items-center',
|
||||
'w-[152px] h-[42px] rounded-full p-1', // استفاده از p-1 (4px) به جای [4px]
|
||||
'bg-[#1a1715]',
|
||||
// deep inset shadow → recessed track feel
|
||||
'shadow-[inset_0_3px_7px_rgba(0,0,0,0.75),inset_0_-1px_2px_rgba(255,255,255,0.04)]',
|
||||
// interaction
|
||||
'cursor-pointer select-none',
|
||||
'active:scale-[0.975] transition-transform duration-150 ease-lux',
|
||||
className,
|
||||
].join(' ')}
|
||||
className={`group flex items-center gap-1.5 rounded-full border border-white/15 bg-white/5 px-3.5 py-2 text-xs font-bold text-ivory/80 backdrop-blur-lg shadow-[inset_0_1px_1px_rgba(255,255,255,0.15)] transition-all duration-300 ease-lux hover:border-copper/60 hover:bg-white/10 hover:text-copper active:scale-95 cursor-pointer ${className}`}
|
||||
>
|
||||
{/* ── Inner Track ───────────────────────────────────────────────
|
||||
این دیو داخلی باعث میشود پدینگ دکمه اصلی روی محاسبات 50 درصدی تاثیر نگذارد
|
||||
*/}
|
||||
<div className="relative w-full h-full flex items-center">
|
||||
|
||||
{/* ── Slider pill ─────────────────────────────────────────────── */}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={[
|
||||
'absolute top-0 left-0', // چسبیده به لبههای Track داخلی
|
||||
'w-1/2 h-full rounded-full', // دقیقاً 50 درصد عرض و کل ارتفاع
|
||||
'bg-[#27221c] border border-[#d5874c]',
|
||||
// subtle raised shadow — tactile without being flashy
|
||||
'shadow-[0_2px_6px_rgba(0,0,0,0.55),0_0_0_1px_rgba(213,135,76,0.18),inset_0_1px_1px_rgba(255,255,255,0.06)]',
|
||||
'transition-transform duration-[380ms] ease-lux',
|
||||
LANGS[lang as LangKey].sliderTranslate,
|
||||
].join(' ')}
|
||||
/>
|
||||
|
||||
{/* ── Labels ──────────────────────────────────────────────────── */}
|
||||
<span className="relative z-10 flex w-full h-full">
|
||||
{(Object.keys(LANGS) as LangKey[]).map((key) => {
|
||||
const isActive = lang === key
|
||||
return (
|
||||
<span
|
||||
key={key}
|
||||
className={[
|
||||
// each label occupies exactly half the capsule
|
||||
'w-1/2 h-full flex justify-center items-center',
|
||||
'text-[11.5px] font-semibold tracking-wide leading-none',
|
||||
'transition-colors duration-[380ms] ease-lux',
|
||||
isActive ? 'text-[#d5874c]' : 'text-[#6a6662]',
|
||||
].join(' ')}
|
||||
>
|
||||
{LANGS[key].label}
|
||||
</span>
|
||||
)
|
||||
})}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
<LanguagesIcon className="h-3.5 w-3.5 transition-transform duration-300 group-hover:rotate-12" />
|
||||
<span>{t('switchLang')}</span>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,22 +18,20 @@ export function Faq({ hideHeading = false }: FaqProps) {
|
||||
|
||||
return (
|
||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="faq-title">
|
||||
<div className="mx-auto max-w-site px-4 md:px-8 lg:px-10">
|
||||
|
||||
{/* Header — centered, full width */}
|
||||
<div className={hideHeading ? "mx-auto max-w-4xl px-6 lg:px-10" : "mx-auto grid max-w-site gap-14 px-6 lg:grid-cols-12 lg:px-10"}>
|
||||
|
||||
{!hideHeading && (
|
||||
<div className="mb-8 md:mb-14 flex flex-col items-center w-full text-center">
|
||||
<div className="lg:col-span-4">
|
||||
<SectionHeading
|
||||
title={t('fallbackFaqTitle')}
|
||||
lead={t('fallbackFaqLead')}
|
||||
headingKey="faq"
|
||||
align="center"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Accordion list — constrained width, direction-aware */}
|
||||
<div className="w-full max-w-3xl mx-auto">
|
||||
<div className={hideHeading ? "" : "lg:col-span-8"}>
|
||||
<ul>
|
||||
{faqs.map((faq, i) => {
|
||||
const isOpen = open === i
|
||||
@@ -45,9 +43,9 @@ export function Faq({ hideHeading = false }: FaqProps) {
|
||||
type="button"
|
||||
onClick={() => setOpen(isOpen ? null : i)}
|
||||
aria-expanded={isOpen}
|
||||
className="flex w-full items-center justify-between gap-6 py-6 text-start transition-colors duration-200 ease-lux hover:text-copper"
|
||||
className="flex w-full items-center justify-between gap-6 py-6 text-right transition-colors duration-200 ease-lux hover:text-copper"
|
||||
>
|
||||
<span className={`text-base font-bold md:text-lg text-start ${isOpen ? 'text-copper' : 'text-ivory'}`}>
|
||||
<span className={`text-base font-bold md:text-lg ${isOpen ? 'text-copper' : 'text-ivory'}`}>
|
||||
{faq.question}
|
||||
</span>
|
||||
<PlusIcon
|
||||
@@ -65,7 +63,7 @@ export function Faq({ hideHeading = false }: FaqProps) {
|
||||
transition={{ duration: 0.28, ease: [0.23, 1, 0.32, 1] }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<p className="pb-7 ps-10 text-sm font-light leading-9 text-ivory/60 text-start">{faq.answer}</p>
|
||||
<p className="pb-7 pl-10 text-sm font-light leading-9 text-ivory/60">{faq.answer}</p>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
Reference in New Issue
Block a user