import { useState, useEffect, useContext } from 'react' import { Plus, LayoutGrid, List, Search, X, FileText } from 'lucide-react' import { AppContext } from '../App' import { useAuth } from '../contexts/AuthContext' import { useLanguage } from '../i18n/LanguageContext' import { api, PLATFORMS } from '../utils/api' import KanbanBoard from '../components/KanbanBoard' import PostCard from '../components/PostCard' import PostDetailPanel from '../components/PostDetailPanel' import DatePresetPicker from '../components/DatePresetPicker' import { SkeletonKanbanBoard, SkeletonTable } from '../components/SkeletonLoader' import EmptyState from '../components/EmptyState' import { useToast } from '../components/ToastContainer' const EMPTY_POST = { title: '', description: '', brand_id: '', platforms: [], status: 'draft', assigned_to: '', scheduled_date: '', notes: '', campaign_id: '', publication_links: [], } export default function PostProduction() { const { t, lang } = useLanguage() const { teamMembers, brands } = useContext(AppContext) const { canEditResource } = useAuth() const toast = useToast() const [posts, setPosts] = useState([]) const [loading, setLoading] = useState(true) const [view, setView] = useState('kanban') const [panelPost, setPanelPost] = useState(null) const [campaigns, setCampaigns] = useState([]) const [filters, setFilters] = useState({ brand: '', platform: '', assignedTo: '', campaign: '', periodFrom: '', periodTo: '' }) const [searchTerm, setSearchTerm] = useState('') const [activePreset, setActivePreset] = useState('') const [moveError, setMoveError] = useState('') useEffect(() => { loadPosts() api.get('/campaigns').then(r => setCampaigns(Array.isArray(r) ? r : (r.data || []))).catch(() => {}) }, []) const loadPosts = async () => { try { const res = await api.get('/posts') setPosts(res.data || res || []) } catch (err) { console.error('Failed to load posts:', err) } finally { setLoading(false) } } const handleMovePost = async (postId, newStatus) => { try { await api.patch(`/posts/${postId}`, { status: newStatus }) toast.success(t('posts.statusUpdated')) loadPosts() } catch (err) { console.error('Move failed:', err) if (err.message?.includes('Cannot publish')) { setMoveError(t('posts.publishRequired')) setTimeout(() => setMoveError(''), 5000) toast.error(t('posts.publishRequired')) } else { toast.error(t('common.updateFailed')) } } } const handlePanelSave = async (postId, data) => { if (postId) { await api.patch(`/posts/${postId}`, data) toast.success(t('posts.updated')) } else { await api.post('/posts', data) toast.success(t('posts.created')) } loadPosts() } const handlePanelDelete = async (postId) => { try { await api.delete(`/posts/${postId}`) toast.success(t('posts.deleted')) loadPosts() } catch (err) { console.error('Delete failed:', err) toast.error(t('common.deleteFailed')) } } const openEdit = (post) => { if (!canEditResource('post', post)) { alert('You can only edit your own posts') return } setPanelPost(post) } const openNew = () => { setPanelPost(EMPTY_POST) } const filteredPosts = posts.filter(p => { if (filters.brand && String(p.brandId || p.brand_id) !== filters.brand) return false if (filters.platform && !(p.platforms || []).includes(filters.platform) && p.platform !== filters.platform) return false if (filters.assignedTo && String(p.assignedTo || p.assigned_to) !== filters.assignedTo) return false if (filters.campaign && String(p.campaignId || p.campaign_id) !== filters.campaign) return false if (searchTerm && !p.title?.toLowerCase().includes(searchTerm.toLowerCase())) return false if (filters.periodFrom || filters.periodTo) { const postDate = p.scheduledDate || p.scheduled_date || p.published_date || p.publishedDate if (!postDate) return false const d = new Date(postDate).toISOString().slice(0, 10) if (filters.periodFrom && d < filters.periodFrom) return false if (filters.periodTo && d > filters.periodTo) return false } return true }) if (loading) { return view === 'kanban' ? : } return (
{/* Toolbar */}
setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary bg-white" />
{ setFilters(f => ({ ...f, periodFrom: from, periodTo: to })); setActivePreset(key) }} onClear={() => { setFilters(f => ({ ...f, periodFrom: '', periodTo: '' })); setActivePreset('') }} />
{ setFilters(f => ({ ...f, periodFrom: e.target.value })); setActivePreset('') }} title={t('posts.periodFrom')} className="text-sm border border-border rounded-lg px-2 py-2 bg-white text-text-secondary focus:outline-none focus:ring-2 focus:ring-brand-primary/20" /> { setFilters(f => ({ ...f, periodTo: e.target.value })); setActivePreset('') }} title={t('posts.periodTo')} className="text-sm border border-border rounded-lg px-2 py-2 bg-white text-text-secondary focus:outline-none focus:ring-2 focus:ring-brand-primary/20" />
{moveError && (
{moveError}
)} {view === 'kanban' ? ( ) : (
{filteredPosts.length === 0 ? ( 0 ? t('common.clearFilters') : null} onSecondaryAction={() => { setFilters({ brand: '', platform: '', assignedTo: '', campaign: '', periodFrom: '', periodTo: '' }) setSearchTerm('') }} /> ) : ( {filteredPosts.map(post => ( openEdit(post)} /> ))}
{t('posts.postTitle')} {t('posts.brand')} {t('posts.status')} {t('posts.platforms')} {t('posts.assignTo')} {t('posts.scheduledDate')}
)}
)} {/* Post Detail Panel */} {panelPost && ( setPanelPost(null)} onSave={handlePanelSave} onDelete={handlePanelDelete} brands={brands} teamMembers={teamMembers} campaigns={campaigns} /> )}
) }