|
|
@ -324,6 +324,9 @@ export function CustomTripDestinationsTab({ |
|
|
const [formFlag, setFormFlag] = useState('🇮🇶') |
|
|
const [formFlag, setFormFlag] = useState('🇮🇶') |
|
|
const [selectedCityPresetId, setSelectedCityPresetId] = useState<string>('') |
|
|
const [selectedCityPresetId, setSelectedCityPresetId] = useState<string>('') |
|
|
const [formIsActive, setFormIsActive] = useState(true) |
|
|
const [formIsActive, setFormIsActive] = useState(true) |
|
|
|
|
|
const [formImage, setFormImage] = useState<string>('') |
|
|
|
|
|
const [formBlogSlug, setFormBlogSlug] = useState<string>('') |
|
|
|
|
|
const [formJourneysCount, setFormJourneysCount] = useState<number>(10) |
|
|
|
|
|
|
|
|
const currentLang = |
|
|
const currentLang = |
|
|
SERVICE_LANGUAGES.find((l) => l.code === activeLangTab) || SERVICE_LANGUAGES[0] |
|
|
SERVICE_LANGUAGES.find((l) => l.code === activeLangTab) || SERVICE_LANGUAGES[0] |
|
|
@ -360,6 +363,9 @@ export function CustomTripDestinationsTab({ |
|
|
setFormFlag('🇮🇶') |
|
|
setFormFlag('🇮🇶') |
|
|
setSelectedCityPresetId('') |
|
|
setSelectedCityPresetId('') |
|
|
setFormIsActive(true) |
|
|
setFormIsActive(true) |
|
|
|
|
|
setFormImage('') |
|
|
|
|
|
setFormBlogSlug('') |
|
|
|
|
|
setFormJourneysCount(10) |
|
|
setValidationError(null) |
|
|
setValidationError(null) |
|
|
setIsFormOpen(true) |
|
|
setIsFormOpen(true) |
|
|
} |
|
|
} |
|
|
@ -392,6 +398,9 @@ export function CustomTripDestinationsTab({ |
|
|
setDescriptionsMap(initialDesc) |
|
|
setDescriptionsMap(initialDesc) |
|
|
setFormCountry(item.country) |
|
|
setFormCountry(item.country) |
|
|
setFormFlag(item.flag || '📍') |
|
|
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
|
|
|
// Find if item corresponds to an official preset city
|
|
|
const matched = OFFICIAL_PRESET_CITIES.find( |
|
|
const matched = OFFICIAL_PRESET_CITIES.find( |
|
|
@ -473,6 +482,9 @@ export function CustomTripDestinationsTab({ |
|
|
nameEn: names.en.trim() || undefined, |
|
|
nameEn: names.en.trim() || undefined, |
|
|
country: formCountry.trim(), |
|
|
country: formCountry.trim(), |
|
|
flag: formFlag, |
|
|
flag: formFlag, |
|
|
|
|
|
image: formImage.trim() || undefined, |
|
|
|
|
|
blogSlug: formBlogSlug.trim() || undefined, |
|
|
|
|
|
journeysCount: Number(formJourneysCount) || 10, |
|
|
description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, |
|
|
description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, |
|
|
holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, |
|
|
holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, |
|
|
isActive: formIsActive, |
|
|
isActive: formIsActive, |
|
|
@ -486,6 +498,9 @@ export function CustomTripDestinationsTab({ |
|
|
nameEn: names.en.trim() || undefined, |
|
|
nameEn: names.en.trim() || undefined, |
|
|
country: formCountry.trim() || 'عراق', |
|
|
country: formCountry.trim() || 'عراق', |
|
|
flag: formFlag, |
|
|
flag: formFlag, |
|
|
|
|
|
image: formImage.trim() || undefined, |
|
|
|
|
|
blogSlug: formBlogSlug.trim() || undefined, |
|
|
|
|
|
journeysCount: Number(formJourneysCount) || 10, |
|
|
description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, |
|
|
description: descriptionsMap.fa.trim() || descriptionsMap.ar.trim() || undefined, |
|
|
holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, |
|
|
holyPlaces: holyPlacesArray.length ? holyPlacesArray : undefined, |
|
|
hotelsCount: 0, |
|
|
hotelsCount: 0, |
|
|
@ -643,8 +658,21 @@ export function CustomTripDestinationsTab({ |
|
|
> |
|
|
> |
|
|
<td className="px-4 py-3.5 font-mono text-muted-foreground">{toFa(idx + 1)}</td> |
|
|
<td className="px-4 py-3.5 font-mono text-muted-foreground">{toFa(idx + 1)}</td> |
|
|
<td className="px-4 py-3.5 font-bold text-foreground"> |
|
|
<td className="px-4 py-3.5 font-bold text-foreground"> |
|
|
<div className="flex items-center gap-2.5"> |
|
|
|
|
|
|
|
|
<div className="flex items-center gap-3"> |
|
|
|
|
|
{item.image ? ( |
|
|
|
|
|
<div className="size-11 rounded-xl overflow-hidden shrink-0 border border-border/70 bg-muted/40 shadow-2xs"> |
|
|
|
|
|
<img |
|
|
|
|
|
src={item.image} |
|
|
|
|
|
alt={item.name} |
|
|
|
|
|
className="size-full object-cover group-hover:scale-105 transition-transform duration-300" |
|
|
|
|
|
onError={(e) => { |
|
|
|
|
|
(e.currentTarget as HTMLElement).style.display = 'none' |
|
|
|
|
|
}} |
|
|
|
|
|
/> |
|
|
|
|
|
</div> |
|
|
|
|
|
) : ( |
|
|
<span className="text-xl select-none">{item.flag || '📍'}</span> |
|
|
<span className="text-xl select-none">{item.flag || '📍'}</span> |
|
|
|
|
|
)} |
|
|
<div> |
|
|
<div> |
|
|
<div className="group-hover:text-primary transition-colors flex items-center gap-1.5 font-bold text-sm"> |
|
|
<div className="group-hover:text-primary transition-colors flex items-center gap-1.5 font-bold text-sm"> |
|
|
<span>{item.name}</span> |
|
|
<span>{item.name}</span> |
|
|
@ -801,6 +829,58 @@ export function CustomTripDestinationsTab({ |
|
|
/> |
|
|
/> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* فیلدهای تنظیمات رسانهای، تصویر و پیوند به راهنمای بلاگ */} |
|
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 p-3.5 rounded-xl bg-card border border-border/70 shadow-2xs"> |
|
|
|
|
|
{/* آدرس یا مسیر تصویر شهر */} |
|
|
|
|
|
<div className="space-y-1.5 sm:col-span-2"> |
|
|
|
|
|
<label className="text-xs font-bold text-foreground flex items-center justify-between"> |
|
|
|
|
|
<span className="flex items-center gap-1.5"> |
|
|
|
|
|
<Ic name="image" className="size-3.5 text-muted-foreground" /> |
|
|
|
|
|
<span>تصویر شهر مقصد (URL یا مسیر فایل)</span> |
|
|
|
|
|
</span> |
|
|
|
|
|
{formImage && ( |
|
|
|
|
|
<span className="text-[10px] text-emerald-600 font-medium">دارای تصویر</span> |
|
|
|
|
|
)} |
|
|
|
|
|
</label> |
|
|
|
|
|
<Input |
|
|
|
|
|
value={formImage} |
|
|
|
|
|
onChange={(e) => 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" |
|
|
|
|
|
/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* تعداد تورها / سفرها */} |
|
|
|
|
|
<div className="space-y-1.5"> |
|
|
|
|
|
<label className="text-xs font-bold text-foreground block"> |
|
|
|
|
|
تعداد سفرهای فعال: |
|
|
|
|
|
</label> |
|
|
|
|
|
<Input |
|
|
|
|
|
type="number" |
|
|
|
|
|
min={0} |
|
|
|
|
|
value={formJourneysCount} |
|
|
|
|
|
onChange={(e) => setFormJourneysCount(Number(e.target.value) || 0)} |
|
|
|
|
|
dir="ltr" |
|
|
|
|
|
className="h-10 rounded-xl text-xs bg-card border-border/70 font-mono shadow-2xs" |
|
|
|
|
|
/> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
{/* اسلاگ مقاله بلاگ */} |
|
|
|
|
|
<div className="space-y-1.5 sm:col-span-3"> |
|
|
|
|
|
<label className="text-xs font-bold text-foreground block"> |
|
|
|
|
|
اسلاگ مقاله راهنمای زیارت در بلاگ (اختیاری): |
|
|
|
|
|
</label> |
|
|
|
|
|
<Input |
|
|
|
|
|
value={formBlogSlug} |
|
|
|
|
|
onChange={(e) => 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" |
|
|
|
|
|
/> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
{/* فیلدهای انتخابی کشور و شهر بر اساس کشور انتخابی (پایینتر از تبهای زبان) */} |
|
|
{/* فیلدهای انتخابی کشور و شهر بر اساس کشور انتخابی (پایینتر از تبهای زبان) */} |
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 p-3.5 rounded-xl bg-card border border-border/70 shadow-2xs"> |
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3 p-3.5 rounded-xl bg-card border border-border/70 shadow-2xs"> |
|
|
{/* فیلد انتخابی کشور مقصد */} |
|
|
{/* فیلد انتخابی کشور مقصد */} |
|
|
|