// Social Media Content Calendar const CCIcons = window.Icons; const { toDateKey: ccKey, parseDateKey: ccParse, relativeDate: ccRel } = window.PMStore; const PLATFORMS = [ { id: 'instagram', label: 'Instagram', short: 'IG', color: '#E1306C', icon: 'Instagram' }, { id: 'facebook', label: 'Facebook', short: 'FB', color: '#1877F2', icon: 'Facebook' }, { id: 'linkedin', label: 'LinkedIn', short: 'LI', color: '#0A66C2', icon: 'Linkedin' }, { id: 'tiktok', label: 'TikTok', short: 'TT', color: '#010101', icon: 'TikTok' }, { id: 'twitter', label: 'X / Twitter', short: 'TW', color: '#1DA1F2', icon: 'Twitter' }, { id: 'youtube', label: 'YouTube', short: 'YT', color: '#FF0000', icon: 'Youtube' }, ]; const CONTENT_TYPES = [ { id: 'post', label: 'Gönderi', emoji: '📷' }, { id: 'story', label: 'Hikaye', emoji: '⚡' }, { id: 'reel', label: 'Reel', emoji: '🎬' }, { id: 'video', label: 'Video', emoji: '▶️' }, { id: 'carousel', label: 'Carousel', emoji: '🔄' }, { id: 'article', label: 'Makale', emoji: '📝' }, ]; const CONTENT_STATUSES = [ { id: 'brief', label: 'Brief', color: '#94a3b8' }, { id: 'production', label: 'Üretimde', color: '#3b82f6' }, { id: 'review', label: 'İncelemede', color: '#a855f7' }, { id: 'client_review', label: 'Müşteri Onayı', color: '#f59e0b' }, { id: 'approved', label: 'Onaylandı', color: '#22c55e' }, { id: 'scheduled', label: 'Zamanlandı', color: '#6366f1' }, { id: 'published', label: 'Yayınlandı', color: '#ff601f' }, ]; const DAYS_TR = ['Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt', 'Paz']; const MONTHS_TR = ['Ocak','Şubat','Mart','Nisan','Mayıs','Haziran','Temmuz','Ağustos','Eylül','Ekim','Kasım','Aralık']; function d0(date) { const d = new Date(date); d.setHours(0,0,0,0); return d; } function ContentCalendar({ state, onSelectTask, onAddTask, onUpdateTask }) { const today = d0(new Date()); const [year, setYear] = React.useState(today.getFullYear()); const [month, setMonth] = React.useState(today.getMonth()); const [pfFilter, setPF] = React.useState('all'); const [projFilter, setPJ] = React.useState('all'); const [viewMode, setVM] = React.useState('month'); const [createDate, setCD] = React.useState(null); const prevMonth = () => month === 0 ? (setMonth(11), setYear(y => y-1)) : setMonth(m => m-1); const nextMonth = () => month === 11? (setMonth(0), setYear(y => y+1)) : setMonth(m => m+1); const goToday = () => { setYear(today.getFullYear()); setMonth(today.getMonth()); }; const projById = Object.fromEntries((state.projects||[]).map(p => [p.id, p])); const contentTasks = (state.tasks||[]).filter(t => t.platform && !t.archived && (pfFilter === 'all' || t.platform === pfFilter) && (projFilter === 'all' || t.projectId === projFilter) ); // Monthly grid const firstDay = new Date(year, month, 1); const lastDay = new Date(year, month + 1, 0); const startDow = (firstDay.getDay() + 6) % 7; const cells = []; for (let i = 0; i < startDow; i++) cells.push(null); for (let d = 1; d <= lastDay.getDate(); d++) cells.push(new Date(year, month, d)); const tasksByDate = {}; contentTasks.forEach(t => { if (t.due) { (tasksByDate[t.due] = tasksByDate[t.due] || []).push(t); } }); // Stats bar const monthStart = ccKey(new Date(year, month, 1)); const monthEnd = ccKey(new Date(year, month + 1, 0)); const monthTasks = contentTasks.filter(t => t.due && t.due >= monthStart && t.due <= monthEnd); return (
{/* ── Toolbar ─────────────────────────────────────────── */}

{MONTHS_TR[month]} {year}

