feat(home): پروژههای شاخص بدون تب وضعیت — سه کارت ثابت (بابک، میرزاطاهر ۳، میرزا طاهر ۵)
This commit is contained in:
@@ -1,87 +1,37 @@
|
|||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { AnimatePresence, motion } from 'framer-motion'
|
import { AnimatePresence, motion } from 'framer-motion'
|
||||||
import { Reveal } from '../Reveal'
|
import { Reveal } from '../Reveal'
|
||||||
import { PremiumButton } from '../PremiumButton'
|
import { PremiumButton } from '../PremiumButton'
|
||||||
import { ProjectCard } from '../ProjectCard'
|
import { ProjectCard } from '../ProjectCard'
|
||||||
import { SectionHeading } from '../SectionHeading'
|
import { SectionHeading } from '../SectionHeading'
|
||||||
import { useSectionHeading, useSite, useStatusLabel } from '../../api'
|
import { useSectionHeading, useSite } from '../../api'
|
||||||
import { useFieldSaver } from '../../admin/AdminContext'
|
import { useFieldSaver } from '../../admin/AdminContext'
|
||||||
import { useLang } from '../../i18n/lang'
|
import { useLang } from '../../i18n/lang'
|
||||||
import { InlineEdit } from '../admin/InlineEdit'
|
import { InlineEdit } from '../admin/InlineEdit'
|
||||||
import type { ProjectStatus } from '../../data/site'
|
|
||||||
|
|
||||||
export function FeaturedProjects() {
|
export function FeaturedProjects() {
|
||||||
const { status_labels, projects } = useSite()
|
const { projects } = useSite()
|
||||||
const { t, href } = useLang()
|
const { href } = useLang()
|
||||||
const heading = useSectionHeading('featured_projects')
|
const heading = useSectionHeading('featured_projects')
|
||||||
const save = useFieldSaver()
|
const save = useFieldSaver()
|
||||||
|
|
||||||
const saveStatusLabel = (key: string, value: string) => {
|
// سه پروژهٔ ثابت صفحهٔ اصلی — بدون تب وضعیت؛ ویرایش پنل همانجا اعمال میشود
|
||||||
const label = status_labels.find((s) => s.key === key)
|
const FEATURED_TITLES = ['آرامش - بابک', 'آرامش - میرزاطاهر 3', 'آرامش - میرزا طاهر 5']
|
||||||
if (!label) return
|
const list = projects.filter((p) => FEATURED_TITLES.includes(p.title))
|
||||||
void save('status-labels', label.id, { label: value }, (d) => {
|
.sort((a, b) => FEATURED_TITLES.indexOf(a.title) - FEATURED_TITLES.indexOf(b.title))
|
||||||
const s = d.status_labels.find((x) => x.id === label.id)
|
|
||||||
if (s) s.label = value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const FILTERS: { key: ProjectStatus; label: string }[] = [
|
|
||||||
{ key: 'done', label: useStatusLabel('done') },
|
|
||||||
{ key: 'progress', label: useStatusLabel('progress') },
|
|
||||||
]
|
|
||||||
|
|
||||||
const [status, setStatus] = useState<ProjectStatus>('done')
|
|
||||||
|
|
||||||
// سه پروژه از دیتابیس — ویرایش پنل همانجا اعمال میشود
|
|
||||||
const list = projects.filter((p) => p.status === status).slice(0, 3)
|
|
||||||
const BASE_IMG_URL = "/seed-media/2026/09/";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="bg-ink py-24 lg:py-32" aria-labelledby="projects-title">
|
<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">
|
<div className="mx-auto max-w-site px-6 lg:px-10">
|
||||||
<SectionHeading title={heading.title} lead={heading.lead} headingKey="featured_projects" />
|
<SectionHeading title={heading.title} lead={heading.lead} headingKey="featured_projects" />
|
||||||
|
|
||||||
{/* دکمههای فیلتر مینیمال و شیشهای (بدون کادر) */}
|
|
||||||
<div className="mt-10 flex items-center justify-center gap-4 md:gap-6" role="tablist" aria-label={t('projectFilterAria')}>
|
|
||||||
{FILTERS.map((filter, index) => {
|
|
||||||
const isSelected = status === filter.key
|
|
||||||
return (
|
|
||||||
<React.Fragment key={filter.key}>
|
|
||||||
{index > 0 && (
|
|
||||||
<span className="h-6 w-[2px] rounded-full bg-ivory/10" aria-hidden="true" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
role="tab"
|
|
||||||
aria-selected={isSelected}
|
|
||||||
onClick={() => setStatus(filter.key)}
|
|
||||||
className={`group relative rounded-lg px-6 py-2.5 text-sm font-medium transition-all duration-300 ease-lux active:scale-95 cursor-pointer select-none backdrop-blur-md ${
|
|
||||||
isSelected
|
|
||||||
? 'bg-copper/15 text-copper shadow-[0_4px_15px_rgba(197,143,92,0.1)]'
|
|
||||||
: 'bg-transparent text-ivory/60 hover:bg-white/5 hover:text-ivory'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span className="relative z-10 transition-colors duration-300 ease-out">
|
|
||||||
<InlineEdit
|
|
||||||
value={filter.label}
|
|
||||||
onSave={(v) => saveStatusLabel(filter.key, v)}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-10 min-h-[420px]">
|
<div className="mt-10 min-h-[420px]">
|
||||||
<AnimatePresence mode="wait">
|
<AnimatePresence mode="wait">
|
||||||
<motion.div
|
<motion.div
|
||||||
key={status}
|
key="featured"
|
||||||
initial={{ opacity: 0, y: 15 }}
|
initial={{ opacity: 0, y: 15 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
exit={{ opacity: 0, y: -15 }}
|
|
||||||
transition={{ duration: 0.3, ease: [0.23, 1, 0.32, 1] }}
|
transition={{ duration: 0.3, ease: [0.23, 1, 0.32, 1] }}
|
||||||
className="grid gap-6 lg:grid-cols-3"
|
className="grid gap-6 lg:grid-cols-3"
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user