import * as React from 'react' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Ic } from '@/icons' import { Shimmer } from '@/components/shared/shimmer_frontend' import { TourDetailOverviewShimmer } from './TourDetailShimmer' import { fetchTourTranslations, saveTourTranslations, fetchTourItineraryTranslations, saveTourItineraryTranslation, } from '../services/tour-api' import type { TourTranslationsResponse, TourTranslationData, TourItineraryTranslationsResponseItem, } from '../types' interface TourTranslationsDialogProps { tourId: number | null tourTitle?: string open: boolean onOpenChange: (open: boolean) => void onSuccess?: () => void } const SUPPORTED_LANGUAGES = [ { code: 'ar', label: 'العربية', flag: '🇸🇦', dir: 'rtl' }, { code: 'fa', label: 'فارسی', flag: '🇮🇷', dir: 'rtl' }, { code: 'en', label: 'English', flag: '🇬🇧', dir: 'ltr' }, { code: 'ru', label: 'Русский', flag: '🇷🇺', dir: 'ltr' }, { code: 'id', label: 'Indonesia', flag: '🇮🇩', dir: 'ltr' }, ] as const type LangCode = (typeof SUPPORTED_LANGUAGES)[number]['code'] export function TourTranslationsDialog({ tourId, tourTitle, open, onOpenChange, onSuccess, }: TourTranslationsDialogProps) { const [selectedLang, setSelectedLang] = React.useState('ar') const [activeSection, setActiveSection] = React.useState<'base' | 'features' | 'tips' | 'itinerary'>('base') const [isLoading, setIsLoading] = React.useState(false) const [isSaving, setIsSaving] = React.useState(false) const [errorMessage, setErrorMessage] = React.useState(null) const [successMessage, setSuccessMessage] = React.useState(null) // Translations data const [transData, setTransData] = React.useState(null) const [itineraries, setItineraries] = React.useState([]) // Local draft states for current selected language const [formTitle, setFormTitle] = React.useState('') const [formDesc, setFormDesc] = React.useState('') const [formFeatures, setFormFeatures] = React.useState([]) const [newFeatureText, setNewFeatureText] = React.useState('') const [formTips, setFormTips] = React.useState<{ title: string; desc: string }[]>([]) const [newTipTitle, setNewTipTitle] = React.useState('') const [newTipDesc, setNewTipDesc] = React.useState('') // Drafts for itineraries: { [itineraryId]: { title: string, summary: string } } const [itinDrafts, setItinDrafts] = React.useState>({}) // Load translations on open React.useEffect(() => { if (!open || !tourId) return let isMounted = true setIsLoading(true) setErrorMessage(null) setSuccessMessage(null) Promise.all([ fetchTourTranslations(tourId), fetchTourItineraryTranslations(tourId).catch(() => []), ]) .then(([tourTrans, itinTrans]) => { if (!isMounted) return setTransData(tourTrans) setItineraries(itinTrans || []) // Default to a language different from primary if primary is English/Persian const defaultLang = tourTrans.primary_language === 'fa' ? 'ar' : 'fa' setSelectedLang(defaultLang as LangCode) initDraftForLanguage(defaultLang as LangCode, tourTrans, itinTrans || []) }) .catch((err) => { if (!isMounted) return setErrorMessage(err?.message || 'خطا در بارگذاری ترجمه‌های تور از سرور.') }) .finally(() => { if (isMounted) setIsLoading(false) }) return () => { isMounted = false } }, [open, tourId]) // Sync draft whenever selectedLang changes const initDraftForLanguage = ( lang: LangCode, tourTrans: TourTranslationsResponse | null, itinList: TourItineraryTranslationsResponseItem[] ) => { if (!tourTrans) return const isPrimary = tourTrans.primary_language === lang const existingTour = isPrimary ? { title: tourTrans.primary_data.title || '', description: tourTrans.primary_data.description || '', tour_feature: tourTrans.primary_data.tour_feature || [], travel_tips: tourTrans.primary_data.travel_tips || {}, } : tourTrans.translations[lang] || { title: '', description: '', tour_feature: [], travel_tips: {}, } setFormTitle(existingTour.title || '') setFormDesc(existingTour.description || '') const featuresList = (existingTour.tour_feature || []).map((f) => f.title) setFormFeatures(featuresList) const tipsList = Object.entries(existingTour.travel_tips || {}).map(([title, desc]) => ({ title, desc: String(desc), })) setFormTips(tipsList) // Init itinerary drafts for this language const drafts: Record = {} itinList.forEach((it) => { const itTrans = it.translations[lang] drafts[it.id] = { title: itTrans?.title || '', summary: itTrans?.summary || '', } }) setItinDrafts(drafts) } const handleLanguageChange = (lang: LangCode) => { setSelectedLang(lang) setErrorMessage(null) setSuccessMessage(null) initDraftForLanguage(lang, transData, itineraries) } const handleAddFeature = () => { if (!newFeatureText.trim()) return setFormFeatures((prev) => [...prev, newFeatureText.trim()]) setNewFeatureText('') } const handleRemoveFeature = (idx: number) => { setFormFeatures((prev) => prev.filter((_, i) => i !== idx)) } const handleAddTip = () => { if (!newTipTitle.trim() || !newTipDesc.trim()) return setFormTips((prev) => [...prev, { title: newTipTitle.trim(), desc: newTipDesc.trim() }]) setNewTipTitle('') setNewTipDesc('') } const handleRemoveTip = (idx: number) => { setFormTips((prev) => prev.filter((_, i) => i !== idx)) } const handleItineraryDraftChange = (itId: number, field: 'title' | 'summary', val: string) => { setItinDrafts((prev) => ({ ...prev, [itId]: { title: prev[itId]?.title || '', summary: prev[itId]?.summary || '', [field]: val, }, })) } const handleSave = async () => { if (!tourId || !transData) return if (!formTitle.trim() && !isPrimary) { setErrorMessage(`وارد کردن عنوان تور برای زبان ${activeLangConfig?.label || selectedLang} اجباری است.`) return } setIsSaving(true) setErrorMessage(null) setSuccessMessage(null) try { // Build tour_feature array const tour_feature = formFeatures.map((title) => ({ title })) // Build travel_tips object const travel_tips: Record = {} formTips.forEach((item) => { travel_tips[item.title] = item.desc }) // 1. Save Tour translation await saveTourTranslations(tourId, { lang_code: selectedLang, title: formTitle.trim(), description: formDesc.trim(), tour_feature, travel_tips, }) // 2. Save Itinerary translations const itinSavePromises = Object.entries(itinDrafts).map(([itIdStr, draft]) => { const itId = Number(itIdStr) if (!draft.title.trim() && !draft.summary.trim()) return Promise.resolve() return saveTourItineraryTranslation(tourId, { itinerary_id: itId, lang_code: selectedLang, title: draft.title.trim(), summary: draft.summary.trim(), }) }) await Promise.all(itinSavePromises) // Refresh data const [updatedTour, updatedItin] = await Promise.all([ fetchTourTranslations(tourId), fetchTourItineraryTranslations(tourId).catch(() => []), ]) setTransData(updatedTour) setItineraries(updatedItin || []) setSuccessMessage(`ترجمه زبان ${SUPPORTED_LANGUAGES.find((l) => l.code === selectedLang)?.label} با موفقیت در دیتابیس ذخیره شد.`) if (onSuccess) onSuccess() } catch (err: any) { setErrorMessage(err?.message || 'خطا در ذخیره ترجمه در سرور.') } finally { setIsSaving(false) } } const activeLangConfig = SUPPORTED_LANGUAGES.find((l) => l.code === selectedLang) const isPrimary = transData?.primary_language === selectedLang return ( {/* Dialog Header */}
مدیریت ترجمه‌های 5 زبانه تور {tourTitle || `تور شناسه #${tourId}`} • ذخیره‌سازی پویا در سرور و پایگاه‌داده
{/* Primary Language Indicator */} {transData && (
زبان پایه دیتابیس: {SUPPORTED_LANGUAGES.find((l) => l.code === transData.primary_language)?.label || transData.primary_language}
)} {/* دکمه ضربدر بالای مودال */}
{/* 5-Language Selector Bar */}
{SUPPORTED_LANGUAGES.map((lang) => { const isSelected = lang.code === selectedLang const hasTrans = Boolean( transData?.primary_language === lang.code || transData?.translations[lang.code]?.title ) return ( ) })}
{/* Dialog Body */}
{isLoading ? ( ) : ( <> {errorMessage && (
{errorMessage}
)} {successMessage && (
{successMessage}
)} {/* Section Tabs */}
{/* Active Section Form */}
{/* 1. Base Info */} {activeSection === 'base' && (
setFormTitle(e.target.value)} placeholder={`عنوان تور به زبان ${activeLangConfig?.label}...`} className="bg-surface-2 border-border/50 text-sm" disabled={isPrimary} /> {isPrimary && (

* این زبان، زبان اصلی تور است و از فرم اصلی مدیریت می‌شود.

)}