{PLATFORMS.map(p => ( ))}
{[{id:'month',label:'Aylık'},{id:'list',label:'Liste'},{id:'pipeline',label:'Pipeline'}].map(v => ( ))}
{/* ── Ay özeti ────────────────────────────────────────── */} {monthTasks.length > 0 && (
{monthTasks.length} içerik {PLATFORMS.filter(p => monthTasks.some(t => t.platform===p.id)).map(p => { const cnt = monthTasks.filter(t => t.platform===p.id).length; return ( {p.short} {cnt} ); })} {CONTENT_STATUSES.map(s => { const cnt = monthTasks.filter(t => (t.contentStatus||'brief')===s.id).length; if (!cnt) return null; return ( {s.label} {cnt} ); })}
)} {/* ── Görünümler ──────────────────────────────────────── */} {viewMode === 'month' && (
{DAYS_TR.map(d =>
{d}
)}
{cells.map((date, i) => { if (!date) return
; const dk = ccKey(date); const dayTasks = tasksByDate[dk] || []; const isToday = date.getTime() === today.getTime(); const isWeekend = date.getDay() === 0 || date.getDay() === 6; return (
{date.getDate()}
{dayTasks.slice(0,3).map(t => ( onSelectTask(t.id)}/> ))} {dayTasks.length > 3 && (
+{dayTasks.length-3} daha
)}
); })}
)} {viewMode === 'list' && ( setCD(ccKey(today))} onUpdateTask={onUpdateTask} /> )} {viewMode === 'pipeline' && ( setCD(ccKey(today))} /> )} {createDate && ( { onAddTask(data); setCD(null); }} onClose={() => setCD(null)} /> )}
); } /* ── İçerik kartı (aylık grid için) ───────────────────────── */ function ContentCard({ task, project, onClick }) { const pf = PLATFORMS.find(p => p.id === task.platform); const ct = CONTENT_TYPES.find(c => c.id === task.contentType); const cs = CONTENT_STATUSES.find(s => s.id === (task.contentStatus || 'brief')); return (
{pf?.short} {ct && {ct.emoji}}
{task.title}
{project &&
{project.name}
}
); } /* ── Liste görünümü ────────────────────────────────────────── */ function ContentListView({ tasks, projById, onSelectTask, onNew, onUpdateTask }) { const sorted = [...tasks].sort((a,b) => (a.due||'9999').localeCompare(b.due||'9999')); // Group by week const groups = []; sorted.forEach(t => { const d = t.due ? ccParse(t.due) : null; const weekLabel = d ? getWeekLabel(d) : 'Tarihi Belirsiz'; const last = groups[groups.length-1]; if (last && last.label === weekLabel) last.tasks.push(t); else groups.push({ label: weekLabel, tasks: [t] }); }); return (
{groups.length === 0 && (
İçerik yok
Filtre değiştirin veya yeni içerik ekleyin.
)} {groups.map(g => (
{g.label} {g.tasks.length}
{g.tasks.map(t => ( onSelectTask(t.id)} onStatusChange={(s) => onUpdateTask(t.id, { contentStatus: s })} /> ))}
))}
); } function ContentListRow({ task, project, onClick, onStatusChange }) { const pf = PLATFORMS.find(p => p.id === task.platform); const ct = CONTENT_TYPES.find(c => c.id === task.contentType); const cs = CONTENT_STATUSES.find(s => s.id === (task.contentStatus||'brief')); return (
{pf?.short}
{task.title}
{project && <>{project.name}} {ct && · {ct.emoji} {ct.label}} {task.due && · {ccRel(task.due)}}
{task.description &&
{task.description.slice(0,80)}{task.description.length>80?'…':''}
}
e.stopPropagation()}>
); } /* ── Pipeline (Kanban benzeri onay akışı) ─────────────────── */ function ContentPipeline({ tasks, projById, onSelectTask, onUpdateTask, onNew }) { const [drag, setDrag] = React.useState(null); const drop = (statusId) => { if (drag) onUpdateTask(drag, { contentStatus: statusId }); setDrag(null); }; return (
{CONTENT_STATUSES.map(status => { const colTasks = tasks.filter(t => (t.contentStatus||'brief') === status.id); return (
e.preventDefault()} onDrop={() => drop(status.id)}>
{status.label} {colTasks.length}
{colTasks.length === 0 && (
Boş
)} {colTasks.map(t => { const pf = PLATFORMS.find(p => p.id === t.platform); const ct = CONTENT_TYPES.find(c => c.id === t.contentType); return (
setDrag(t.id)} onDragEnd={() => setDrag(null)} onClick={() => onSelectTask(t.id)}>
{pf?.short} {ct && {ct.emoji}} {t.due && {ccRel(t.due)}}
{t.title}
{projById[t.projectId] && (
{projById[t.projectId].name}
)} {t.description && (
{t.description.slice(0,60)}{t.description.length>60?'…':''}
)}
); })}
); })}
); } /* ── İçerik oluştur modal ──────────────────────────────────── */ function ContentCreateModal({ date, state, onSave, onClose }) { const [platform, setPF] = React.useState('instagram'); const [contentType, setCT] = React.useState('post'); const [title, setTT] = React.useState(''); const [caption, setCA] = React.useState(''); const [projectId, setPJ] = React.useState(state.activeProjectId || state.projects[0]?.id || ''); const [postDate, setPD] = React.useState(date || new Date().toISOString().slice(0,10)); const [contentStatus, setCS] = React.useState('brief'); const selPf = PLATFORMS.find(p => p.id === platform); const save = () => { if (!title.trim()) return; onSave({ title: title.trim(), description: caption.trim(), due: postDate || null, projectId, platform, contentType, contentStatus, status: 'todo', priority: 'medium', }); }; return (
e.stopPropagation()}>
{selPf?.short}

Yeni İçerik Planı

{/* Platform */}
Platform
{PLATFORMS.map(p => { const Icon = CCIcons[p.icon]; return ( ); })}
{/* İçerik tipi */}
İçerik Tipi
{CONTENT_TYPES.map(ct => ( ))}