From 2615d2c4aef8068df690523e7a8949ef36498434 Mon Sep 17 00:00:00 2001 From: Muhammad Amin Ghorbani Date: Wed, 28 Oct 2026 17:30:54 +0330 Subject: [PATCH] feat: add image, blog slug, and journeys count fields to custom trip destinations --- .../CustomTripDestinationDetailPage.tsx | 75 ++++++++++++++++- .../components/CustomTripDestinationsTab.tsx | 84 ++++++++++++++++++- .../custom-trip/services/custom-trip-api.ts | 7 ++ src/features/custom-trip/types.ts | 18 +++- 4 files changed, 179 insertions(+), 5 deletions(-) diff --git a/src/features/custom-trip/CustomTripDestinationDetailPage.tsx b/src/features/custom-trip/CustomTripDestinationDetailPage.tsx index 5c70bcc..4740d69 100644 --- a/src/features/custom-trip/CustomTripDestinationDetailPage.tsx +++ b/src/features/custom-trip/CustomTripDestinationDetailPage.tsx @@ -89,6 +89,9 @@ export default function CustomTripDestinationDetailPage() { const [destNameEn, setDestNameEn] = useState('') const [destCountry, setDestCountry] = useState('عراق') const [destFlag, setDestFlag] = useState('🇮🇶') + const [destImage, setDestImage] = useState('') + const [destBlogSlug, setDestBlogSlug] = useState('') + const [destJourneysCount, setDestJourneysCount] = useState(10) const [destDescription, setDestDescription] = useState('') const [destHolyPlaces, setDestHolyPlaces] = useState('') const [destIsActive, setDestIsActive] = useState(true) @@ -151,6 +154,9 @@ export default function CustomTripDestinationDetailPage() { setDestNameEn(target.nameEn || '') setDestCountry(target.country) setDestFlag(target.flag || '📍') + setDestImage(target.image || '') + setDestBlogSlug(target.blogSlug || target.blog_slug || '') + setDestJourneysCount(target.journeysCount ?? target.journeys_count ?? 10) setDestDescription(target.description || '') setDestHolyPlaces((target.holyPlaces || []).join('، ')) setDestIsActive(target.isActive) @@ -433,6 +439,9 @@ export default function CustomTripDestinationDetailPage() { nameEn: primaryNameEn, country: destCountry.trim(), flag: destFlag, + image: destImage.trim() || undefined, + blogSlug: destBlogSlug.trim() || undefined, + journeysCount: Number(destJourneysCount) || 10, description: primaryDesc, holyPlaces: primaryPlaces.length ? primaryPlaces : undefined, isActive: destIsActive, @@ -884,8 +893,21 @@ export default function CustomTripDestinationDetailPage() {
{/* هدر: نام شهر، وضعیت و دکمه‌های اکشن */}
-
- {destFlag || destination.flag || '📍'} +
+ {destination.image || destImage ? ( +
+ {destination.name} { + (e.currentTarget as HTMLElement).style.display = 'none' + }} + /> +
+ ) : ( + {destFlag || destination.flag || '📍'} + )}

@@ -1085,6 +1107,55 @@ export default function CustomTripDestinationDetailPage() { {/* فرم: بدنه اسکرول‌شونده و فوتر فیکس‌شده */}
+ {/* تنظیمات رسانه‌ای، تصویر و پیوند به وبلاگ */} +
+
+ + setDestImage(e.target.value)} + dir="ltr" + placeholder="مثال: /media/destinations/karbala.jpg یا https://..." + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+ +
+ + setDestJourneysCount(Number(e.target.value) || 0)} + dir="ltr" + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+ +
+ + setDestBlogSlug(e.target.value)} + dir="ltr" + placeholder="مثال: ziyarat-rituals-and-prayers-of-karbala" + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+
+ {/* تنظیمات کلی شهر مقصد (کشور و وضعیت) */}
{/* انتخاب کشور مقصد */} diff --git a/src/features/custom-trip/components/CustomTripDestinationsTab.tsx b/src/features/custom-trip/components/CustomTripDestinationsTab.tsx index 3cffa9d..ca5120e 100644 --- a/src/features/custom-trip/components/CustomTripDestinationsTab.tsx +++ b/src/features/custom-trip/components/CustomTripDestinationsTab.tsx @@ -324,6 +324,9 @@ export function CustomTripDestinationsTab({ const [formFlag, setFormFlag] = useState('🇮🇶') const [selectedCityPresetId, setSelectedCityPresetId] = useState('') const [formIsActive, setFormIsActive] = useState(true) + const [formImage, setFormImage] = useState('') + const [formBlogSlug, setFormBlogSlug] = useState('') + const [formJourneysCount, setFormJourneysCount] = useState(10) const currentLang = SERVICE_LANGUAGES.find((l) => l.code === activeLangTab) || SERVICE_LANGUAGES[0] @@ -360,6 +363,9 @@ export function CustomTripDestinationsTab({ setFormFlag('🇮🇶') setSelectedCityPresetId('') setFormIsActive(true) + setFormImage('') + setFormBlogSlug('') + setFormJourneysCount(10) setValidationError(null) setIsFormOpen(true) } @@ -392,6 +398,9 @@ export function CustomTripDestinationsTab({ setDescriptionsMap(initialDesc) setFormCountry(item.country) setFormFlag(item.flag || '📍') + setFormImage(item.image || '') + setFormBlogSlug(item.blogSlug || item.blog_slug || '') + setFormJourneysCount(item.journeysCount ?? item.journeys_count ?? 10) // Find if item corresponds to an official preset city const matched = OFFICIAL_PRESET_CITIES.find( @@ -473,6 +482,9 @@ export function CustomTripDestinationsTab({ nameEn: names.en.trim() || undefined, country: formCountry.trim(), flag: formFlag, + image: formImage.trim() || undefined, + blogSlug: formBlogSlug.trim() || undefined, + journeysCount: Number(formJourneysCount) || 10, description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, isActive: formIsActive, @@ -486,6 +498,9 @@ export function CustomTripDestinationsTab({ nameEn: names.en.trim() || undefined, country: formCountry.trim() || 'عراق', flag: formFlag, + image: formImage.trim() || undefined, + blogSlug: formBlogSlug.trim() || undefined, + journeysCount: Number(formJourneysCount) || 10, description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, hotelsCount: 0, @@ -643,8 +658,21 @@ export function CustomTripDestinationsTab({ > {toFa(idx + 1)} -
- {item.flag || '📍'} +
+ {item.image ? ( +
+ {item.name} { + (e.currentTarget as HTMLElement).style.display = 'none' + }} + /> +
+ ) : ( + {item.flag || '📍'} + )}
{item.name} @@ -801,6 +829,58 @@ export function CustomTripDestinationsTab({ />
+ {/* فیلدهای تنظیمات رسانه‌ای، تصویر و پیوند به راهنمای بلاگ */} +
+ {/* آدرس یا مسیر تصویر شهر */} +
+ + setFormImage(e.target.value)} + dir="ltr" + placeholder="مثال: /media/destinations/karbala.jpg یا https://..." + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+ + {/* تعداد تورها / سفرها */} +
+ + setFormJourneysCount(Number(e.target.value) || 0)} + dir="ltr" + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+ + {/* اسلاگ مقاله بلاگ */} +
+ + setFormBlogSlug(e.target.value)} + dir="ltr" + placeholder="مثال: ziyarat-rituals-and-prayers-of-karbala" + className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" + /> +
+
+ {/* فیلدهای انتخابی کشور و شهر بر اساس کشور انتخابی (پایین‌تر از تب‌های زبان) */}
{/* فیلد انتخابی کشور مقصد */} diff --git a/src/features/custom-trip/services/custom-trip-api.ts b/src/features/custom-trip/services/custom-trip-api.ts index 7a9540b..8eeeecc 100644 --- a/src/features/custom-trip/services/custom-trip-api.ts +++ b/src/features/custom-trip/services/custom-trip-api.ts @@ -451,6 +451,13 @@ export async function saveCustomTripDestination( name_en: item.nameEn, country: item.country, flag: item.flag, + image: item.image, + blog_slug: item.blogSlug || item.blog_slug, + blogSlug: item.blogSlug || item.blog_slug, + journeys_count: item.journeysCount ?? item.journeys_count, + journeysCount: item.journeysCount ?? item.journeys_count, + short_description: item.shortDescription || item.short_description, + shortDescription: item.shortDescription || item.short_description, description: item.description, holyPlaces: item.holyPlaces, holy_places: item.holyPlaces, diff --git a/src/features/custom-trip/types.ts b/src/features/custom-trip/types.ts index 9abc23d..b488702 100644 --- a/src/features/custom-trip/types.ts +++ b/src/features/custom-trip/types.ts @@ -87,12 +87,28 @@ export interface CustomTripDestinationItem { nameEn?: string country: string flag?: string + image?: string + blogSlug?: string + blog_slug?: string + journeysCount?: number + journeys_count?: number + shortDescription?: string + short_description?: string description?: string holyPlaces?: string[] hotelsCount: number transportsCount?: number isActive: boolean - translations?: Record + translations?: Record< + string, + { + name?: string + description?: string + short_description?: string + shortDescription?: string + holyPlaces?: string[] + } + > } export interface CustomTripTransportRelationItem {