import { useState, useEffect, useContext } from 'react' import { Plus, Copy, Check, ExternalLink, Trash2, Save, FileEdit, Languages, ShieldCheck, Globe, Lock } from 'lucide-react' import { AppContext } from '../App' import { useLanguage } from '../i18n/LanguageContext' import { api } from '../utils/api' import { AVAILABLE_LANGUAGES, TRANSLATION_STATUS_COLORS, isTextSelected, groupTextsByLanguage } from '../utils/translations' import Modal from './Modal' import TabbedModal from './TabbedModal' import { useToast } from './ToastContainer' import ApproverMultiSelect from './ApproverMultiSelect' export default function TranslationDetailPanel({ translation, onClose, onUpdate, onDelete, assignableUsers = [], posts: externalPosts }) { const { t } = useLanguage() const { brands } = useContext(AppContext) const toast = useToast() const isApproved = translation.status === 'approved' const [editTitle, setEditTitle] = useState(translation.title || '') const [editSourceContent, setEditSourceContent] = useState(translation.source_content || '') const [editSourceLanguage, setEditSourceLanguage] = useState(translation.source_language || 'EN') const [editApproverIds, setEditApproverIds] = useState( translation.approvers?.map(a => String(a.id)) || (translation.approver_ids ? translation.approver_ids.split(',').map(s => s.trim()).filter(Boolean) : []) ) const reviewUrl = translation.approval_token ? `${window.location.origin}/review-translation/${translation.approval_token}` : '' const [activeTab, setActiveTab] = useState('details') const [texts, setTexts] = useState([]) const [loading, setLoading] = useState(true) const [savingDraft, setSavingDraft] = useState(false) const [submitting, setSubmitting] = useState(false) const [copied, setCopied] = useState(false) const [freshReviewUrl, setFreshReviewUrl] = useState('') const [copiedTextId, setCopiedTextId] = useState(null) // Post selector const [posts, setPosts] = useState(externalPosts || []) const [showCreatePost, setShowCreatePost] = useState(false) const [newPostTitle, setNewPostTitle] = useState('') const [creatingPost, setCreatingPost] = useState(false) // Language add modal const [showAddLang, setShowAddLang] = useState(false) const [langForm, setLangForm] = useState({ language_code: '', content: '' }) const [savingLang, setSavingLang] = useState(false) // Delete confirm const [confirmDeleteTextId, setConfirmDeleteTextId] = useState(null) const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) // Inline editing for translation texts const [editingTextId, setEditingTextId] = useState(null) const [editingContent, setEditingContent] = useState('') useEffect(() => { loadTexts() }, [translation.Id]) useEffect(() => { if (externalPosts) setPosts(externalPosts) }, [externalPosts]) useEffect(() => { setEditTitle(translation.title || '') setEditSourceContent(translation.source_content || '') setEditSourceLanguage(translation.source_language || 'EN') setEditApproverIds( translation.approvers?.map(a => String(a.id)) || (translation.approver_ids ? translation.approver_ids.split(',').map(s => s.trim()).filter(Boolean) : []) ) }, [translation.Id]) const loadTexts = async () => { try { const res = await api.get(`/translations/${translation.Id}/texts`) setTexts(Array.isArray(res) ? res : []) } catch (err) { console.error('Failed to load texts:', err) } finally { setLoading(false) } } const handleSaveDraft = async () => { if (!editTitle.trim()) { toast.error(t('translations.titleRequired')) return } setSavingDraft(true) try { await api.patch(`/translations/${translation.Id}`, { title: editTitle, source_content: editSourceContent, source_language: editSourceLanguage, approver_ids: editApproverIds.length > 0 ? editApproverIds.join(',') : null, }) toast.success(t('translations.draftSaved')) onUpdate() } catch (err) { toast.error(t('translations.failedSaveDraft')) } finally { setSavingDraft(false) } } const handleFieldUpdate = async (field, value) => { try { await api.patch(`/translations/${translation.Id}`, { [field]: value || null }) toast.success(t('translations.updated')) onUpdate() } catch (err) { toast.error(t('translations.failedUpdate')) } } const handleAddTranslationText = async () => { if (!langForm.language_code || !langForm.content) { toast.error(t('translations.allFieldsRequired')) return } setSavingLang(true) try { const lang = AVAILABLE_LANGUAGES.find(l => l.code === langForm.language_code) await api.post(`/translations/${translation.Id}/texts`, { language_code: langForm.language_code, language_label: lang?.label || langForm.language_code, content: langForm.content, }) toast.success(t('translations.translationAdded')) setShowAddLang(false) setLangForm({ language_code: '', content: '' }) loadTexts() } catch (err) { toast.error(t('translations.failedAddTranslation')) } finally { setSavingLang(false) } } const handleUpdateText = async (textId) => { try { await api.patch(`/translations/${translation.Id}/texts/${textId}`, { content: editingContent, }) toast.success(t('translations.updated')) setEditingTextId(null) loadTexts() } catch (err) { toast.error(t('translations.failedUpdate')) } } const handleDeleteText = async (textId) => { try { await api.delete(`/translations/${translation.Id}/texts/${textId}`) toast.success(t('translations.translationDeleted')) loadTexts() } catch (err) { toast.error(t('translations.failedDeleteTranslation')) } } const handleSubmitReview = async () => { setSubmitting(true) try { const res = await api.post(`/translations/${translation.Id}/submit-review`) setFreshReviewUrl(res.reviewUrl || res.data?.reviewUrl || '') toast.success(t('translations.submittedForReview')) onUpdate() } catch (err) { toast.error(t('translations.failedSubmitReview')) } finally { setSubmitting(false) } } const copyReviewLink = () => { const url = freshReviewUrl || reviewUrl navigator.clipboard.writeText(url) setCopied(true) toast.success(t('translations.linkCopied')) setTimeout(() => setCopied(false), 2000) } const handleDelete = async () => { try { await onDelete(translation.Id || translation.id || translation._id) } catch (err) { toast.error(t('translations.failedDelete')) } } const handleCreatePost = async () => { if (!newPostTitle.trim()) return setCreatingPost(true) try { const created = await api.post('/posts', { title: newPostTitle, status: 'draft' }) const postId = created.Id || created.id || created._id setPosts(prev => [created, ...prev]) await handleFieldUpdate('post_id', postId) setShowCreatePost(false) setNewPostTitle('') } catch (err) { toast.error(t('translations.postCreateFailed')) } finally { setCreatingPost(false) } } const copyTextContent = (content, id) => { navigator.clipboard.writeText(content) setCopiedTextId(id) toast.success(t('translations.copiedToClipboard')) setTimeout(() => setCopiedTextId(null), 2000) } // Available languages (exclude source language only — multiple options per language allowed) const targetLanguages = AVAILABLE_LANGUAGES.filter(l => l.code !== translation.source_language) // Group texts by language const textsByLanguage = groupTextsByLanguage(texts) const tabs = [ { key: 'details', label: t('translations.details'), icon: FileEdit }, { key: 'translations', label: t('translations.translationTexts'), icon: Languages, badge: texts.length }, { key: 'review', label: t('translations.review'), icon: ShieldCheck }, ] const currentReviewUrl = freshReviewUrl || reviewUrl return ( <>
setEditTitle(e.target.value)} readOnly={isApproved} className={`text-lg font-bold text-text-primary bg-transparent border-none outline-none focus:ring-0 w-full ${isApproved ? 'cursor-default' : ''}`} placeholder={t('translations.titlePlaceholder')} />
{translation.status?.replace('_', ' ')} {AVAILABLE_LANGUAGES.find(l => l.code === editSourceLanguage)?.label || editSourceLanguage} {translation.creator_name && ( {t('review.createdBy')} {translation.creator_name} )}
} tabs={tabs} activeTab={activeTab} onTabChange={setActiveTab} footer={isApproved ? (
{t('translations.approvedReadOnly')}
) : (
)} > {/* Details Tab */} {activeTab === 'details' && (

{t('translations.sourceLanguage')}

{t('translations.sourceContent')}