feat(projects): داده‌های واقعی پروژه‌ها — ستون‌های تازه و حذف داده‌های ماک

- دیتابیس/مدل/اسکیما: land_area (مساحت عرصه)، pilot_floors (طبقات روی
  همکف/پیلوت) و سه ستون واحدها (مسکونی/تجاری/اداری) به‌جای «واحدها»ی تکی
- فایل استاتیک projectsData (داده‌های ماک) حذف شد؛ لیست، جزئیات و
  «پروژه‌های شاخص» همه از دیتابیس می‌خوانند
- برچسب‌های i18n فهرست مشخصات مطابق شیت تیم
This commit is contained in:
aramesh-site deploy
2026-09-28 10:50:39 +00:00
parent 0c87a287a3
commit 182a1bb674
10 changed files with 60 additions and 117 deletions
+5 -1
View File
@@ -27,7 +27,11 @@ EN_COLUMNS: list[tuple[str, str, str]] = [
("projects", "year_en", "VARCHAR(64)"), ("projects", "year_en", "VARCHAR(64)"),
("projects", "area_en", "VARCHAR(64)"), ("projects", "area_en", "VARCHAR(64)"),
("projects", "floors_en", "VARCHAR(64)"), ("projects", "floors_en", "VARCHAR(64)"),
("projects", "units_en", "VARCHAR(64)"), ("projects", "land_area", "VARCHAR(64)"),
("projects", "pilot_floors", "VARCHAR(32)"),
("projects", "units_residential", "VARCHAR(32)"),
("projects", "units_commercial", "VARCHAR(32)"),
("projects", "units_office", "VARCHAR(32)"),
("projects", "client_en", "VARCHAR(128)"), ("projects", "client_en", "VARCHAR(128)"),
("projects", "project_type", "VARCHAR(128)"), ("projects", "project_type", "VARCHAR(128)"),
("projects", "project_type_en", "VARCHAR(128)"), ("projects", "project_type_en", "VARCHAR(128)"),
+5 -2
View File
@@ -72,8 +72,11 @@ class Project(Base):
area_en: Mapped[str] = mapped_column(String(64), default="") area_en: Mapped[str] = mapped_column(String(64), default="")
floors: Mapped[str] = mapped_column(String(64), default="") floors: Mapped[str] = mapped_column(String(64), default="")
floors_en: Mapped[str] = mapped_column(String(64), default="") floors_en: Mapped[str] = mapped_column(String(64), default="")
units: Mapped[str] = mapped_column(String(64), default="") land_area: Mapped[str] = mapped_column(String(64), default="")
units_en: Mapped[str] = mapped_column(String(64), default="") pilot_floors: Mapped[str] = mapped_column(String(32), default="")
units_residential: Mapped[str] = mapped_column(String(32), default="")
units_commercial: Mapped[str] = mapped_column(String(32), default="")
units_office: Mapped[str] = mapped_column(String(32), default="")
client: Mapped[str] = mapped_column(String(128), default="") client: Mapped[str] = mapped_column(String(128), default="")
client_en: Mapped[str] = mapped_column(String(128), default="") client_en: Mapped[str] = mapped_column(String(128), default="")
# مشخصات تکمیلی پروژه — به درخواست تیم: نوع پروژه و کاربری # مشخصات تکمیلی پروژه — به درخواست تیم: نوع پروژه و کاربری
+10 -4
View File
@@ -57,8 +57,11 @@ class ProjectOut(ORMModel):
area_en: str = "" area_en: str = ""
floors: str floors: str
floors_en: str = "" floors_en: str = ""
units: str land_area: str = ""
units_en: str = "" pilot_floors: str = ""
units_residential: str = ""
units_commercial: str = ""
units_office: str = ""
client: str client: str
client_en: str = "" client_en: str = ""
project_type: str = "" project_type: str = ""
@@ -455,6 +458,11 @@ class ProjectIn(BaseModel):
year: str = "" year: str = ""
year_en: str = "" year_en: str = ""
status: Literal["done", "progress"] = "done" status: Literal["done", "progress"] = "done"
land_area: str = ""
pilot_floors: str = ""
units_residential: str = ""
units_commercial: str = ""
units_office: str = ""
project_type: str = "" project_type: str = ""
project_type_en: str = "" project_type_en: str = ""
usage: str = "" usage: str = ""
@@ -465,8 +473,6 @@ class ProjectIn(BaseModel):
area_en: str = "" area_en: str = ""
floors: str = "" floors: str = ""
floors_en: str = "" floors_en: str = ""
units: str = ""
units_en: str = ""
client: str = "" client: str = ""
client_en: str = "" client_en: str = ""
summary: str = "" summary: str = ""
+4 -2
View File
@@ -69,8 +69,10 @@ export const ENTITIES: Record<string, EntityConfig> = {
F('title', 'عنوان'), F('slug', 'نشانی (slug)', 'text', undefined, true), F('location', 'موقعیت'), F('title', 'عنوان'), F('slug', 'نشانی (slug)', 'text', undefined, true), F('location', 'موقعیت'),
F('year', 'سال'), F('status', 'وضعیت', 'text', 'done یا progress', true), F('year', 'سال'), F('status', 'وضعیت', 'text', 'done یا progress', true),
F('image', 'تصویر شاخص', 'image', undefined, true), F('gallery', 'گالری (هر خط یک URL)', 'list', undefined, true), F('image', 'تصویر شاخص', 'image', undefined, true), F('gallery', 'گالری (هر خط یک URL)', 'list', undefined, true),
F('area', 'زیربنا'), F('floors', 'طبقات'), F('units', 'واحدها'), F('land_area', 'مساحت عرصه (زمین)'), F('area', 'مساحت زیربنا (اعیان)'),
F('project_type', 'نوع پروژه'), F('usage', 'کاربری'), F('client', 'کارفرما'), F('usage', 'کاربری ساختمان'), F('pilot_floors', 'طبقات روی همکف (پیلوت)'), F('floors', 'تعداد طبقات'),
F('units_residential', 'تعداد واحد مسکونی'), F('units_commercial', 'تعداد واحد تجاری'), F('units_office', 'تعداد واحد اداری'),
F('project_type', 'نوع پروژه'),
F('summary', 'خلاصه', 'textarea'), F('body', 'متن پروژه (هر پاراگراف یک خط)', 'list'), F('summary', 'خلاصه', 'textarea'), F('body', 'متن پروژه (هر پاراگراف یک خط)', 'list'),
], ],
defaults: { title: 'پروژه جدید', slug: `project-${Date.now()}`, location: '', year: '', status: 'done', image: '', gallery: [], area: '', floors: '', units: '', client: '', summary: '', body: [] }, defaults: { title: 'پروژه جدید', slug: `project-${Date.now()}`, location: '', year: '', status: 'done', image: '', gallery: [], area: '', floors: '', units: '', client: '', summary: '', body: [] },
+8 -1
View File
@@ -2,7 +2,14 @@ import { Link } from 'react-router-dom'
import { useStatusLabel } from '../api' import { useStatusLabel } from '../api'
import { useLang } from '../i18n/lang' import { useLang } from '../i18n/lang'
import { PremiumButton } from './PremiumButton' import { PremiumButton } from './PremiumButton'
import type { StaticProject } from '../data/projectsData' export type StaticProject = {
id: string
slug: string
name: { en: string; fa: string }
location: { en: string; fa: string }
area: { en: string; fa: string }
status: 'done' | 'progress'
}
import type { ProjectCardItem } from '../data/site' import type { ProjectCardItem } from '../data/site'
type ProjectCardProps = { type ProjectCardProps = {
@@ -10,10 +10,9 @@ 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' import type { ProjectStatus } from '../../data/site'
import { projectsData } from '../../data/projectsData'
export function FeaturedProjects() { export function FeaturedProjects() {
const { status_labels } = useSite() const { status_labels, projects } = useSite()
const { t, href } = useLang() const { t, href } = useLang()
const heading = useSectionHeading('featured_projects') const heading = useSectionHeading('featured_projects')
const save = useFieldSaver() const save = useFieldSaver()
@@ -34,8 +33,8 @@ export function FeaturedProjects() {
const [status, setStatus] = useState<ProjectStatus>('done') const [status, setStatus] = useState<ProjectStatus>('done')
// Filter and slice static project data // سه پروژه از دیتابیس — ویرایش پنل همان‌جا اعمال می‌شود
const list = projectsData.filter((p) => p.status === status).slice(0, 3) const list = projects.filter((p) => p.status === status).slice(0, 3)
const BASE_IMG_URL = "/seed-media/2026/09/"; const BASE_IMG_URL = "/seed-media/2026/09/";
return ( return (
@@ -87,8 +86,7 @@ export function FeaturedProjects() {
className="grid gap-6 lg:grid-cols-3" className="grid gap-6 lg:grid-cols-3"
> >
{list.map((project, i) => { {list.map((project, i) => {
const dummyImageName = `${(i % 6) + 1}.png`; const imageUrl = project.image || undefined;
const imageUrl = `${BASE_IMG_URL}${dummyImageName}`;
return ( return (
<motion.div <motion.div
-74
View File
@@ -1,74 +0,0 @@
export type BilingualText = { en: string; fa: string };
export type StaticProject = {
id: string;
slug: string;
name: BilingualText;
location: BilingualText;
area: BilingualText;
status: 'done' | 'progress';
};
export const projectsData: StaticProject[] = [
{ id: '1', slug: 'project-1', name: { en: 'Project 1', fa: 'پروژه سروش' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '2', slug: 'project-2', name: { en: 'Project 2', fa: 'چهرازی-زیتون' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '3', slug: 'project-3', name: { en: 'Project 3', fa: 'جعفریان' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '4', slug: 'project-4', name: { en: 'Project 4', fa: 'بهار' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '5', slug: 'project-5', name: { en: 'Project 5', fa: 'حسن مرادی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '6', slug: 'project-6', name: { en: 'Project 6', fa: 'کوثر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '7', slug: 'project-7', name: { en: 'Project 7', fa: 'صبا' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '8', slug: 'project-8', name: { en: 'Project 8', fa: 'پورمعالی - بهشت' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '9', slug: 'project-9', name: { en: 'Project 9', fa: '2 پروژه- فردوس' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '10', slug: 'project-10', name: { en: 'Project 10', fa: 'ارم' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '11', slug: 'project-11', name: { en: 'Project 11', fa: 'سینا' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '12', slug: 'project-12', name: { en: 'Project 12', fa: 'مسجد سید' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '13', slug: 'project-13', name: { en: 'Project 13', fa: 'هفده شهریور' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '14', slug: 'project-14', name: { en: 'Project 14', fa: 'آرامش رفیع زاده' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '15', slug: 'project-15', name: { en: 'Project 15', fa: 'بن بست شریعت' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '16', slug: 'project-16', name: { en: 'Project 16', fa: 'قصر - یاسین' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '17', slug: 'project-17', name: { en: 'Project 17', fa: 'پردیس' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '18', slug: 'project-18', name: { en: 'Project 18', fa: 'آرامش حکیم' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '19', slug: 'project-19', name: { en: 'Project 19', fa: 'آرامش دخانیات' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '20', slug: 'project-20', name: { en: 'Project 20', fa: 'بهشتی فرشادی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '21', slug: 'project-21', name: { en: 'Project 21', fa: 'کاکتوس' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '22', slug: 'project-22', name: { en: 'Project 22', fa: 'آرامش - صیاد' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '23', slug: 'project-23', name: { en: 'Project 23', fa: 'کوثر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '24', slug: 'project-24', name: { en: 'Project 24', fa: 'آرامش - بوعلی 1' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '25', slug: 'project-25', name: { en: 'Project 25', fa: 'میرزا نصیر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '26', slug: 'project-26', name: { en: 'Project 26', fa: 'نیکان' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '27', slug: 'project-27', name: { en: 'Project 27', fa: 'آرامش-رسا' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '28', slug: 'project-28', name: { en: 'Project 28', fa: 'نگین' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '29', slug: 'project-29', name: { en: 'Project 29', fa: 'آرامش - بوعلی 2' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '30', slug: 'project-30', name: { en: 'Project 30', fa: 'آرامش استاد همایی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '31', slug: 'project-31', name: { en: 'Project 31', fa: 'آرامش صدر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '32', slug: 'project-32', name: { en: 'Project 32', fa: 'آرامش - سپهسالار' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '33', slug: 'project-33', name: { en: 'Project 33', fa: 'آرامش- صدر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '34', slug: 'project-34', name: { en: 'Project 34', fa: 'آرامش - سنایی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '35', slug: 'project-35', name: { en: 'Project 35', fa: 'مغازه لباس فروشی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '36', slug: 'project-36', name: { en: 'Project 36', fa: 'آرامش - آتشگاه' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '37', slug: 'project-37', name: { en: 'Project 37', fa: 'آرامش - بزرگمهر' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '38', slug: 'project-38', name: { en: 'Project 38', fa: 'آرامش - سعدی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '39', slug: 'project-39', name: { en: 'Project 39', fa: 'آرامش - صمدیه لباف' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '40', slug: 'project-40', name: { en: 'Project 40', fa: 'آرامش 2' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '41', slug: 'project-41', name: { en: 'Project 41', fa: 'آرامش 5 رمضان' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '42', slug: 'project-42', name: { en: 'Project 42', fa: 'آرامش احمدآباد' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '43', slug: 'project-43', name: { en: 'Project 43', fa: 'تجاری آتشگاه' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '44', slug: 'project-44', name: { en: 'Project 44', fa: 'آرامش - شهیدان' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '45', slug: 'project-45', name: { en: 'Project 45', fa: 'آرامش خواجو' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '46', slug: 'project-46', name: { en: 'Project 46', fa: 'آرامش- اداری ابوالحسن' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '47', slug: 'project-47', name: { en: 'Project 47', fa: 'آرامش - انقلاب' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '48', slug: 'project-48', name: { en: 'Project 48', fa: 'آرامش - شریف' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '49', slug: 'project-49', name: { en: 'Project 49', fa: 'آرامش - کهندژ' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '50', slug: 'project-50', name: { en: 'Project 50', fa: 'آرامش جی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '51', slug: 'project-51', name: { en: 'Project 51', fa: 'آرامش - بابک' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '52', slug: 'project-52', name: { en: 'Project 52', fa: 'آرامش - شمس آبادی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '53', slug: 'project-53', name: { en: 'Project 53', fa: 'آرامش - میرزاطاهر 3' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '54', slug: 'project-54', name: { en: 'Project 54', fa: 'آرامش - وحدت' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '55', slug: 'project-55', name: { en: 'Project 55', fa: 'آرامش پروین' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'done' },
{ id: '56', slug: 'project-56', name: { en: 'Project 56', fa: 'خلیفه سلطانی' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '57', slug: 'project-57', name: { en: 'Project 57', fa: 'آرامش - میرزا طاهر 5' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '58', slug: 'project-58', name: { en: 'Project 58', fa: 'آرامش - میرزاطاهر 6' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '59', slug: 'project-59', name: { en: 'Project 59', fa: 'آرامش - میرزا طاهر 4' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
{ id: '60', slug: 'project-60', name: { en: 'Project 60', fa: 'آرامش- میرزا طاهر 1' }, location: { en: 'Isfahan', fa: 'اصفهان' }, area: { en: '1000', fa: '۱۰۰۰' }, status: 'progress' },
];
+12 -2
View File
@@ -54,10 +54,15 @@ export const fa = {
projectNotFoundText: 'ممکن است نشانی تغییر کرده باشد. فهرست کامل پروژه‌ها را ببینید.', projectNotFoundText: 'ممکن است نشانی تغییر کرده باشد. فهرست کامل پروژه‌ها را ببینید.',
backToProjects: 'بازگشت به پروژه‌ها', backToProjects: 'بازگشت به پروژه‌ها',
specStatus: 'وضعیت', specStatus: 'وضعیت',
specLandArea: 'مساحت عرصه (زمین)',
specPilotFloors: 'طبقات روی همکف (پیلوت)',
specUnitsResidential: 'تعداد واحد مسکونی',
specUnitsCommercial: 'تعداد واحد تجاری',
specUnitsOffice: 'تعداد واحد اداری',
specLocation: 'موقعیت', specLocation: 'موقعیت',
specProjectType: 'نوع پروژه', specProjectType: 'نوع پروژه',
specUsage: 'کاربری', specUsage: 'کاربری',
specYear: 'سال', specYear: 'سال ساخت',
specArea: 'زیربنا', specArea: 'زیربنا',
specFloors: 'تعداد طبقات', specFloors: 'تعداد طبقات',
specUnits: 'واحدها', specUnits: 'واحدها',
@@ -170,10 +175,15 @@ export const en: Dict = {
projectNotFoundText: 'The address may have changed. Browse the full list of projects.', projectNotFoundText: 'The address may have changed. Browse the full list of projects.',
backToProjects: 'Back to projects', backToProjects: 'Back to projects',
specStatus: 'Status', specStatus: 'Status',
specLandArea: 'Land area',
specPilotFloors: 'Floors above ground',
specUnitsResidential: 'Residential units',
specUnitsCommercial: 'Commercial units',
specUnitsOffice: 'Office units',
specLocation: 'Location', specLocation: 'Location',
specProjectType: 'Project type', specProjectType: 'Project type',
specUsage: 'Land use', specUsage: 'Land use',
specYear: 'Year', specYear: 'Year built',
specArea: 'Built area', specArea: 'Built area',
specFloors: 'Floors', specFloors: 'Floors',
specUnits: 'Units', specUnits: 'Units',
+6 -3
View File
@@ -54,11 +54,14 @@ export function ProjectDetail() {
const facts = [ const facts = [
{ label: t('specStatus'), value: statusLabel }, { label: t('specStatus'), value: statusLabel },
{ label: t('specYear'), value: project.year }, { label: t('specYear'), value: project.year },
{ label: t('specFloors'), value: project.floors }, { label: t('specLandArea'), value: project.land_area },
{ label: t('specUnits'), value: project.units },
{ label: t('specProjectType'), value: project.project_type },
{ label: t('specArea'), value: project.area }, { label: t('specArea'), value: project.area },
{ label: t('specUsage'), value: project.usage }, { label: t('specUsage'), value: project.usage },
{ label: t('specPilotFloors'), value: project.pilot_floors },
{ label: t('specFloors'), value: project.floors },
{ label: t('specUnitsResidential'), value: project.units_residential },
{ label: t('specUnitsCommercial'), value: project.units_commercial },
{ label: t('specUnitsOffice'), value: project.units_office },
].filter((f) => String(f.value ?? '').trim() !== '') ].filter((f) => String(f.value ?? '').trim() !== '')
return ( return (
+6 -22
View File
@@ -10,7 +10,6 @@ import { useLang } from '../i18n/lang';
import { useFieldSaver } from '../admin/AdminContext'; import { useFieldSaver } from '../admin/AdminContext';
import { InlineEdit } from '../components/admin/InlineEdit'; import { InlineEdit } from '../components/admin/InlineEdit';
import { AddItemButton, EditableItem } from '../admin/Editable'; import { AddItemButton, EditableItem } from '../admin/Editable';
import { projectsData } from '../data/projectsData';
import { LazyProjectsMapAll } from '../map/LazyMaps'; import { LazyProjectsMapAll } from '../map/LazyMaps';
import { MAP_PROJECTS, type MapProject } from '../data/mapProjects'; import { MAP_PROJECTS, type MapProject } from '../data/mapProjects';
@@ -38,11 +37,7 @@ export function Projects() {
// پروژه‌ها از دیتابیس خوانده می‌شوند تا ویرایش پنل همان‌جا دیده شود؛ // پروژه‌ها از دیتابیس خوانده می‌شوند تا ویرایش پنل همان‌جا دیده شود؛
// فایل استاتیک فقط وقتی دیتابیس خالی است جانشین می‌شود (محتوای قدیمی). // فایل استاتیک فقط وقتی دیتابیس خالی است جانشین می‌شود (محتوای قدیمی).
const fromDb = dbProjects.length > 0; const cardProjects = useMemo(() => dbProjects, [dbProjects]);
const cardProjects = useMemo(() => {
if (fromDb) return dbProjects;
return projectsData.map((p) => ({ ...p, id: Number(p.id) }));
}, [fromDb, dbProjects]);
// Filtering and Pagination State // Filtering and Pagination State
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
@@ -83,11 +78,7 @@ export function Projects() {
const dbMatch = dbProjects.find( const dbMatch = dbProjects.find(
(p) => p.title === mapProject.name || p.title_en === mapProject.name, (p) => p.title === mapProject.name || p.title_en === mapProject.name,
); );
if (dbMatch) return `/projects/${dbMatch.slug}`; return dbMatch ? `/projects/${dbMatch.slug}` : undefined;
const match = projectsData.find(
(p) => p.name.fa === mapProject.name || p.name.en === mapProject.name,
);
return match ? `/projects/${match.slug}` : undefined;
}; };
return ( return (
@@ -147,29 +138,22 @@ export function Projects() {
{displayedProjects.map((project, index) => { {displayedProjects.map((project, index) => {
// تصویر رکورد دیتابیس اولویت دارد؛ فقط ردیف‌های بدون تصویر از // تصویر رکورد دیتابیس اولویت دارد؛ فقط ردیف‌های بدون تصویر از
// تصاویر جایگزین تکراری استفاده می‌کنند. // تصاویر جایگزین تکراری استفاده می‌کنند.
const hasOwnImage = 'image' in project && Boolean((project as { image?: string }).image);
const imageUrl = hasOwnImage ? undefined : `${BASE_IMG_URL}${(index % 6) + 1}.png`;
const card = ( const card = (
<ProjectCard <ProjectCard
project={project} project={project}
imageUrl={imageUrl}
detailHref={`/projects/${project.slug}`} detailHref={`/projects/${project.slug}`}
/> />
); );
return ( return (
<Reveal key={project.id} delay={0.05 * (index % itemsPerPage)} className="h-full"> <Reveal key={project.id} delay={0.05 * (index % itemsPerPage)} className="h-full">
{fromDb ? ( <EditableItem resource="projects" item={project} editHref={`/admin/projects/${project.id}`} className="h-full">
<EditableItem resource="projects" item={project} editHref={`/admin/projects/${project.id}`} className="h-full"> {card}
{card} </EditableItem>
</EditableItem>
) : (
card
)}
</Reveal> </Reveal>
); );
})} })}
{fromDb && <AddItemButton resource="projects" editHrefBase="/admin/projects" />} <AddItemButton resource="projects" editHrefBase="/admin/projects" />
</div> </div>
{/* Load More Button */} {/* Load More Button */}