From edcbd0dc58224529cf78929e9a9607b66fdec802 Mon Sep 17 00:00:00 2001 From: aramesh-site deploy Date: Mon, 28 Sep 2026 10:17:25 +0000 Subject: [PATCH] =?UTF-8?q?feat(projects):=20=D9=81=DB=8C=D9=84=D8=AF?= =?UTF-8?q?=D9=87=D8=A7=DB=8C=20=C2=AB=D9=86=D9=88=D8=B9=20=D9=BE=D8=B1?= =?UTF-8?q?=D9=88=DA=98=D9=87=C2=BB=20=D9=88=20=C2=AB=DA=A9=D8=A7=D8=B1?= =?UTF-8?q?=D8=A8=D8=B1=DB=8C=C2=BB=20+=20=D9=81=D9=87=D8=B1=D8=B3=D8=AA?= =?UTF-8?q?=20=D9=85=D8=B4=D8=AE=D8=B5=D8=A7=D8=AA=20=D8=AC=D8=AF=DB=8C?= =?UTF-8?q?=D8=AF=20=D8=B5=D9=81=D8=AD=D9=87=D9=94=20=D8=AC=D8=B2=D8=A6?= =?UTF-8?q?=DB=8C=D8=A7=D8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - دیتابیس/مدل/اسکیما: ستون‌های project_type/usage دوزبانه (مهاجرت DDL idempotent — با استارت بک‌اند اعمال می‌شود) - پنل: کشویر پروژه‌ها دو فیلد تازه گرفت - صفحهٔ جزئیات: فهرست مشخصات مطابق داده‌های تیم — وضعیت / سال / طبقات / واحدها / نوع پروژه / زیربنا / کاربری (ردیف‌های خالی پنهان‌اند) --- backend/app/migrations.py | 4 ++++ backend/app/models.py | 5 +++++ backend/app/schemas.py | 8 ++++++++ frontend/src/admin/entities.ts | 3 ++- frontend/src/i18n/dict.ts | 6 ++++++ frontend/src/pages/ProjectDetail.tsx | 12 ++++++++---- 6 files changed, 33 insertions(+), 5 deletions(-) diff --git a/backend/app/migrations.py b/backend/app/migrations.py index 0f299d6..a50caf5 100644 --- a/backend/app/migrations.py +++ b/backend/app/migrations.py @@ -29,6 +29,10 @@ EN_COLUMNS: list[tuple[str, str, str]] = [ ("projects", "floors_en", "VARCHAR(64)"), ("projects", "units_en", "VARCHAR(64)"), ("projects", "client_en", "VARCHAR(128)"), + ("projects", "project_type", "VARCHAR(128)"), + ("projects", "project_type_en", "VARCHAR(128)"), + ("projects", "usage", "VARCHAR(128)"), + ("projects", "usage_en", "VARCHAR(128)"), ("projects", "summary_en", "TEXT"), ("projects", "body_en", "JSON"), ("articles", "title_en", "VARCHAR(256)"), diff --git a/backend/app/models.py b/backend/app/models.py index 2140107..9de3872 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -76,6 +76,11 @@ class Project(Base): units_en: Mapped[str] = mapped_column(String(64), default="") client: Mapped[str] = mapped_column(String(128), default="") client_en: Mapped[str] = mapped_column(String(128), default="") + # مشخصات تکمیلی پروژه — به درخواست تیم: نوع پروژه و کاربری + project_type: Mapped[str] = mapped_column(String(128), default="") + project_type_en: Mapped[str] = mapped_column(String(128), default="") + usage: Mapped[str] = mapped_column(String(128), default="") + usage_en: Mapped[str] = mapped_column(String(128), default="") summary: Mapped[str] = mapped_column(Text, default="") summary_en: Mapped[str] = mapped_column(Text, default="") body: Mapped[list] = mapped_column(JSON, default=list) diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 948a118..5d55829 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -61,6 +61,10 @@ class ProjectOut(ORMModel): units_en: str = "" client: str client_en: str = "" + project_type: str = "" + project_type_en: str = "" + usage: str = "" + usage_en: str = "" summary: str summary_en: str = "" body: list[str] @@ -451,6 +455,10 @@ class ProjectIn(BaseModel): year: str = "" year_en: str = "" status: Literal["done", "progress"] = "done" + project_type: str = "" + project_type_en: str = "" + usage: str = "" + usage_en: str = "" image: str = "" gallery: list[str] = [] area: str = "" diff --git a/frontend/src/admin/entities.ts b/frontend/src/admin/entities.ts index ea13934..bdef2d5 100644 --- a/frontend/src/admin/entities.ts +++ b/frontend/src/admin/entities.ts @@ -69,7 +69,8 @@ export const ENTITIES: Record = { F('title', 'عنوان'), F('slug', 'نشانی (slug)', 'text', undefined, true), F('location', 'موقعیت'), F('year', 'سال'), F('status', 'وضعیت', 'text', 'done یا progress', true), F('image', 'تصویر شاخص', 'image', undefined, true), F('gallery', 'گالری (هر خط یک URL)', 'list', undefined, true), - F('area', 'زیربنا'), F('floors', 'طبقات'), F('units', 'واحدها'), F('client', 'کارفرما'), + F('area', 'زیربنا'), F('floors', 'طبقات'), F('units', 'واحدها'), + F('project_type', 'نوع پروژه'), F('usage', 'کاربری'), F('client', 'کارفرما'), F('summary', 'خلاصه', 'textarea'), F('body', 'متن پروژه (هر پاراگراف یک خط)', 'list'), ], defaults: { title: 'پروژه جدید', slug: `project-${Date.now()}`, location: '', year: '', status: 'done', image: '', gallery: [], area: '', floors: '', units: '', client: '', summary: '', body: [] }, diff --git a/frontend/src/i18n/dict.ts b/frontend/src/i18n/dict.ts index 26749ec..43f2749 100644 --- a/frontend/src/i18n/dict.ts +++ b/frontend/src/i18n/dict.ts @@ -53,7 +53,10 @@ export const fa = { projectNotFound: 'این پروژه پیدا نشد', projectNotFoundText: 'ممکن است نشانی تغییر کرده باشد. فهرست کامل پروژه‌ها را ببینید.', backToProjects: 'بازگشت به پروژه‌ها', + specStatus: 'وضعیت', specLocation: 'موقعیت', + specProjectType: 'نوع پروژه', + specUsage: 'کاربری', specYear: 'سال', specArea: 'زیربنا', specFloors: 'تعداد طبقات', @@ -166,7 +169,10 @@ export const en: Dict = { projectNotFound: 'Project not found', projectNotFoundText: 'The address may have changed. Browse the full list of projects.', backToProjects: 'Back to projects', + specStatus: 'Status', specLocation: 'Location', + specProjectType: 'Project type', + specUsage: 'Land use', specYear: 'Year', specArea: 'Built area', specFloors: 'Floors', diff --git a/frontend/src/pages/ProjectDetail.tsx b/frontend/src/pages/ProjectDetail.tsx index 5ace33b..5798f89 100644 --- a/frontend/src/pages/ProjectDetail.tsx +++ b/frontend/src/pages/ProjectDetail.tsx @@ -49,14 +49,18 @@ export function ProjectDetail() { const related = projects.filter((p) => p.slug !== project.slug).slice(0, 3) + // مشخصات هر پروژه — همان فهرستی که تیم داده دارد: + // وضعیت / سال / تعداد طبقات / واحدها / نوع پروژه / زیربنا / کاربری + // ردیف‌های خالی نمایش داده نمی‌شوند. const facts = [ - { label: t('specLocation'), value: project.location }, + { label: t('specStatus'), value: statusLabel }, { label: t('specYear'), value: project.year }, - { label: t('specArea'), value: project.area }, { label: t('specFloors'), value: project.floors }, { label: t('specUnits'), value: project.units }, - { label: t('specClient'), value: project.client }, - ] + { label: t('specProjectType'), value: project.project_type }, + { label: t('specArea'), value: project.area }, + { label: t('specUsage'), value: project.usage }, + ].filter((f) => String(f.value ?? '').trim() !== '') return ( <>