diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4874598..eaad8aa 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -22,6 +22,7 @@ import { AdminProvider, useAdmin } from './admin/AdminContext'; import { AdminBar } from './admin/AdminBar'; import { AdminLoginPage } from './admin/AdminLoginPage'; import { ArticleWritePage } from './admin/ArticleWritePage'; +import { ProjectWritePage } from './admin/ProjectWritePage'; import { LangProvider, useLang } from './i18n/lang'; // مسیرهای عمومی یک بار تعریف می‌شوند و دو بار سرو می‌شوند: ریشه فارسی و /en انگلیسی @@ -69,6 +70,8 @@ function AnimatedRoutes() { } /> } /> } /> + } /> + } /> {FA_ROUTES} } /> {EN_ROUTES} diff --git a/frontend/src/admin/Editable.tsx b/frontend/src/admin/Editable.tsx index 84fb0e1..9372f98 100644 --- a/frontend/src/admin/Editable.tsx +++ b/frontend/src/admin/Editable.tsx @@ -1,5 +1,6 @@ import { useState } from 'react' import type { ReactNode } from 'react' +import { useNavigate } from 'react-router-dom' import { ArrowDownIcon, ArrowUpIcon, PencilIcon, PlusIcon, Trash2Icon } from 'lucide-react' import { Link } from 'react-router-dom' import Swal from 'sweetalert2' @@ -163,12 +164,16 @@ export function AddItemButton({ resource, label, variant = 'cell', + /** در صورت ارسال، پس از ساخته‌شدن رکورد به صفحهٔ ویرایش اختصاصی هدایت می‌شود (کشو باز نمی‌شود) */ + editHrefBase, }: { resource: string label?: string variant?: 'cell' | 'button' + editHrefBase?: string }) { const { isAuthenticated, isEditMode, runSave } = useAdmin() + const navigate = useNavigate() const { updateSite } = useSiteData() const config = ENTITIES[resource] const [newItem, setNewItem] = useState(null) @@ -183,6 +188,10 @@ export function AddItemButton({ const list = d[config.dataKey as keyof SiteData] as unknown as ItemWithId[] if (Array.isArray(list)) list.push(created) }) + if (editHrefBase) { + navigate(`${editHrefBase}/${created.id}`) + return + } setNewItem(created) }) } diff --git a/frontend/src/admin/ProjectWritePage.tsx b/frontend/src/admin/ProjectWritePage.tsx new file mode 100644 index 0000000..d7e5032 --- /dev/null +++ b/frontend/src/admin/ProjectWritePage.tsx @@ -0,0 +1,345 @@ +import { useEffect, useState } from 'react' +import { Link, Navigate, useNavigate, useParams } from 'react-router-dom' +import { ArrowRightIcon, Trash2Icon } from 'lucide-react' +import Swal from 'sweetalert2' +import { useSiteData } from '../api' +import { useAdmin } from './AdminContext' +import { adminApi } from './adminClient' +import { ImageField } from '../components/admin/ImageField' +import { GalleryField } from '../components/admin/GalleryField' + +type ProjectDraft = { + id: number + slug: string + title: string + title_en: string + location: string + location_en: string + year: string + year_en: string + status: 'done' | 'progress' + project_type: string + project_type_en: string + usage: string + usage_en: string + area: string + area_en: string + floors: string + floors_en: string + units: string + units_en: string + client: string + client_en: string + summary: string + summary_en: string + body: string[] + body_en: string[] + image: string + gallery: string[] +} + +const EMPTY: ProjectDraft = { + id: 0, + slug: `project-${Date.now()}`, + title: '', title_en: '', + location: '', location_en: '', + year: '', year_en: '', + status: 'done', + project_type: '', project_type_en: '', + usage: '', usage_en: '', + area: '', area_en: '', + floors: '', floors_en: '', + units: '', units_en: '', + client: '', client_en: '', + summary: '', summary_en: '', + body: [], body_en: [], + image: '', gallery: [], +} + +const INPUT = + 'w-full border border-ivory/15 bg-char px-3 py-2 text-sm font-light text-ivory placeholder:text-ivory/30 focus:border-copper focus:outline-none' + +/** جفت دوزبانه — فارسی و English همیشه هر دو دیده می‌شوند و با یک ذخیره ثبت می‌شوند */ +function Pair({ + label, + fa, + en, + textarea = false, +}: { + label: string + fa: { value: string; onChange: (v: string) => void } + en: { value: string; onChange: (v: string) => void } + textarea?: boolean +}) { + return ( +
+

{label}

+
+
+ فارسی + {textarea ? ( +