import React, { useState, useEffect, useMemo } from 'react' import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom' import { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, } from '@/components/ui/dialog' import DeleteConfirmDialog from '@/components/shared/DeleteConfirmDialog' import { Switch } from '@/components/ui/switch' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Ic } from '@/icons' import { toFa, cn } from '@/lib/utils' import { useTranslation } from '@/i18n' import { Shimmer } from '@/components/shared/shimmer_frontend' import { CustomTripDestinationItem, CustomTripHotelItem, CustomTripTransportRelationItem, CustomTripOriginItem, } from './types' import { ServiceTranslation, SERVICE_LANGUAGES, type ServiceLanguageCode, } from '@/components/shared/ServiceTranslation' import { OFFICIAL_PRESET_CITIES } from './components/CustomTripDestinationsTab' import { fetchCustomTripDestinations, fetchCustomTripOrigins, fetchCustomTripHotels, fetchCustomTripTransportRelations, saveCustomTripDestination, deleteCustomTripDestination, saveCustomTripHotel, deleteCustomTripHotel, saveCustomTripTransportRelation, deleteCustomTripTransportRelation, } from './services/custom-trip-api' const PRESET_DEST_COUNTRIES = [ { name: 'عراق', flag: '🇮🇶' }, { name: 'ایران', flag: '🇮🇷' }, { name: 'عربستان سعودی', flag: '🇸🇦' }, { name: 'سوریه', flag: '🇸🇾' }, ] const COMMON_AMENITIES = [ { id: 'wifi', name: 'اینترنت وای‌فای رایگان' }, { id: 'restaurant', name: 'رستوران ایرانی و بوفه باز' }, { id: 'shrine_transfer', name: 'ترانسفر حرم مطهر رایگان' }, { id: 'airport_transfer', name: 'ترانسفر فرودگاهی اختصاصی' }, { id: 'laundry', name: 'لاندری و خدمات خشکشویی' }, { id: 'wheelchair', name: 'خدمات ویژه ویلچر و توان‌یابان' }, { id: 'doctor', name: 'پزشک و خدمات درمانی مقیم' }, { id: 'safety_box', name: 'صندوق امانات دیجیتال در اتاق' }, ] export default function CustomTripDestinationDetailPage() { const { t } = useTranslation() const { id } = useParams<{ id: string }>() const navigate = useNavigate() const [searchParams, setSearchParams] = useSearchParams() // Default active tab is 'info' (مشخصات و اطلاعات شهر) const currentTab = searchParams.get('tab') || 'info' const [isLoading, setIsLoading] = useState(true) const [destination, setDestination] = useState(null) const [allOrigins, setAllOrigins] = useState([]) const [allDestinations, setAllDestinations] = useState([]) const [allHotels, setAllHotels] = useState([]) const [allRelations, setAllRelations] = useState([]) // --- TAB 1: DESTINATION INFO FORM STATE --- const [destName, setDestName] = useState('') 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) const [isDestSavedSuccess, setIsDestSavedSuccess] = useState(false) const [isDestDeleteOpen, setIsDestDeleteOpen] = useState(false) const [isEditDestModalOpen, setIsEditDestModalOpen] = useState(false) const [isTogglingStatus, setIsTogglingStatus] = useState(false) const [isSavingDestination, setIsSavingDestination] = useState(false) const handleToggleStatus = async (newVal: boolean) => { if (!destination) return setIsTogglingStatus(true) try { const updated: CustomTripDestinationItem = { ...destination, isActive: newVal, } await saveCustomTripDestination(updated) setDestIsActive(newVal) setDestination(updated) setIsDestSavedSuccess(true) setTimeout(() => setIsDestSavedSuccess(false), 2500) } catch (err) { console.error('Failed to toggle destination active status:', err) } finally { setIsTogglingStatus(false) } } // Multi-language Tab 1 states (Arabic is default primary) const [activeLangTab, setActiveLangTab] = useState('ar') const [names, setNames] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [holyPlacesMap, setHolyPlacesMap] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [descriptionsMap, setDescriptionsMap] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const currentLang = SERVICE_LANGUAGES.find((l) => l.code === activeLangTab) || SERVICE_LANGUAGES[0] const initDestinationState = (target: CustomTripDestinationItem) => { setDestination(target) setDestName(target.name) 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) const preset = OFFICIAL_PRESET_CITIES.find( (c) => String(c.id).toLowerCase() === String(target.id).toLowerCase() || c.names.fa === target.name || c.names.en.toLowerCase() === (target.nameEn || '').toLowerCase() ) setNames({ ar: target.translations?.ar?.name || preset?.names.ar || '', fa: target.translations?.fa?.name || preset?.names.fa || target.name || '', en: target.translations?.en?.name || preset?.names.en || target.nameEn || '', ru: target.translations?.ru?.name || preset?.names.ru || '', id: target.translations?.id?.name || preset?.names.id || '', }) setHolyPlacesMap({ ar: target.translations?.ar?.holyPlaces?.join('، ') || preset?.holyPlaces.ar || '', fa: target.translations?.fa?.holyPlaces?.join('، ') || preset?.holyPlaces.fa || (target.holyPlaces || []).join('، '), en: target.translations?.en?.holyPlaces?.join(', ') || preset?.holyPlaces.en || '', ru: target.translations?.ru?.holyPlaces?.join(', ') || preset?.holyPlaces.ru || '', id: target.translations?.id?.holyPlaces?.join(', ') || preset?.holyPlaces.id || '', }) setDescriptionsMap({ ar: target.translations?.ar?.description || preset?.description.ar || '', fa: target.translations?.fa?.description || preset?.description.fa || target.description || '', en: target.translations?.en?.description || preset?.description.en || '', ru: target.translations?.ru?.description || preset?.description.ru || '', id: target.translations?.id?.description || preset?.description.id || '', }) } // --- TAB 2: HOTELS STATE --- const [hotelSearch, setHotelSearch] = useState('') const [hotelStarsFilter, setHotelStarsFilter] = useState<'all' | '5' | '4' | '3'>('all') const [hotelViewMode, setHotelViewMode] = useState<'cards' | 'table'>('cards') const [isHotelFormOpen, setIsHotelFormOpen] = useState(false) const [editingHotel, setEditingHotel] = useState(null) const [hotelToDelete, setHotelToDelete] = useState(null) // Hotel form fields const [hotelLangTab, setHotelLangTab] = useState('ar') const [hotelNames, setHotelNames] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [hotelDistances, setHotelDistances] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [hotelDescriptions, setHotelDescriptions] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [hotelGuideNotes, setHotelGuideNotes] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [hotelFormStars, setHotelFormStars] = useState(5) const [hotelFormScore, setHotelFormScore] = useState(4.8) const [hotelFormAmenities, setHotelFormAmenities] = useState([]) const [hotelFormCustomAmenity, setHotelFormCustomAmenity] = useState('') const [hotelFormPriceAdult, setHotelFormPriceAdult] = useState(100) const [hotelFormPriceChild, setHotelFormPriceChild] = useState(50) const [hotelFormIsActive, setHotelFormIsActive] = useState(true) // --- TAB 3: TRANSPORTS STATE --- const [transportSearch, setTransportSearch] = useState('') const [transportMethodFilter, setTransportMethodFilter] = useState<'all' | 'flight' | 'train' | 'road'>('all') const [transportViewMode, setTransportViewMode] = useState<'cards' | 'table'>('cards') const [isTransportFormOpen, setIsTransportFormOpen] = useState(false) const [editingTransport, setEditingTransport] = useState(null) const [transportToDelete, setTransportToDelete] = useState(null) // Transport form fields (Arabic is default primary) const [transportLangTab, setTransportLangTab] = useState('ar') const [transportGuideNotes, setTransportGuideNotes] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [transportAirplaneDetailsMap, setTransportAirplaneDetailsMap] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [transportTrainDetailsMap, setTransportTrainDetailsMap] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [transportRoadDetailsMap, setTransportRoadDetailsMap] = useState>({ ar: '', fa: '', en: '', ru: '', id: '', }) const [transportFormFromCity, setTransportFormFromCity] = useState('') const [transportFormFromCountry, setTransportFormFromCountry] = useState('') const [transportFormToCity, setTransportFormToCity] = useState('') const [transportFormToCountry, setTransportFormToCountry] = useState('') const [transportFormHasAirplane, setTransportFormHasAirplane] = useState(true) const [transportFormHasTrain, setTransportFormHasTrain] = useState(false) const [transportFormHasRoad, setTransportFormHasRoad] = useState(false) const [transportFormIsActive, setTransportFormIsActive] = useState(true) // Load all initial data useEffect(() => { let isMounted = true async function load() { setIsLoading(true) try { const [dests, origs, hotels, rels] = await Promise.all([ fetchCustomTripDestinations(), fetchCustomTripOrigins(), fetchCustomTripHotels(), fetchCustomTripTransportRelations(), ]) if (isMounted) { setAllDestinations(dests) setAllOrigins(origs) setAllHotels(hotels) setAllRelations(rels) const target = dests.find( (d) => String(d.id) === String(id) || d.name === String(id) || (d.nameEn && d.nameEn.toLowerCase() === String(id).toLowerCase()) ) if (target) { initDestinationState(target) } else if (dests.length > 0) { initDestinationState(dests[0]) } } } finally { if (isMounted) setIsLoading(false) } } load() return () => { isMounted = false } }, [id]) // Destination-specific hotels const destHotels = useMemo(() => { if (!destination) return [] return allHotels.filter( (h) => h.cityName === destination.name || destination.name.includes(h.cityName) || h.cityName.includes(destination.name) ) }, [allHotels, destination]) // Filtered hotels const filteredHotels = useMemo(() => { return destHotels.filter((h) => { const matchStars = hotelStarsFilter === 'all' || String(h.stars) === hotelStarsFilter const term = hotelSearch.trim().toLowerCase() const matchSearch = !term || h.name.toLowerCase().includes(term) || (h.distanceToShrine && h.distanceToShrine.toLowerCase().includes(term)) || (h.description && h.description.toLowerCase().includes(term)) || (h.guideNote && h.guideNote.toLowerCase().includes(term)) || h.amenities.some((a) => a.toLowerCase().includes(term)) return matchStars && matchSearch }) }, [destHotels, hotelStarsFilter, hotelSearch]) // Destination-specific transport relations const destRelations = useMemo(() => { if (!destination) return [] return allRelations.filter( (r) => r.toCity === destination.name || destination.name.includes(r.toCity) || r.toCity.includes(destination.name) ) }, [allRelations, destination]) // Filtered relations const filteredRelations = useMemo(() => { return destRelations.filter((rel) => { const matchMethod = transportMethodFilter === 'all' || (transportMethodFilter === 'flight' && rel.hasAirplane) || (transportMethodFilter === 'train' && rel.hasTrain) || (transportMethodFilter === 'road' && rel.hasRoad) const term = transportSearch.trim().toLowerCase() const matchSearch = !term || rel.fromCity.toLowerCase().includes(term) || rel.fromCountry.toLowerCase().includes(term) || (rel.airplaneDetails && rel.airplaneDetails.toLowerCase().includes(term)) || (rel.trainDetails && rel.trainDetails.toLowerCase().includes(term)) || (rel.roadDetails && rel.roadDetails.toLowerCase().includes(term)) || (rel.guideNote && rel.guideNote.toLowerCase().includes(term)) return matchMethod && matchSearch }) }, [destRelations, transportMethodFilter, transportSearch]) const handleTabChange = (tabKey: string) => { setSearchParams({ tab: tabKey }) } // --- TAB 1: SAVE DESTINATION HANDLER --- const handleSaveDestinationInfo = async (e: React.FormEvent) => { e.preventDefault() if (!destination || isSavingDestination) return const primaryName = names[activeLangTab]?.trim() || names.fa?.trim() || names.ar?.trim() || names.en?.trim() || destName.trim() if (!primaryName) return setIsSavingDestination(true) try { const translations: Record = {} SERVICE_LANGUAGES.forEach((lang) => { const langName = names[lang.code]?.trim() const langDesc = descriptionsMap[lang.code]?.trim() const langPlaces = (holyPlacesMap[lang.code] || '') .split(/[،,]/) .map((s) => s.trim()) .filter(Boolean) if (langName || langDesc || langPlaces.length > 0) { translations[lang.code] = { name: langName || undefined, description: langDesc || undefined, holyPlaces: langPlaces.length > 0 ? langPlaces : undefined, } } }) const primaryNameEn = names.en?.trim() || destNameEn.trim() || undefined const primaryDesc = descriptionsMap.fa?.trim() || descriptionsMap.ar?.trim() || descriptionsMap.en?.trim() || destDescription.trim() || undefined const primaryPlaces = (holyPlacesMap.fa || holyPlacesMap.ar || destHolyPlaces) .split(/[،,]/) .map((s) => s.trim()) .filter(Boolean) const updated: CustomTripDestinationItem = { ...destination, name: primaryName, 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, translations, } await saveCustomTripDestination(updated) setDestination(updated) setDestName(primaryName) if (primaryNameEn) setDestNameEn(primaryNameEn) if (primaryDesc) setDestDescription(primaryDesc) setIsDestSavedSuccess(true) setIsEditDestModalOpen(false) setIsSavingDestination(false) setTimeout(() => setIsDestSavedSuccess(false), 3000) } catch (err) { console.error('Failed to save destination:', err) setIsSavingDestination(false) } } const handleDeleteDestinationConfirm = async () => { if (!destination) return await deleteCustomTripDestination(destination.id) navigate('/custom-trip/destinations') } // --- TAB 2: HOTEL HANDLERS --- const currentHotelLang = SERVICE_LANGUAGES.find((l) => l.code === hotelLangTab) || SERVICE_LANGUAGES[0] const handleOpenAddHotel = () => { setEditingHotel(null) setHotelLangTab('ar') const emptyMap: Record = { ar: '', fa: '', en: '', ru: '', id: '' } setHotelNames(emptyMap) setHotelDistances(emptyMap) setHotelDescriptions(emptyMap) setHotelGuideNotes(emptyMap) setHotelFormStars(5) setHotelFormScore(4.8) setHotelFormAmenities(['اینترنت وای‌فای رایگان', 'رستوران ایرانی و بوفه باز']) setHotelFormCustomAmenity('') setHotelFormPriceAdult(110) setHotelFormPriceChild(55) setHotelFormIsActive(true) setIsHotelFormOpen(true) } const handleOpenEditHotel = (hotel: CustomTripHotelItem) => { setEditingHotel(hotel) setHotelLangTab('ar') const newNames: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newDistances: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newDescs: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newGuides: Record = { ar: '', fa: '', en: '', ru: '', id: '' } if (hotel.translations) { for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as ServiceLanguageCode[]) { if (hotel.translations[lang]?.name) newNames[lang] = hotel.translations[lang].name! if (hotel.translations[lang]?.distanceToShrine) newDistances[lang] = hotel.translations[lang].distanceToShrine! if (hotel.translations[lang]?.description) newDescs[lang] = hotel.translations[lang].description! if (hotel.translations[lang]?.guideNote) newGuides[lang] = hotel.translations[lang].guideNote! } } // Fallbacks if (!newNames.ar) newNames.ar = hotel.name || '' if (!newNames.fa) newNames.fa = hotel.name || '' if (!newDistances.ar) newDistances.ar = hotel.distanceToShrine || '' if (!newDistances.fa) newDistances.fa = hotel.distanceToShrine || '' if (!newDescs.ar) newDescs.ar = hotel.description || '' if (!newDescs.fa) newDescs.fa = hotel.description || '' if (!newGuides.ar && hotel.guideNote) newGuides.ar = hotel.guideNote if (!newGuides.fa && hotel.guideNote) newGuides.fa = hotel.guideNote setHotelNames(newNames) setHotelDistances(newDistances) setHotelDescriptions(newDescs) setHotelGuideNotes(newGuides) setHotelFormStars(hotel.stars) setHotelFormScore(hotel.score || 4.8) setHotelFormAmenities(hotel.amenities || []) setHotelFormCustomAmenity('') setHotelFormPriceAdult(hotel.pricePerDayAdult || 100) setHotelFormPriceChild(hotel.pricePerDayChild || 50) setHotelFormIsActive(hotel.isActive) setIsHotelFormOpen(true) } const handleToggleHotelAmenity = (name: string) => { setHotelFormAmenities((prev) => prev.includes(name) ? prev.filter((a) => a !== name) : [...prev, name] ) } const handleAddCustomHotelAmenity = () => { const val = hotelFormCustomAmenity.trim() if (!val) return if (!hotelFormAmenities.includes(val)) { setHotelFormAmenities((prev) => [...prev, val]) } setHotelFormCustomAmenity('') } const handleSaveHotel = async (e: React.FormEvent) => { e.preventDefault() if (!destination) return const primaryName = hotelNames[hotelLangTab]?.trim() || hotelNames.ar?.trim() || hotelNames.fa?.trim() || hotelNames.en?.trim() || 'هتل جدید' const primaryDistance = hotelDistances[hotelLangTab]?.trim() || hotelDistances.ar?.trim() || hotelDistances.fa?.trim() || 'فاصله استاندارد تا حرم مطهر' const primaryDesc = hotelDescriptions[hotelLangTab]?.trim() || hotelDescriptions.ar?.trim() || hotelDescriptions.fa?.trim() || 'هتل درجه یک و مجهز جهت اقامت زائرین گرامی.' const primaryGuide = hotelGuideNotes[hotelLangTab]?.trim() || hotelGuideNotes.ar?.trim() || hotelGuideNotes.fa?.trim() || undefined const translations: Record< string, { name?: string; description?: string; distanceToShrine?: string; guideNote?: string } > = {} for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as ServiceLanguageCode[]) { translations[lang] = { name: hotelNames[lang]?.trim() || primaryName, distanceToShrine: hotelDistances[lang]?.trim() || primaryDistance, description: hotelDescriptions[lang]?.trim() || primaryDesc, guideNote: hotelGuideNotes[lang]?.trim() || primaryGuide, } } const item: CustomTripHotelItem = { id: editingHotel ? editingHotel.id : `hotel_${Date.now()}`, name: primaryName, cityName: destination.name, stars: hotelFormStars, score: hotelFormScore, distanceToShrine: primaryDistance, amenities: hotelFormAmenities, description: primaryDesc, guideNote: primaryGuide, pricePerDayAdult: hotelFormPriceAdult, pricePerDayChild: hotelFormPriceChild, isActive: hotelFormIsActive, translations, } await saveCustomTripHotel(item) setAllHotels((prev) => { const exists = prev.some((h) => h.id === item.id) return exists ? prev.map((h) => (h.id === item.id ? item : h)) : [item, ...prev] }) setIsHotelFormOpen(false) } const handleConfirmDeleteHotel = async () => { if (!hotelToDelete) return await deleteCustomTripHotel(hotelToDelete.id) setAllHotels((prev) => prev.filter((h) => h.id !== hotelToDelete.id)) setHotelToDelete(null) } // --- TAB 3: TRANSPORT HANDLERS --- const currentTransportLang = SERVICE_LANGUAGES.find((l) => l.code === transportLangTab) || SERVICE_LANGUAGES[0] const handleOpenAddTransport = () => { setEditingTransport(null) setTransportLangTab('ar') const emptyMap: Record = { ar: '', fa: '', en: '', ru: '', id: '' } setTransportGuideNotes(emptyMap) setTransportAirplaneDetailsMap(emptyMap) setTransportTrainDetailsMap(emptyMap) setTransportRoadDetailsMap(emptyMap) setTransportFormFromCity(allOrigins.length > 0 ? allOrigins[0].name : 'مشهد') setTransportFormFromCountry(allOrigins.length > 0 ? allOrigins[0].country : 'ایران') setTransportFormToCity(destination?.name || (allDestinations.length > 0 ? allDestinations[0].name : 'کربلای معلی')) setTransportFormToCountry(destination?.country || (allDestinations.length > 0 ? allDestinations[0].country : 'عراق')) setTransportFormHasAirplane(true) setTransportFormHasTrain(false) setTransportFormHasRoad(false) setTransportFormIsActive(true) setIsTransportFormOpen(true) } const handleOpenEditTransport = (rel: CustomTripTransportRelationItem) => { setEditingTransport(rel) setTransportLangTab('ar') const newGuides: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newAirplanes: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newTrains: Record = { ar: '', fa: '', en: '', ru: '', id: '' } const newRoads: Record = { ar: '', fa: '', en: '', ru: '', id: '' } if (rel.translations) { for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as ServiceLanguageCode[]) { if (rel.translations[lang]?.guideNote) newGuides[lang] = rel.translations[lang].guideNote! if (rel.translations[lang]?.airplaneDetails) newAirplanes[lang] = rel.translations[lang].airplaneDetails! if (rel.translations[lang]?.trainDetails) newTrains[lang] = rel.translations[lang].trainDetails! if (rel.translations[lang]?.roadDetails) newRoads[lang] = rel.translations[lang].roadDetails! } } // Fallbacks: prioritize Arabic first if (!newGuides.ar && rel.guideNote) newGuides.ar = rel.guideNote if (!newGuides.fa && rel.guideNote) newGuides.fa = rel.guideNote if (!newAirplanes.ar && rel.airplaneDetails) newAirplanes.ar = rel.airplaneDetails if (!newAirplanes.fa && rel.airplaneDetails) newAirplanes.fa = rel.airplaneDetails if (!newTrains.ar && rel.trainDetails) newTrains.ar = rel.trainDetails if (!newTrains.fa && rel.trainDetails) newTrains.fa = rel.trainDetails if (!newRoads.ar && rel.roadDetails) newRoads.ar = rel.roadDetails if (!newRoads.fa && rel.roadDetails) newRoads.fa = rel.roadDetails setTransportGuideNotes(newGuides) setTransportAirplaneDetailsMap(newAirplanes) setTransportTrainDetailsMap(newTrains) setTransportRoadDetailsMap(newRoads) setTransportFormFromCity(rel.fromCity) setTransportFormFromCountry(rel.fromCountry) setTransportFormToCity(rel.toCity || destination?.name || '') setTransportFormToCountry(rel.toCountry || destination?.country || '') setTransportFormHasAirplane(rel.hasAirplane) setTransportFormHasTrain(rel.hasTrain) setTransportFormHasRoad(rel.hasRoad) setTransportFormIsActive(rel.isActive !== false) setIsTransportFormOpen(true) } const handleSaveTransport = async (e: React.FormEvent) => { e.preventDefault() if (!destination || !transportFormFromCity.trim()) return const primaryAirplane = transportAirplaneDetailsMap[transportLangTab]?.trim() || transportAirplaneDetailsMap.ar?.trim() || transportAirplaneDetailsMap.fa?.trim() || undefined const primaryTrain = transportTrainDetailsMap[transportLangTab]?.trim() || transportTrainDetailsMap.ar?.trim() || transportTrainDetailsMap.fa?.trim() || undefined const primaryRoad = transportRoadDetailsMap[transportLangTab]?.trim() || transportRoadDetailsMap.ar?.trim() || transportRoadDetailsMap.fa?.trim() || undefined const primaryGuide = transportGuideNotes[transportLangTab]?.trim() || transportGuideNotes.ar?.trim() || transportGuideNotes.fa?.trim() || undefined const translations: Record< string, { guideNote?: string; airplaneDetails?: string; trainDetails?: string; roadDetails?: string } > = {} for (const lang of ['ar', 'fa', 'en', 'ru', 'id'] as ServiceLanguageCode[]) { translations[lang] = { guideNote: transportGuideNotes[lang]?.trim() || primaryGuide, airplaneDetails: transportAirplaneDetailsMap[lang]?.trim() || primaryAirplane, trainDetails: transportTrainDetailsMap[lang]?.trim() || primaryTrain, roadDetails: transportRoadDetailsMap[lang]?.trim() || primaryRoad, } } const targetToCity = transportFormToCity.trim() || destination.name const targetToCountry = transportFormToCountry.trim() || destination.country const item: CustomTripTransportRelationItem = { id: editingTransport ? editingTransport.id : `rel_${Date.now()}`, fromCity: transportFormFromCity.trim(), fromCountry: transportFormFromCountry.trim() || 'نامشخص', toCity: targetToCity, toCountry: targetToCountry, hasAirplane: transportFormHasAirplane, airplaneDetails: primaryAirplane, hasTrain: transportFormHasTrain, trainDetails: primaryTrain, hasRoad: transportFormHasRoad, roadDetails: primaryRoad, guideNote: primaryGuide, isActive: transportFormIsActive, translations, } await saveCustomTripTransportRelation(item) setAllRelations((prev) => { const exists = prev.some((r) => r.id === item.id) return exists ? prev.map((r) => (r.id === item.id ? item : r)) : [item, ...prev] }) setIsTransportFormOpen(false) } const handleConfirmDeleteTransport = async () => { if (!transportToDelete) return await deleteCustomTripTransportRelation(transportToDelete.id) setAllRelations((prev) => prev.filter((r) => r.id !== transportToDelete.id)) setTransportToDelete(null) } if (isLoading) { return (
) } if (!destination) { return (

شهر مقصد مورد نظر یافت نشد!

) } return (
{/* Top Breadcrumb Navigation */}
شهرهای مقصد / {destFlag || destination.flag || '📍'} {destName || destination.name}
{destIsActive ? 'مقصد فعال' : 'مقصد غیرفعال'}
{/* Tabs Navigation Header (Segmented Control) */}
{/* Tab 1: City Info */} {/* Tab 2: Hotels */} {/* Tab 3: Transportation */}
{/* ========================================================================= */} {/* TAB 1: DESTINATION INFO (شناسنامه و مشخصات مقصد) */} {/* ========================================================================= */} {currentTab === 'info' && (
{/* کارت اصلی شناسنامه شهر */}
{/* هدر: نام شهر، وضعیت و دکمه‌های اکشن */}
{destination.image || destImage ? (
{destination.name} { (e.currentTarget as HTMLElement).style.display = 'none' }} />
) : ( {destFlag || destination.flag || '📍'} )}

{destination.name}

{destination.nameEn && ( ({destination.nameEn}) )} {destIsActive ? 'شهر فعال در سامانه' : 'شهر غیرفعال'}

شناسنامه شهر، اماکن متبرکه، هتل‌های مرتبط و ترجمه‌های بین‌المللی مقصد

{/* دکمه‌های عملیات */}
{/* اعلان موفقیت ذخیره در صورت وجود */} {isDestSavedSuccess && (
مشخصات شهر مقصد با موفقیت در سامانه ذخیره و به‌روزرسانی شد.
)} {/* کارت‌های آماری سریع */}
کشور مقصد {destFlag || destination.flag || '📍'} {destCountry || destination.country}
handleTabChange('hotels')} className="p-3.5 rounded-xl bg-muted/30 border border-border/60 cursor-pointer hover:border-primary/50 transition-colors" title="مشاهده هتل‌ها" > هتل‌های فعال {toFa(destHotels.length)} هتل
handleTabChange('transports')} className="p-3.5 rounded-xl bg-muted/30 border border-border/60 cursor-pointer hover:border-primary/50 transition-colors" title="مشاهده مسیرهای حمل‌ونقل" > مسیرهای متصل {toFa(destRelations.length)} مسیر
وضعیت در سامانه
{destIsActive ? 'فعال' : 'غیرفعال'}
{/* ماژول نمایش مشخصات چندزبانه با ServiceTranslation در حالت Readonly */}
setActiveLangTab(lang)} completedLangs={{ ar: Boolean(names.ar?.trim()), fa: Boolean(names.fa?.trim()), en: Boolean(names.en?.trim()), ru: Boolean(names.ru?.trim()), id: Boolean(names.id?.trim()), }} primaryLang="ar" title="شناسنامه و متون چندزبانه مقصد" variant="pills" />
{/* محتوای زبان انتخاب‌شده */}
نام شهر مقصد ({currentLang.name}):
{names[activeLangTab] || destination.name || '---'}
{holyPlacesMap[activeLangTab] && (
اماکن مقدسه و زیارتگاه‌ها ({currentLang.name}):
{holyPlacesMap[activeLangTab] .split(/[،,]/) .map((p) => p.trim()) .filter(Boolean) .map((place, idx) => ( 🕌 {place} ))}
)}
توضیحات و راهنمای شهر ({currentLang.name}):
{descriptionsMap[activeLangTab] || destination.description || 'توضیحاتی برای این زبان ثبت نشده است.'}
{/* ========================================================================= */} {/* MODAL: EDIT DESTINATION INFO (طبق مدل popup.tsx / dialog.tsx) */} {/* ========================================================================= */} {/* هدر ثابت مدال */}
ویرایش اطلاعات و شناسنامه شهر {destination.name} ویرایش مشخصات اصلی، موقعیت کشور و توضیحات چندزبانه مقصد
{/* فرم: بدنه اسکرول‌شونده و فوتر فیکس‌شده */}
{/* تنظیمات رسانه‌ای، تصویر و پیوند به وبلاگ */}
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" />
{/* تنظیمات کلی شهر مقصد (کشور و وضعیت) */}
{/* انتخاب کشور مقصد */}
{/* وضعیت شهر در سامانه */}
{destIsActive ? 'شهر فعال' : 'شهر غیرفعال'}
{/* ماژول چندزبانه ترجمه */}
setActiveLangTab(lang)} completedLangs={{ ar: Boolean(names.ar?.trim()), fa: Boolean(names.fa?.trim()), en: Boolean(names.en?.trim()), ru: Boolean(names.ru?.trim()), id: Boolean(names.id?.trim()), }} primaryLang="ar" title="اطلاعات متنی و چندزبانه مقصد" required variant="pills" />
{/* فیلدهای ترجمه‌پذیر متناسب با زبان فعال */}
{currentLang.dir.toUpperCase()}
setNames((prev) => ({ ...prev, [activeLangTab]: e.target.value })) } dir={currentLang.dir} placeholder={`نام شهر به زبان ${currentLang.name}...`} className="h-10 rounded-xl text-xs bg-card border-border/70 focus-visible:border-primary focus-visible:ring-1 focus-visible:ring-primary shadow-2xs" required={activeLangTab === 'fa' || activeLangTab === 'ar'} />
setHolyPlacesMap((prev) => ({ ...prev, [activeLangTab]: e.target.value })) } dir={currentLang.dir} placeholder={`اماکن متبرکه به زبان ${currentLang.name}...`} className="h-10 rounded-xl text-xs bg-card border-border/70 focus-visible:border-primary focus-visible:ring-1 focus-visible:ring-primary shadow-2xs" />