feat(projects): فیلدهای «نوع پروژه» و «کاربری» + فهرست مشخصات جدید صفحهٔ جزئیات

- دیتابیس/مدل/اسکیما: ستون‌های project_type/usage دوزبانه (مهاجرت DDL
  idempotent — با استارت بک‌اند اعمال می‌شود)
- پنل: کشویر پروژه‌ها دو فیلد تازه گرفت
- صفحهٔ جزئیات: فهرست مشخصات مطابق داده‌های تیم — وضعیت / سال / طبقات /
  واحدها / نوع پروژه / زیربنا / کاربری (ردیف‌های خالی پنهان‌اند)
This commit is contained in:
aramesh-site deploy
2026-09-28 10:17:25 +00:00
parent 5cf7b51686
commit edcbd0dc58
6 changed files with 33 additions and 5 deletions
+4
View File
@@ -29,6 +29,10 @@ EN_COLUMNS: list[tuple[str, str, str]] = [
("projects", "floors_en", "VARCHAR(64)"), ("projects", "floors_en", "VARCHAR(64)"),
("projects", "units_en", "VARCHAR(64)"), ("projects", "units_en", "VARCHAR(64)"),
("projects", "client_en", "VARCHAR(128)"), ("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", "summary_en", "TEXT"),
("projects", "body_en", "JSON"), ("projects", "body_en", "JSON"),
("articles", "title_en", "VARCHAR(256)"), ("articles", "title_en", "VARCHAR(256)"),
+5
View File
@@ -76,6 +76,11 @@ class Project(Base):
units_en: Mapped[str] = mapped_column(String(64), default="") units_en: Mapped[str] = mapped_column(String(64), 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="")
# مشخصات تکمیلی پروژه — به درخواست تیم: نوع پروژه و کاربری
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: Mapped[str] = mapped_column(Text, default="")
summary_en: Mapped[str] = mapped_column(Text, default="") summary_en: Mapped[str] = mapped_column(Text, default="")
body: Mapped[list] = mapped_column(JSON, default=list) body: Mapped[list] = mapped_column(JSON, default=list)
+8
View File
@@ -61,6 +61,10 @@ class ProjectOut(ORMModel):
units_en: str = "" units_en: str = ""
client: str client: str
client_en: str = "" client_en: str = ""
project_type: str = ""
project_type_en: str = ""
usage: str = ""
usage_en: str = ""
summary: str summary: str
summary_en: str = "" summary_en: str = ""
body: list[str] body: list[str]
@@ -451,6 +455,10 @@ class ProjectIn(BaseModel):
year: str = "" year: str = ""
year_en: str = "" year_en: str = ""
status: Literal["done", "progress"] = "done" status: Literal["done", "progress"] = "done"
project_type: str = ""
project_type_en: str = ""
usage: str = ""
usage_en: str = ""
image: str = "" image: str = ""
gallery: list[str] = [] gallery: list[str] = []
area: str = "" area: str = ""
+2 -1
View File
@@ -69,7 +69,8 @@ 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('client', 'کارفرما'), F('area', 'زیربنا'), F('floors', 'طبقات'), F('units', 'واحدها'),
F('project_type', 'نوع پروژه'), F('usage', 'کاربری'), F('client', 'کارفرما'),
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: [] },
+6
View File
@@ -53,7 +53,10 @@ export const fa = {
projectNotFound: 'این پروژه پیدا نشد', projectNotFound: 'این پروژه پیدا نشد',
projectNotFoundText: 'ممکن است نشانی تغییر کرده باشد. فهرست کامل پروژه‌ها را ببینید.', projectNotFoundText: 'ممکن است نشانی تغییر کرده باشد. فهرست کامل پروژه‌ها را ببینید.',
backToProjects: 'بازگشت به پروژه‌ها', backToProjects: 'بازگشت به پروژه‌ها',
specStatus: 'وضعیت',
specLocation: 'موقعیت', specLocation: 'موقعیت',
specProjectType: 'نوع پروژه',
specUsage: 'کاربری',
specYear: 'سال', specYear: 'سال',
specArea: 'زیربنا', specArea: 'زیربنا',
specFloors: 'تعداد طبقات', specFloors: 'تعداد طبقات',
@@ -166,7 +169,10 @@ export const en: Dict = {
projectNotFound: 'Project not found', projectNotFound: 'Project not found',
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',
specLocation: 'Location', specLocation: 'Location',
specProjectType: 'Project type',
specUsage: 'Land use',
specYear: 'Year', specYear: 'Year',
specArea: 'Built area', specArea: 'Built area',
specFloors: 'Floors', specFloors: 'Floors',
+8 -4
View File
@@ -49,14 +49,18 @@ export function ProjectDetail() {
const related = projects.filter((p) => p.slug !== project.slug).slice(0, 3) const related = projects.filter((p) => p.slug !== project.slug).slice(0, 3)
// مشخصات هر پروژه — همان فهرستی که تیم داده دارد:
// وضعیت / سال / تعداد طبقات / واحدها / نوع پروژه / زیربنا / کاربری
// ردیف‌های خالی نمایش داده نمی‌شوند.
const facts = [ const facts = [
{ label: t('specLocation'), value: project.location }, { label: t('specStatus'), value: statusLabel },
{ label: t('specYear'), value: project.year }, { label: t('specYear'), value: project.year },
{ label: t('specArea'), value: project.area },
{ label: t('specFloors'), value: project.floors }, { label: t('specFloors'), value: project.floors },
{ label: t('specUnits'), value: project.units }, { 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 ( return (
<> <>