You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1616 lines
76 KiB

import React, { useState, useEffect, useMemo } from 'react'
import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom'
import { Input } from '@/components/ui/input'
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 { Ic } from '@/icons'
import { toFa } from '@/lib/utils'
import { useTranslation } from '@/i18n'
import { Shimmer } from '@/components/shared/shimmer_frontend'
import type {
CustomTripDestinationItem,
CustomTripHotelItem,
CustomTripTransportRelationItem,
CustomTripOriginItem,
} from './types'
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<CustomTripDestinationItem | null>(null)
const [allOrigins, setAllOrigins] = useState<CustomTripOriginItem[]>([])
const [allHotels, setAllHotels] = useState<CustomTripHotelItem[]>([])
const [allRelations, setAllRelations] = useState<CustomTripTransportRelationItem[]>([])
// --- TAB 1: DESTINATION INFO FORM STATE ---
const [destName, setDestName] = useState('')
const [destNameEn, setDestNameEn] = useState('')
const [destCountry, setDestCountry] = useState('عراق')
const [destFlag, setDestFlag] = useState('🇮🇶')
const [destDescription, setDestDescription] = useState('')
const [destHolyPlaces, setDestHolyPlaces] = useState('')
const [destIsActive, setDestIsActive] = useState(true)
const [isDestSavedSuccess, setIsDestSavedSuccess] = useState(false)
const [isDestDeleteOpen, setIsDestDeleteOpen] = useState(false)
// --- 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<CustomTripHotelItem | null>(null)
const [hotelToDelete, setHotelToDelete] = useState<CustomTripHotelItem | null>(null)
// Hotel form fields
const [hotelFormName, setHotelFormName] = useState('')
const [hotelFormStars, setHotelFormStars] = useState<number>(5)
const [hotelFormDistance, setHotelFormDistance] = useState('')
const [hotelFormScore, setHotelFormScore] = useState<number>(4.8)
const [hotelFormDescription, setHotelFormDescription] = useState('')
const [hotelFormGuideNote, setHotelFormGuideNote] = useState('')
const [hotelFormAmenities, setHotelFormAmenities] = useState<string[]>([])
const [hotelFormCustomAmenity, setHotelFormCustomAmenity] = useState('')
const [hotelFormPriceAdult, setHotelFormPriceAdult] = useState<number>(100)
const [hotelFormPriceChild, setHotelFormPriceChild] = useState<number>(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<CustomTripTransportRelationItem | null>(null)
const [transportToDelete, setTransportToDelete] = useState<CustomTripTransportRelationItem | null>(null)
// Transport form fields
const [transportFormFromCity, setTransportFormFromCity] = useState('')
const [transportFormFromCountry, setTransportFormFromCountry] = useState('')
const [transportFormHasAirplane, setTransportFormHasAirplane] = useState(true)
const [transportFormAirplaneDetails, setTransportFormAirplaneDetails] = useState('')
const [transportFormHasTrain, setTransportFormHasTrain] = useState(false)
const [transportFormTrainDetails, setTransportFormTrainDetails] = useState('')
const [transportFormHasRoad, setTransportFormHasRoad] = useState(false)
const [transportFormRoadDetails, setTransportFormRoadDetails] = useState('')
const [transportFormGuideNote, setTransportFormGuideNote] = useState('')
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) {
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) {
setDestination(target)
// Init Tab 1 Form
setDestName(target.name)
setDestNameEn(target.nameEn || '')
setDestCountry(target.country)
setDestFlag(target.flag || '📍')
setDestDescription(target.description || '')
setDestHolyPlaces((target.holyPlaces || []).join('، '))
setDestIsActive(target.isActive)
} else if (dests.length > 0) {
const fallback = dests[0]
setDestination(fallback)
setDestName(fallback.name)
setDestNameEn(fallback.nameEn || '')
setDestCountry(fallback.country)
setDestFlag(fallback.flag || '📍')
setDestDescription(fallback.description || '')
setDestHolyPlaces((fallback.holyPlaces || []).join('، '))
setDestIsActive(fallback.isActive)
}
}
} 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 || !destName.trim()) return
const places = destHolyPlaces
.split(/[،,]/)
.map((s) => s.trim())
.filter(Boolean)
const updated: CustomTripDestinationItem = {
...destination,
name: destName.trim(),
nameEn: destNameEn.trim() || undefined,
country: destCountry.trim(),
flag: destFlag,
description: destDescription.trim() || undefined,
holyPlaces: places.length ? places : undefined,
isActive: destIsActive,
}
await saveCustomTripDestination(updated)
setDestination(updated)
setIsDestSavedSuccess(true)
setTimeout(() => setIsDestSavedSuccess(false), 3000)
}
const handleDeleteDestinationConfirm = async () => {
if (!destination) return
await deleteCustomTripDestination(destination.id)
navigate('/custom-trip/destinations')
}
// --- TAB 2: HOTEL HANDLERS ---
const handleOpenAddHotel = () => {
setEditingHotel(null)
setHotelFormName('')
setHotelFormStars(5)
setHotelFormDistance('')
setHotelFormScore(4.8)
setHotelFormDescription('')
setHotelFormGuideNote('')
setHotelFormAmenities(['اینترنت وای‌فای رایگان', 'رستوران ایرانی و بوفه باز'])
setHotelFormCustomAmenity('')
setHotelFormPriceAdult(110)
setHotelFormPriceChild(55)
setHotelFormIsActive(true)
setIsHotelFormOpen(true)
}
const handleOpenEditHotel = (hotel: CustomTripHotelItem) => {
setEditingHotel(hotel)
setHotelFormName(hotel.name)
setHotelFormStars(hotel.stars)
setHotelFormDistance(hotel.distanceToShrine || '')
setHotelFormScore(hotel.score || 4.8)
setHotelFormDescription(hotel.description || '')
setHotelFormGuideNote(hotel.guideNote || '')
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 || !hotelFormName.trim()) return
const item: CustomTripHotelItem = {
id: editingHotel ? editingHotel.id : `hotel_${Date.now()}`,
name: hotelFormName.trim(),
cityName: destination.name,
stars: hotelFormStars,
score: hotelFormScore,
distanceToShrine: hotelFormDistance.trim() || 'فاصله استاندارد تا حرم مطهر',
amenities: hotelFormAmenities,
description: hotelFormDescription.trim() || 'هتل درجه یک و مجهز جهت اقامت زائرین گرامی.',
guideNote: hotelFormGuideNote.trim() || undefined,
pricePerDayAdult: hotelFormPriceAdult,
pricePerDayChild: hotelFormPriceChild,
isActive: hotelFormIsActive,
}
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 handleOpenAddTransport = () => {
setEditingTransport(null)
setTransportFormFromCity(allOrigins.length > 0 ? allOrigins[0].name : 'تهران')
setTransportFormFromCountry(allOrigins.length > 0 ? allOrigins[0].country : 'ایران')
setTransportFormHasAirplane(true)
setTransportFormAirplaneDetails(`پرواز مستقیم به فرودگاه نجف / بغداد`)
setTransportFormHasTrain(false)
setTransportFormTrainDetails('')
setTransportFormHasRoad(false)
setTransportFormRoadDetails('')
setTransportFormGuideNote(`معمولاً پروازهای این مسیر به صورت روزانه یا چارتر هفتگی انجام می‌شود.`)
setTransportFormIsActive(true)
setIsTransportFormOpen(true)
}
const handleOpenEditTransport = (rel: CustomTripTransportRelationItem) => {
setEditingTransport(rel)
setTransportFormFromCity(rel.fromCity)
setTransportFormFromCountry(rel.fromCountry)
setTransportFormHasAirplane(rel.hasAirplane)
setTransportFormAirplaneDetails(rel.airplaneDetails || '')
setTransportFormHasTrain(rel.hasTrain)
setTransportFormTrainDetails(rel.trainDetails || '')
setTransportFormHasRoad(rel.hasRoad)
setTransportFormRoadDetails(rel.roadDetails || '')
setTransportFormGuideNote(rel.guideNote || '')
setTransportFormIsActive(rel.isActive)
setIsTransportFormOpen(true)
}
const handleSaveTransport = async (e: React.FormEvent) => {
e.preventDefault()
if (!destination || !transportFormFromCity.trim()) return
const item: CustomTripTransportRelationItem = {
id: editingTransport ? editingTransport.id : `rel_${Date.now()}`,
fromCity: transportFormFromCity.trim(),
fromCountry: transportFormFromCountry.trim() || 'نامشخص',
toCity: destination.name,
toCountry: destination.country,
hasAirplane: transportFormHasAirplane,
airplaneDetails: transportFormHasAirplane ? transportFormAirplaneDetails.trim() || undefined : undefined,
hasTrain: transportFormHasTrain,
trainDetails: transportFormHasTrain ? transportFormTrainDetails.trim() || undefined : undefined,
hasRoad: transportFormHasRoad,
roadDetails: transportFormHasRoad ? transportFormRoadDetails.trim() || undefined : undefined,
guideNote: transportFormGuideNote.trim() || undefined,
isActive: transportFormIsActive,
}
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 (
<div className="space-y-4">
<Shimmer className="h-10 w-48 rounded-xl" />
<Shimmer className="h-44 w-full rounded-2xl" />
<Shimmer className="h-72 w-full rounded-2xl" />
</div>
)
}
if (!destination) {
return (
<div className="p-8 text-center rounded-2xl bg-card border border-border/70 space-y-4">
<Ic name="alert" className="size-10 text-muted-foreground mx-auto" />
<h2 className="text-base font-bold text-foreground">شهر مقصد مورد نظر یافت نشد!</h2>
<Button onClick={() => navigate('/custom-trip/destinations')} variant="outline" className="rounded-xl">
بازگشت به فهرست شهرهای مقصد
</Button>
</div>
)
}
return (
<div className="space-y-5">
{/* Top Breadcrumb Navigation */}
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<div className="flex items-center gap-2 text-xs sm:text-sm text-muted-foreground">
<Link
to="/custom-trip/destinations"
className="flex items-center gap-1.5 hover:text-foreground font-semibold transition-colors"
>
<Ic name="arrowRight" className="size-4" />
<span>شهرهای مقصد</span>
</Link>
<span className="text-border">/</span>
<span className="font-bold text-foreground flex items-center gap-1.5">
<span>{destination.flag || '📍'}</span>
<span>{destination.name}</span>
</span>
</div>
<div className="flex items-center gap-2">
<span
className={`inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 rounded-xl border ${
destination.isActive
? 'bg-emerald-500/10 text-emerald-600 border-emerald-500/30'
: 'bg-muted text-muted-foreground border-border/60'
}`}
>
<span
className={`size-2 rounded-full ${
destination.isActive ? 'bg-emerald-500 animate-pulse' : 'bg-muted-foreground'
}`}
/>
<span>{destination.isActive ? 'مقصد فعال و قابل رزرو' : 'مقصد غیرفعال'}</span>
</span>
</div>
</div>
{/* Tabs Navigation Header: 1. Info & Description | 2. Hotels | 3. Transportation */}
<div className="flex items-center gap-2 p-1.5 rounded-2xl bg-muted/60 border border-border/70 overflow-x-auto">
{/* Tab 1: City Info & Custom Trip Description */}
<button
type="button"
onClick={() => handleTabChange('info')}
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-semibold transition-all cursor-pointer whitespace-nowrap ${
currentTab === 'info'
? 'bg-card text-foreground shadow-xs border border-border/70 font-bold'
: 'text-muted-foreground hover:text-foreground hover:bg-card/50'
}`}
>
<Ic name="mapPin" className={`size-4 ${currentTab === 'info' ? 'text-primary' : ''}`} />
<span>مشخصات و توضیحات مقصد (Description)</span>
</button>
{/* Tab 2: Hotels */}
<button
type="button"
onClick={() => handleTabChange('hotels')}
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-semibold transition-all cursor-pointer whitespace-nowrap ${
currentTab === 'hotels'
? 'bg-card text-foreground shadow-xs border border-border/70 font-bold'
: 'text-muted-foreground hover:text-foreground hover:bg-card/50'
}`}
>
<Ic name="bed" className={`size-4 ${currentTab === 'hotels' ? 'text-primary' : ''}`} />
<span>هتل‌ها و اقامتگاه‌های مقصد</span>
<span className="px-2 py-0.5 rounded-full text-[11px] font-mono font-bold bg-muted text-muted-foreground">
{toFa(destHotels.length)}
</span>
</button>
{/* Tab 3: Transportation */}
<button
type="button"
onClick={() => handleTabChange('transports')}
className={`flex items-center gap-2 px-4 py-2.5 rounded-xl text-xs sm:text-sm font-semibold transition-all cursor-pointer whitespace-nowrap ${
currentTab === 'transports'
? 'bg-card text-foreground shadow-xs border border-border/70 font-bold'
: 'text-muted-foreground hover:text-foreground hover:bg-card/50'
}`}
>
<Ic name="route" className={`size-4 ${currentTab === 'transports' ? 'text-primary' : ''}`} />
<span>روش‌ها و مسیرهای حمل‌ونقل (Transportation)</span>
<span className="px-2 py-0.5 rounded-full text-[11px] font-mono font-bold bg-muted text-muted-foreground">
{toFa(destRelations.length)}
</span>
</button>
</div>
{/* ========================================================================= */}
{/* TAB 1: DESTINATION INFO & DESCRIPTION (صفحه اول: مشخصات و توضیحات شهر) */}
{/* ========================================================================= */}
{currentTab === 'info' && (
<div className="space-y-4">
<form onSubmit={handleSaveDestinationInfo} className="space-y-4">
<div className="rounded-2xl bg-card border border-border/70 p-5 sm:p-6 shadow-xs space-y-5">
<div className="flex items-center justify-between border-b border-border/60 pb-4">
<div>
<h2 className="text-base sm:text-lg font-black text-foreground flex items-center gap-2">
<span className="text-2xl select-none">{destFlag || '📍'}</span>
<span>اطلاعات و شناسنامه شهر {destination.name}</span>
</h2>
<p className="text-xs text-muted-foreground mt-0.5">
ویرایش مشخصات اصلی، موقعیت کشور، اماکن مقدسه و توضیحات شهر برای ماژول سفر اختصاصی (Custom Trip).
</p>
</div>
{isDestSavedSuccess && (
<div className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-emerald-500/10 border border-emerald-500/30 text-emerald-600 text-xs font-bold animate-fadeIn">
<Ic name="checkCircle" className="size-4" />
<span>تغییرات با موفقیت ذخیره شد</span>
</div>
)}
</div>
{/* Form Grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
{/* 1. Country */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-foreground block">
کشور مقصد:
</label>
<select
value={destCountry}
onChange={(e) => {
const cName = e.target.value
setDestCountry(cName)
const preset = PRESET_DEST_COUNTRIES.find((p) => p.name === cName)
if (preset) setDestFlag(preset.flag)
}}
className="w-full h-11 px-3 text-xs sm:text-sm rounded-xl bg-background border border-border/70 text-foreground font-medium focus:ring-1 focus:ring-primary focus:outline-none"
>
{PRESET_DEST_COUNTRIES.map((c) => (
<option key={c.name} value={c.name}>
{c.flag} {c.name}
</option>
))}
</select>
</div>
{/* 2. City Name FA */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-foreground block">
نام شهر مقصد (فارسی):
</label>
<Input
value={destName}
onChange={(e) => setDestName(e.target.value)}
placeholder="مثال: کربلای معلی، نجف اشرف، مشهد مقدس..."
className="h-11 rounded-xl text-xs sm:text-sm"
required
/>
</div>
{/* 3. City Name EN */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-foreground block">
نام لاتین شهر (انگلیسی / English):
</label>
<Input
value={destNameEn}
onChange={(e) => setDestNameEn(e.target.value)}
placeholder="e.g. Karbala, Najaf, Mashhad..."
className="h-11 rounded-xl text-xs sm:text-sm font-mono"
/>
</div>
{/* 4. Active Status Toggle */}
<div className="space-y-1.5">
<label className="text-xs font-bold text-foreground block">
وضعیت شهر در سامانه:
</label>
<button
type="button"
onClick={() => setDestIsActive(!destIsActive)}
className={`w-full h-11 px-4 rounded-xl text-xs font-bold flex items-center justify-between border transition-all cursor-pointer ${
destIsActive
? 'bg-emerald-500/10 text-emerald-600 border-emerald-500/30 hover:bg-emerald-500/20'
: 'bg-muted text-muted-foreground border-border/70 hover:bg-muted/80'
}`}
>
<span>{destIsActive ? 'شهر فعال و در دسترس در رزرو' : 'شهر غیرفعال'}</span>
<span
className={`size-2.5 rounded-full ${
destIsActive ? 'bg-emerald-500 animate-pulse' : 'bg-muted-foreground'
}`}
/>
</button>
</div>
</div>
{/* 5. Holy Places */}
<div className="space-y-1.5 pt-1">
<label className="text-xs font-bold text-foreground block">
اماکن مقدسه و زیارتگاه‌های شاخص (با ویرگول جدا کنید):
</label>
<Input
value={destHolyPlaces}
onChange={(e) => setDestHolyPlaces(e.target.value)}
placeholder="مثال: حرم مطهر امام حسین (ع)، حرم حضرت ابوالفضل العباس (ع)، تل زینبیه، مقام علی‌اکبر (ع)..."
className="h-11 rounded-xl text-xs sm:text-sm"
/>
</div>
{/* 6. City Description for Custom Trip (توضیح برای آن شهر که در Custom Trip در توضیحات شهر قرار داده می‌شود) */}
<div className="space-y-1.5 pt-1">
<div className="flex items-center justify-between">
<label className="text-xs font-bold text-foreground block">
توضیحات و راهنمای شهر در سفر اختصاصی (Description):
</label>
<span className="text-[11px] text-muted-foreground">
این متن در اپلیکیشن هنگام انتخاب این مقصد به زائر نمایش داده می‌شود.
</span>
</div>
<textarea
value={destDescription}
onChange={(e) => setDestDescription(e.target.value)}
rows={4}
placeholder="شرح کوتاه اماکن مقدسه، شرایط سفر، فصول مناسب زیارت و ویژگی‌های این شهر زیارتی..."
className="w-full p-3.5 text-xs sm:text-sm rounded-xl bg-background border border-border/70 text-foreground leading-relaxed focus:ring-1 focus:ring-primary focus:outline-none resize-y"
/>
</div>
{/* Save Footer Bar */}
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 pt-3 border-t border-border/60">
<Button
type="submit"
className="h-10 px-6 rounded-xl font-bold text-xs sm:text-sm bg-primary text-white hover:bg-primary/90 shadow-xs"
>
<Ic name="check" className="size-4" />
<span>ذخیره و ثبت تغییرات شهر مقصد</span>
</Button>
<Button
type="button"
variant="ghost"
onClick={() => setIsDestDeleteOpen(true)}
className="h-10 px-4 rounded-xl text-xs font-bold text-rose-600 hover:bg-rose-500/10 hover:text-rose-700"
>
<Ic name="trash" className="size-4" />
<span>حذف این شهر مقصد</span>
</Button>
</div>
</div>
</form>
{/* Delete Destination Dialog */}
<DeleteConfirmDialog
open={isDestDeleteOpen}
onOpenChange={setIsDestDeleteOpen}
title={`حذف شهر مقصد ${destination.name}`}
description={`آیا از حذف کامل شهر مقصد «${destination.name}» اطمینان دارید؟ تمام هتل‌ها و مسیرهای مرتبط با آن نیز حذف خواهند شد.`}
onConfirm={handleDeleteDestinationConfirm}
/>
</div>
)}
{/* ========================================================================= */}
{/* TAB 2: HOTELS (هتل‌ها و اقامتگاه‌های مقصد) */}
{/* ========================================================================= */}
{currentTab === 'hotels' && (
<div className="space-y-4">
{/* Action & Filter Bar */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-3 p-3.5 rounded-2xl bg-card border border-border/70 shadow-xs">
<div className="flex items-center gap-2 flex-1 flex-wrap">
<div className="relative flex-1 min-w-[220px]">
<Ic
name="search"
className="absolute start-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground"
/>
<Input
value={hotelSearch}
onChange={(e) => setHotelSearch(e.target.value)}
placeholder="جستجوی نام هتل، فاصله تا حرم، راهنما یا امکانات..."
className="ps-9 h-10 rounded-xl bg-background text-xs sm:text-sm"
/>
</div>
{/* Stars Filter */}
<div className="flex items-center gap-1 p-1 bg-muted/60 rounded-xl border border-border/50 text-xs">
{(['all', '5', '4', '3'] as const).map((s) => (
<button
key={s}
type="button"
onClick={() => setHotelStarsFilter(s)}
className={`px-3 py-1.5 rounded-lg font-medium transition-all cursor-pointer ${
hotelStarsFilter === s
? 'bg-card text-foreground shadow-2xs font-bold'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{s === 'all' ? 'همه ستاره‌ها' : `${toFa(s)} ستاره`}
</button>
))}
</div>
</div>
<div className="flex items-center gap-2 justify-end">
{/* View Mode Toggle */}
<div className="flex items-center p-1 bg-muted/60 rounded-xl border border-border/50">
<button
type="button"
onClick={() => setHotelViewMode('cards')}
className={`p-1.5 rounded-lg transition-all cursor-pointer ${
hotelViewMode === 'cards'
? 'bg-card text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground'
}`}
title="نمایش کارتی"
>
<Ic name="grid" className="size-4" />
</button>
<button
type="button"
onClick={() => setHotelViewMode('table')}
className={`p-1.5 rounded-lg transition-all cursor-pointer ${
hotelViewMode === 'table'
? 'bg-card text-foreground shadow-2xs'
: 'text-muted-foreground hover:text-foreground'
}`}
title="نمایش جدولی"
>
<Ic name="list" className="size-4" />
</button>
</div>
<Button
onClick={handleOpenAddHotel}
className="rounded-xl h-10 text-xs sm:text-sm font-bold gap-1.5 bg-primary text-white hover:bg-primary/90 shadow-xs"
>
<Ic name="plus" className="size-4" />
<span>افزودن هتل به {destination.name}</span>
</Button>
</div>
</div>
{/* Hotels Content */}
{filteredHotels.length === 0 ? (
<div className="p-8 text-center rounded-2xl bg-card border border-border/70 space-y-3">
<Ic name="bed" className="size-8 text-muted-foreground mx-auto" />
<p className="text-sm font-medium text-foreground">
هیچ هتلی برای شهر {destination.name} یافت نشد.
</p>
<Button onClick={handleOpenAddHotel} variant="outline" className="rounded-xl text-xs">
+ افزودن اولین هتل برای این شهر
</Button>
</div>
) : hotelViewMode === 'cards' ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{filteredHotels.map((hotel) => (
<div
key={hotel.id}
className="rounded-2xl bg-card border border-border/70 p-4 sm:p-5 hover:border-primary/40 transition-all space-y-3 shadow-2xs flex flex-col justify-between"
>
<div className="space-y-3">
{/* Header */}
<div className="flex items-start justify-between gap-3">
<div>
<div className="flex items-center gap-1.5">
<h3 className="text-base font-bold text-foreground">{hotel.name}</h3>
<div className="flex items-center text-amber-500">
{Array.from({ length: hotel.stars }).map((_, i) => (
<span key={i} className="text-xs">★</span>
))}
</div>
</div>
<div className="text-xs text-muted-foreground mt-0.5 flex items-center gap-1">
<Ic name="mapPin" className="size-3 text-primary" />
<span>{hotel.cityName}</span>
<span className="text-border">•</span>
<span className="font-mono text-emerald-600 dark:text-emerald-400 font-bold">
امتیاز {toFa(hotel.score)} از ۵
</span>
</div>
</div>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
onClick={() => handleOpenEditHotel(hotel)}
className="size-8 p-0 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground"
title="ویرایش هتل"
>
<Ic name="pencil" className="size-3.5" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setHotelToDelete(hotel)}
className="size-8 p-0 rounded-lg hover:bg-rose-500/10 text-muted-foreground hover:text-rose-600"
title="حذف هتل"
>
<Ic name="trash" className="size-3.5" />
</Button>
</div>
</div>
{/* Distance to shrine */}
<div className="p-2.5 rounded-xl bg-muted/50 border border-border/50 flex items-center gap-2 text-xs">
<span className="text-base select-none">🕌</span>
<div>
<span className="font-semibold text-foreground">نزدیکی به حرم: </span>
<span className="text-muted-foreground">{hotel.distanceToShrine}</span>
</div>
</div>
{/* Hotel Description (توضیحات هتل) */}
{hotel.description && (
<p className="text-xs text-muted-foreground leading-relaxed line-clamp-2">
{hotel.description}
</p>
)}
{/* Guide Note (فیلد توضیحی راهنما: جدا از توضیحات هتل) */}
{hotel.guideNote && (
<div className="p-2.5 rounded-xl bg-amber-500/10 border border-amber-500/20 text-xs space-y-1">
<div className="flex items-center gap-1.5 font-bold text-amber-700 dark:text-amber-400">
<Ic name="sparkles" className="size-3.5" />
<span>راهنما و تجربه اقامت در این هتل:</span>
</div>
<p className="text-muted-foreground leading-relaxed">
{hotel.guideNote}
</p>
</div>
)}
{/* Amenities */}
{hotel.amenities && hotel.amenities.length > 0 && (
<div className="flex items-center gap-1.5 flex-wrap pt-1">
{hotel.amenities.map((amenity, aIdx) => (
<span
key={aIdx}
className="text-[11px] px-2 py-0.5 rounded-lg bg-card border border-border/70 text-muted-foreground font-medium"
>
{amenity}
</span>
))}
</div>
)}
</div>
{/* Pricing Footer */}
<div className="pt-3 border-t border-border/50 flex items-center justify-between text-xs">
<span className="text-muted-foreground">نرخ هر شب:</span>
<div className="flex items-center gap-3">
<div className="flex items-baseline gap-1">
<span className="text-muted-foreground text-[11px]">بزرگسال:</span>
<span className="font-mono font-bold text-foreground">
${toFa(hotel.pricePerDayAdult)}
</span>
</div>
<div className="flex items-baseline gap-1">
<span className="text-muted-foreground text-[11px]">کودک:</span>
<span className="font-mono font-bold text-foreground">
${toFa(hotel.pricePerDayChild)}
</span>
</div>
</div>
</div>
</div>
))}
</div>
) : (
/* Table View */
<div className="overflow-hidden rounded-2xl border border-border/70 bg-card shadow-xs">
<div className="overflow-x-auto">
<table className="w-full text-xs sm:text-sm text-start">
<thead className="bg-muted/40 text-muted-foreground border-b border-border/60 font-medium">
<tr>
<th className="px-4 py-3.5 text-start">نام هتل</th>
<th className="px-4 py-3.5 text-center">ستاره</th>
<th className="px-4 py-3.5 text-start">فاصله تا حرم مطهر</th>
<th className="px-4 py-3.5 text-start">توضیحات و راهنمای اقامت</th>
<th className="px-4 py-3.5 text-center">نرخ هر شب</th>
<th className="px-4 py-3.5 text-center">وضعیت</th>
<th className="px-4 py-3.5 text-center">عملیات</th>
</tr>
</thead>
<tbody className="divide-y divide-border/50">
{filteredHotels.map((hotel) => (
<tr key={hotel.id} className="hover:bg-muted/20 transition-colors">
<td className="px-4 py-3.5 font-bold text-foreground">
<div>{hotel.name}</div>
<span className="text-[11px] font-mono text-muted-foreground">
امتیاز {toFa(hotel.score)}/۵
</span>
</td>
<td className="px-4 py-3.5 text-center">
<span className="text-amber-500 font-bold">
{toFa(hotel.stars)} ★
</span>
</td>
<td className="px-4 py-3.5 text-xs text-muted-foreground max-w-xs">
{hotel.distanceToShrine}
</td>
<td className="px-4 py-3.5 text-xs max-w-sm space-y-1">
<div className="text-muted-foreground line-clamp-1">{hotel.description}</div>
{hotel.guideNote && (
<div className="text-[11px] text-amber-600 dark:text-amber-400 line-clamp-1 font-medium">
💡 راهنما: {hotel.guideNote}
</div>
)}
</td>
<td className="px-4 py-3.5 text-center font-mono">
<div className="font-bold text-foreground">${toFa(hotel.pricePerDayAdult)}</div>
<div className="text-[10px] text-muted-foreground">کودک: ${toFa(hotel.pricePerDayChild)}</div>
</td>
<td className="px-4 py-3.5 text-center">
<span
className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-semibold ${
hotel.isActive
? 'bg-emerald-500/10 text-emerald-600'
: 'bg-muted text-muted-foreground'
}`}
>
<span
className={`size-1.5 rounded-full ${
hotel.isActive ? 'bg-emerald-500' : 'bg-muted-foreground'
}`}
/>
{hotel.isActive ? 'فعال' : 'غیرفعال'}
</span>
</td>
<td className="px-4 py-3.5 text-center">
<div className="flex items-center justify-center gap-1">
<Button
variant="ghost"
size="sm"
onClick={() => handleOpenEditHotel(hotel)}
className="size-8 p-0 rounded-lg hover:bg-muted"
>
<Ic name="pencil" className="size-3.5" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setHotelToDelete(hotel)}
className="size-8 p-0 rounded-lg hover:bg-rose-500/10 text-rose-600"
>
<Ic name="trash" className="size-3.5" />
</Button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
{/* Delete Hotel Dialog */}
<DeleteConfirmDialog
open={!!hotelToDelete}
onOpenChange={(open) => !open && setHotelToDelete(null)}
title="حذف هتل"
description={`آیا از حذف هتل «${hotelToDelete?.name || ''}» اطمینان دارید؟`}
onConfirm={handleConfirmDeleteHotel}
/>
</div>
)}
{/* ========================================================================= */}
{/* TAB 3: TRANSPORTATION (روش‌ها و مسیرهای حمل‌ونقل) */}
{/* ========================================================================= */}
{currentTab === 'transports' && (
<div className="space-y-4">
{/* Action & Filter Bar */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-3 p-3.5 rounded-2xl bg-card border border-border/70 shadow-xs">
<div className="flex items-center gap-2 flex-1 flex-wrap">
<div className="relative flex-1 min-w-[220px]">
<Ic
name="search"
className="absolute start-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground"
/>
<Input
value={transportSearch}
onChange={(e) => setTransportSearch(e.target.value)}
placeholder="جستجوی شهر مبدأ، جزئیات خطوط یا راهنمای مسیر..."
className="ps-9 h-10 rounded-xl bg-background text-xs sm:text-sm"
/>
</div>
{/* Method Filter */}
<div className="flex items-center gap-1 p-1 bg-muted/60 rounded-xl border border-border/50 text-xs">
{(
[
{ id: 'all', label: 'همه روش‌ها' },
{ id: 'flight', label: '✈️ هوایی' },
{ id: 'train', label: '🚆 ریلی' },
{ id: 'road', label: '🚌 جاده‌ای' },
] as const
).map((m) => (
<button
key={m.id}
type="button"
onClick={() => setTransportMethodFilter(m.id)}
className={`px-3 py-1.5 rounded-lg font-medium transition-all cursor-pointer ${
transportMethodFilter === m.id
? 'bg-card text-foreground shadow-2xs font-bold'
: 'text-muted-foreground hover:text-foreground'
}`}
>
{m.label}
</button>
))}
</div>
</div>
<div className="flex items-center gap-2 justify-end">
<Button
onClick={handleOpenAddTransport}
className="rounded-xl h-10 text-xs sm:text-sm font-bold gap-1.5 bg-primary text-white hover:bg-primary/90 shadow-xs"
>
<Ic name="plus" className="size-4" />
<span>تعریف مسیر مبدأ به {destination.name}</span>
</Button>
</div>
</div>
{/* Transports Cards */}
{filteredRelations.length === 0 ? (
<div className="p-8 text-center rounded-2xl bg-card border border-border/70 space-y-3">
<Ic name="route" className="size-8 text-muted-foreground mx-auto" />
<p className="text-sm font-medium text-foreground">
هیچ مسیر حمل‌ونقلی به مقصد {destination.name} ثبت نشده است.
</p>
<Button onClick={handleOpenAddTransport} variant="outline" className="rounded-xl text-xs">
+ افزودن اولین مسیر ارتباطی به این مقصد
</Button>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{filteredRelations.map((rel) => (
<div
key={rel.id}
className="rounded-2xl bg-card border border-border/70 p-4 sm:p-5 hover:border-primary/40 transition-all space-y-3.5 shadow-2xs"
>
{/* Route Header */}
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
<div className="flex items-center gap-1.5 font-bold text-foreground text-sm sm:text-base">
<span>{rel.fromCity}</span>
<Ic name="arrowLeft" className="size-3.5 text-primary" />
<span className="text-primary">{destination.name}</span>
</div>
<span className="text-[11px] px-2 py-0.5 rounded-md bg-muted text-muted-foreground">
{rel.fromCountry}
</span>
</div>
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
onClick={() => handleOpenEditTransport(rel)}
className="size-8 p-0 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground"
title="ویرایش مسیر"
>
<Ic name="pencil" className="size-3.5" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => setTransportToDelete(rel)}
className="size-8 p-0 rounded-lg hover:bg-rose-500/10 text-muted-foreground hover:text-rose-600"
title="حذف مسیر"
>
<Ic name="trash" className="size-3.5" />
</Button>
</div>
</div>
{/* Guide Note for Route (فیلد توضیحی راهنمای مسیر) */}
{rel.guideNote && (
<div className="p-2.5 rounded-xl bg-sky-500/10 border border-sky-500/20 text-xs space-y-1">
<div className="flex items-center gap-1.5 font-bold text-sky-700 dark:text-sky-300">
<Ic name="route" className="size-3.5" />
<span>راهنما و شرایط سفر در این مسیر:</span>
</div>
<p className="text-muted-foreground leading-relaxed">
{rel.guideNote}
</p>
</div>
)}
{/* Transport Methods Status & Details */}
<div className="space-y-2 pt-1">
{/* Airplane */}
<div
className={`p-2.5 rounded-xl border transition-all ${
rel.hasAirplane
? 'bg-sky-500/5 border-sky-500/30'
: 'bg-muted/30 border-border/40 opacity-60'
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-base select-none">✈️</span>
<span className="text-xs font-bold text-foreground">پرواز هوایی</span>
{rel.hasAirplane && (
<span className="text-[10px] px-1.5 py-0.2 rounded bg-sky-500/10 text-sky-600 font-semibold">
فعال
</span>
)}
</div>
</div>
{rel.hasAirplane && rel.airplaneDetails && (
<p className="text-xs text-muted-foreground mt-1.5 ps-6">
{rel.airplaneDetails}
</p>
)}
</div>
{/* Train */}
<div
className={`p-2.5 rounded-xl border transition-all ${
rel.hasTrain
? 'bg-emerald-500/5 border-emerald-500/30'
: 'bg-muted/30 border-border/40 opacity-60'
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-base select-none">🚆</span>
<span className="text-xs font-bold text-foreground">قطار ریلی</span>
{rel.hasTrain && (
<span className="text-[10px] px-1.5 py-0.2 rounded bg-emerald-500/10 text-emerald-600 font-semibold">
فعال
</span>
)}
</div>
</div>
{rel.hasTrain && rel.trainDetails && (
<p className="text-xs text-muted-foreground mt-1.5 ps-6">
{rel.trainDetails}
</p>
)}
</div>
{/* Road */}
<div
className={`p-2.5 rounded-xl border transition-all ${
rel.hasRoad
? 'bg-amber-500/5 border-amber-500/30'
: 'bg-muted/30 border-border/40 opacity-60'
}`}
>
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-base select-none">🚌</span>
<span className="text-xs font-bold text-foreground">اتوبوس و ون VIP (جاده‌ای)</span>
{rel.hasRoad && (
<span className="text-[10px] px-1.5 py-0.2 rounded bg-amber-500/10 text-amber-600 font-semibold">
فعال
</span>
)}
</div>
</div>
{rel.hasRoad && rel.roadDetails && (
<p className="text-xs text-muted-foreground mt-1.5 ps-6">
{rel.roadDetails}
</p>
)}
</div>
</div>
</div>
))}
</div>
)}
{/* Delete Transport Dialog */}
<DeleteConfirmDialog
open={!!transportToDelete}
onOpenChange={(open) => !open && setTransportToDelete(null)}
title="حذف مسیر حمل‌ونقل"
description={`آیا از حذف مسیر «${transportToDelete?.fromCity} به ${destination.name}» مطمئن هستید؟`}
onConfirm={handleConfirmDeleteTransport}
/>
</div>
)}
{/* ========================================================================= */}
{/* MODAL: ADD / EDIT HOTEL */}
{/* ========================================================================= */}
<Dialog open={isHotelFormOpen} onOpenChange={setIsHotelFormOpen}>
<DialogContent className="max-w-xl rounded-2xl p-6 max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-base font-bold text-foreground flex items-center gap-2">
<Ic name="bed" className="size-5 text-primary" />
{editingHotel ? 'ویرایش مشخصات هتل' : `افزودن هتل جدید به ${destination.name}`}
</DialogTitle>
<DialogDescription className="text-xs text-muted-foreground">
اطلاعات، درجه ستاره، فاصله تا حرم، توضیحات هتل و فیلد راهنمای اقامت را ثبت کنید.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSaveHotel} className="space-y-4 my-2 text-xs sm:text-sm">
{/* Locked City Name */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">شهر مقصد:</label>
<div className="h-10 px-3 flex items-center rounded-xl bg-muted/70 border border-border/70 text-foreground font-bold">
{destination.flag} {destination.name} ({destination.country})
</div>
</div>
{/* Hotel Name */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
نام هتل (فارسی و انگلیسی):
</label>
<Input
value={hotelFormName}
onChange={(e) => setHotelFormName(e.target.value)}
placeholder="مثال: هتل بارون کربلا (Baron Hotel Karbala)"
className="h-10 rounded-xl"
required
/>
</div>
{/* Stars & Score */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
درجه هتل (تعداد ستاره):
</label>
<select
value={hotelFormStars}
onChange={(e) => setHotelFormStars(Number(e.target.value))}
className="w-full h-10 px-3 text-xs sm:text-sm rounded-xl bg-background border border-border/70 text-foreground font-medium focus:ring-1 focus:ring-primary focus:outline-none"
>
<option value={5}>۵ ستاره VIP (لوکس)</option>
<option value={4}>۴ ستاره عالی</option>
<option value={3}>۳ ستاره اقتصادی</option>
<option value={2}>۲ ستاره</option>
</select>
</div>
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
امتیاز رضایت (از ۵):
</label>
<Input
type="number"
step="0.1"
min="1"
max="5"
value={hotelFormScore}
onChange={(e) => setHotelFormScore(Number(e.target.value))}
className="h-10 rounded-xl font-mono"
/>
</div>
</div>
{/* Distance to Shrine */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
فاصله و نزدیکی به حرم مطهر:
</label>
<Input
value={hotelFormDistance}
onChange={(e) => setHotelFormDistance(e.target.value)}
placeholder="مثال: ۴۰۰ متر تا باب بغداد حرم مطهر (ترانسفر رایگان هر ۱۰ دقیقه)"
className="h-10 rounded-xl"
/>
</div>
{/* Full Description (توضیحات خود هتل) */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
توضیحات و مشخصات هتل (Description):
</label>
<textarea
value={hotelFormDescription}
onChange={(e) => setHotelFormDescription(e.target.value)}
placeholder="شرح امکانات، بوفه، چشم‌انداز اتاق‌ها و شرایط پذیرش..."
rows={2}
className="w-full p-3 text-xs sm:text-sm rounded-xl bg-background border border-border/70 text-foreground focus:ring-1 focus:ring-primary focus:outline-none"
/>
</div>
{/* Guide Note (فیلد توضیحی راهنما: جدا از توضیحات هتل) */}
<div className="p-3 rounded-xl bg-amber-500/5 border border-amber-500/20 space-y-1.5">
<label className="text-xs font-bold text-amber-800 dark:text-amber-300 flex items-center gap-1.5">
<Ic name="sparkles" className="size-3.5" />
<span>فیلد توضیحی راهنما (Guide Note - راهنمای تجربی مسافران):</span>
</label>
<p className="text-[11px] text-muted-foreground">
توضیحی تکمیلی و راهنمایی برای این هتل (مثلاً: «این هتل در ایام ولادت شلوغ است»، یا نحوه ارائه خدمات به خانواده‌ها).
</p>
<textarea
value={hotelFormGuideNote}
onChange={(e) => setHotelFormGuideNote(e.target.value)}
placeholder="مثال: هتل بارون در کربلا معمولاً ترانسفر سریع و لاندری رایگان شبانه‌روزی دارد..."
rows={2}
className="w-full p-2.5 text-xs rounded-xl bg-background border border-border/70 text-foreground focus:ring-1 focus:ring-primary focus:outline-none"
/>
</div>
{/* Amenities Checkboxes */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1.5">
امکانات و خدمات هتل:
</label>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 p-2.5 rounded-xl bg-muted/30 border border-border/60 max-h-36 overflow-y-auto">
{COMMON_AMENITIES.map((amenity) => {
const isChecked = hotelFormAmenities.includes(amenity.name)
return (
<label
key={amenity.id}
className="flex items-center gap-2 cursor-pointer text-xs text-foreground p-1 hover:bg-card rounded-lg"
>
<input
type="checkbox"
checked={isChecked}
onChange={() => handleToggleHotelAmenity(amenity.name)}
className="rounded border-border text-primary focus:ring-primary"
/>
<span>{amenity.name}</span>
</label>
)
})}
</div>
{/* Add Custom Amenity */}
<div className="flex items-center gap-2 mt-2">
<Input
value={hotelFormCustomAmenity}
onChange={(e) => setHotelFormCustomAmenity(e.target.value)}
placeholder="افزودن امکانات دیگر..."
className="h-9 rounded-xl text-xs"
/>
<Button
type="button"
variant="outline"
size="sm"
onClick={handleAddCustomHotelAmenity}
className="rounded-xl h-9 text-xs"
>
افزودن
</Button>
</div>
</div>
{/* Daily Pricing */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
نرخ روزانه بزرگسال (دلار $):
</label>
<Input
type="number"
min="0"
value={hotelFormPriceAdult}
onChange={(e) => setHotelFormPriceAdult(Number(e.target.value))}
className="h-10 rounded-xl font-mono"
/>
</div>
<div>
<label className="text-xs font-semibold text-foreground block mb-1">
نرخ روزانه کودک (دلار $):
</label>
<Input
type="number"
min="0"
value={hotelFormPriceChild}
onChange={(e) => setHotelFormPriceChild(Number(e.target.value))}
className="h-10 rounded-xl font-mono"
/>
</div>
</div>
{/* Active Status */}
<div className="flex items-center justify-between p-3 rounded-xl bg-muted/40 border border-border/70">
<span className="text-xs font-semibold text-foreground">وضعیت هتل (فعال در سامانه):</span>
<button
type="button"
onClick={() => setHotelFormIsActive(!hotelFormIsActive)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer ${
hotelFormIsActive
? 'bg-emerald-500 text-white'
: 'bg-muted text-muted-foreground'
}`}
>
{hotelFormIsActive ? 'فعال' : 'غیرفعال'}
</button>
</div>
<DialogFooter className="gap-2 sm:gap-0 pt-2">
<Button
type="button"
variant="outline"
onClick={() => setIsHotelFormOpen(false)}
className="rounded-xl text-xs"
>
انصراف
</Button>
<Button
type="submit"
className="rounded-xl text-xs font-bold bg-primary text-white hover:bg-primary/90"
>
{editingHotel ? 'ذخیره تغییرات هتل' : 'ثبت نهایی هتل'}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
{/* ========================================================================= */}
{/* MODAL: ADD / EDIT TRANSPORT ROUTE */}
{/* ========================================================================= */}
<Dialog open={isTransportFormOpen} onOpenChange={setIsTransportFormOpen}>
<DialogContent className="max-w-lg rounded-2xl p-6 max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="text-base font-bold text-foreground flex items-center gap-2">
<Ic name="route" className="size-5 text-primary" />
{editingTransport ? 'ویرایش مسیر حمل‌ونقل' : `تعریف مسیر حمل‌ونقل جدید به ${destination.name}`}
</DialogTitle>
<DialogDescription className="text-xs text-muted-foreground">
مشخصات وسایل نقلیه، خطوط و فیلد توضیحی راهنمای مسیر را ثبت کنید.
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSaveTransport} className="space-y-4 my-2 text-xs sm:text-sm">
{/* Origin City */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">شهر مبدأ:</label>
<select
value={transportFormFromCity}
onChange={(e) => {
const cityName = e.target.value
setTransportFormFromCity(cityName)
const orig = allOrigins.find((o) => o.name === cityName)
if (orig) setTransportFormFromCountry(orig.country)
}}
className="w-full h-10 px-3 text-xs sm:text-sm rounded-xl bg-background border border-border/70 text-foreground font-medium focus:ring-1 focus:ring-primary focus:outline-none"
>
{allOrigins.map((orig) => (
<option key={orig.id} value={orig.name}>
{orig.flag} {orig.name} ({orig.country})
</option>
))}
</select>
</div>
{/* Destination Locked */}
<div>
<label className="text-xs font-semibold text-foreground block mb-1">شهر مقصد (قفل‌شده):</label>
<div className="h-10 px-3 flex items-center rounded-xl bg-muted/70 border border-border/70 text-foreground font-bold">
{destination.flag} {destination.name} ({destination.country})
</div>
</div>
{/* Guide Note for Route (فیلد توضیحی راهنمای مسیر) */}
<div className="p-3 rounded-xl bg-sky-500/5 border border-sky-500/20 space-y-1.5">
<label className="text-xs font-bold text-sky-800 dark:text-sky-300 flex items-center gap-1.5">
<Ic name="route" className="size-3.5" />
<span>فیلد توضیحی راهنمای این مسیر (مثلاً نحوه معمول پروازها یا ترانسفر):</span>
</label>
<textarea
value={transportFormGuideNote}
onChange={(e) => setTransportFormGuideNote(e.target.value)}
placeholder="مثال: پرواز مشهد به نجف معمولاً به صورت روزانه انجام می‌شود..."
rows={2}
className="w-full p-2.5 text-xs rounded-xl bg-background border border-border/70 text-foreground focus:ring-1 focus:ring-primary focus:outline-none"
/>
</div>
{/* Airplane Toggle & Details */}
<div className="p-3 rounded-xl border border-border/70 space-y-2">
<label className="flex items-center gap-2 cursor-pointer font-bold text-foreground text-xs">
<input
type="checkbox"
checked={transportFormHasAirplane}
onChange={(e) => setTransportFormHasAirplane(e.target.checked)}
className="rounded border-border text-primary focus:ring-primary"
/>
<span>✈️ پرواز هوایی در این مسیر فعال است</span>
</label>
{transportFormHasAirplane && (
<Input
value={transportFormAirplaneDetails}
onChange={(e) => setTransportFormAirplaneDetails(e.target.value)}
placeholder="مثال: پرواز مستقیم سلام‌ایر یا ایران‌ایر"
className="h-9 rounded-xl text-xs"
/>
)}
</div>
{/* Train Toggle & Details */}
<div className="p-3 rounded-xl border border-border/70 space-y-2">
<label className="flex items-center gap-2 cursor-pointer font-bold text-foreground text-xs">
<input
type="checkbox"
checked={transportFormHasTrain}
onChange={(e) => setTransportFormHasTrain(e.target.checked)}
className="rounded border-border text-primary focus:ring-primary"
/>
<span>🚆 قطار ریلی در این مسیر فعال است</span>
</label>
{transportFormHasTrain && (
<Input
value={transportFormTrainDetails}
onChange={(e) => setTransportFormTrainDetails(e.target.value)}
placeholder="مثال: قطارهای ۵ ستاره فدک یا خط بصره - کربلا"
className="h-9 rounded-xl text-xs"
/>
)}
</div>
{/* Road Toggle & Details */}
<div className="p-3 rounded-xl border border-border/70 space-y-2">
<label className="flex items-center gap-2 cursor-pointer font-bold text-foreground text-xs">
<input
type="checkbox"
checked={transportFormHasRoad}
onChange={(e) => setTransportFormHasRoad(e.target.checked)}
className="rounded border-border text-primary focus:ring-primary"
/>
<span>🚌 حمل‌ونقل جاده‌ای (اتوبوس VIP و ون اختصاصی)</span>
</label>
{transportFormHasRoad && (
<Input
value={transportFormRoadDetails}
onChange={(e) => setTransportFormRoadDetails(e.target.value)}
placeholder="مثال: ترانسفر زمینی از گذرگاه مرزی مهران یا شلمچه"
className="h-9 rounded-xl text-xs"
/>
)}
</div>
{/* Active Switch */}
<div className="flex items-center justify-between p-3 rounded-xl bg-muted/40 border border-border/70">
<span className="text-xs font-semibold text-foreground">وضعیت مسیر (فعال در سامانه):</span>
<button
type="button"
onClick={() => setTransportFormIsActive(!transportFormIsActive)}
className={`px-3 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer ${
transportFormIsActive
? 'bg-emerald-500 text-white'
: 'bg-muted text-muted-foreground'
}`}
>
{transportFormIsActive ? 'فعال' : 'غیرفعال'}
</button>
</div>
<DialogFooter className="gap-2 sm:gap-0 pt-2">
<Button
type="button"
variant="outline"
onClick={() => setIsTransportFormOpen(false)}
className="rounded-xl text-xs"
>
انصراف
</Button>
<Button
type="submit"
className="rounded-xl text-xs font-bold bg-primary text-white hover:bg-primary/90"
>
{editingTransport ? 'ذخیره تغییرات مسیر' : 'ثبت مسیر حمل‌ونقل'}
</Button>
</DialogFooter>
</form>
</DialogContent>
</Dialog>
</div>
)
}