// Calendar screen — monthly view with tasks and content posts.
(() => {
const { Card, Badge, Avatar } = window.TechyFuelOSDesignSystem_be0222;
const PRIORITY_COLOR = {
urgent: 'var(--red-500)',
high: 'var(--amber-500)',
medium: 'var(--blue-500)',
low: 'var(--slate-400)',
};
const STATUS_DOT = {
todo: 'var(--blue-500)',
in_progress: 'var(--violet-500)',
review: 'var(--amber-500)',
done: 'var(--green-500)',
};
const PLAT_COLOR = {
instagram: 'var(--violet-500)',
facebook: 'var(--blue-600)',
linkedin: 'var(--sky-600)',
twitter: 'var(--sky-400)',
youtube: 'var(--red-500)',
tiktok: 'var(--slate-700)',
};
function getDaysInMonth(year, month) {
const days = [];
const firstDay = new Date(year, month, 1).getDay();
const totalDays = new Date(year, month + 1, 0).getDate();
// Leading blanks
for (let i = 0; i < (firstDay === 0 ? 6 : firstDay - 1); i++) days.push(null);
for (let d = 1; d <= totalDays; d++) days.push(d);
// Trailing blanks to fill last row
while (days.length % 7 !== 0) days.push(null);
return days;
}
function toDateStr(year, month, day) {
return `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
}
function CalendarEvent({ ev }) {
const color = ev.type === 'post' ? (PLAT_COLOR[ev.platform] || 'var(--violet-500)') : (PRIORITY_COLOR[ev.priority] || 'var(--blue-500)');
return (
{ev.title}
);
}
function CalendarCell({ day, year, month, events, isToday, isOtherMonth }) {
const dayEvents = day ? (events[toDateStr(year, month, day)] || []) : [];
return (
{day && (
<>
{day}
{dayEvents.slice(0, 3).map((ev, i) =>
)}
{dayEvents.length > 3 &&
+{dayEvents.length - 3} more
}
>
)}
);
}
function Calendar() {
useLucide();
const today = new Date();
const [year, setYear] = React.useState(today.getFullYear());
const [month, setMonth] = React.useState(today.getMonth());
const [events, setEvents] = React.useState({});
const [totalEvents, setTotalEvents] = React.useState(0);
const [modalOpen, setModalOpen] = React.useState(false);
const [saving, setSaving] = React.useState(false);
const [team, setTeam] = React.useState([]);
const [clients, setClients] = React.useState([]);
const [form, setForm] = React.useState({ title: '', type: 'task', due_date: '', priority: 'medium', status: 'todo', platform: 'instagram', assigned_to: '', client_id: '' });
function set(k, v) { setForm(f => ({ ...f, [k]: v })); }
React.useEffect(() => {
if (!window.API) return;
const addEvent = (dateStr, ev) => {
setEvents(prev => {
const updated = { ...prev };
if (!updated[dateStr]) updated[dateStr] = [];
updated[dateStr] = [...updated[dateStr], ev];
return updated;
});
};
let count = 0;
window.API.getTasks().then(r => {
if (!r.data) return;
r.data.forEach(t => {
if (!t.due_date || t.status === 'done') return;
const ds = t.due_date.split('T')[0];
addEvent(ds, { id: t.id, title: t.title, type: 'task', priority: t.priority, status: t.status });
count++;
});
setTotalEvents(c => c + count);
}).catch(() => {});
window.API.getContent().then(r => {
if (!r.data) return;
r.data.forEach(p => {
if (!p.scheduled_at) return;
const ds = p.scheduled_at.split('T')[0];
addEvent(ds, { id: p.id, title: p.title, type: 'post', platform: p.platform });
});
}).catch(() => {});
window.API.getTeam().then(r => { if (r.data) setTeam(r.data); }).catch(() => {});
window.API.getClients().then(r => { if (r.data) setClients(r.data); }).catch(() => {});
}, []);
async function handleAdd() {
if (!form.title.trim()) return;
setSaving(true);
try {
if (form.type === 'task') {
const payload = { title: form.title, priority: form.priority, status: form.status };
if (form.due_date) payload.due_date = form.due_date;
if (form.assigned_to) payload.assigned_to = form.assigned_to;
if (form.client_id) payload.client_id = form.client_id;
if (window.API) {
const { data } = await window.API.createTask(payload);
if (data && data.due_date) {
const ds = data.due_date.split('T')[0];
setEvents(prev => ({ ...prev, [ds]: [...(prev[ds] || []), { id: data.id, title: data.title, type: 'task', priority: data.priority }] }));
}
}
} else {
const payload = { title: form.title, platform: form.platform, status: 'scheduled' };
if (form.due_date) payload.scheduled_at = form.due_date + 'T09:00:00';
if (form.client_id) payload.client_id = form.client_id;
if (window.API) {
const { data } = await window.API.createPost(payload);
if (data && data.scheduled_at) {
const ds = data.scheduled_at.split('T')[0];
setEvents(prev => ({ ...prev, [ds]: [...(prev[ds] || []), { id: data.id, title: data.title, type: 'post', platform: data.platform }] }));
}
}
}
setModalOpen(false);
setForm({ title: '', type: 'task', due_date: '', priority: 'medium', status: 'todo', platform: 'instagram', assigned_to: '', client_id: '' });
} finally { setSaving(false); }
}
function prevMonth() {
if (month === 0) { setYear(y => y - 1); setMonth(11); }
else setMonth(m => m - 1);
}
function nextMonth() {
if (month === 11) { setYear(y => y + 1); setMonth(0); }
else setMonth(m => m + 1);
}
const days = getDaysInMonth(year, month);
const monthLabel = new Date(year, month, 1).toLocaleDateString('en', { month: 'long', year: 'numeric' });
const todayStr = toDateStr(today.getFullYear(), today.getMonth(), today.getDate());
const allEventsThisMonth = Object.entries(events).filter(([ds]) => ds.startsWith(`${year}-${String(month + 1).padStart(2, '0')}`));
const thisMonthCount = allEventsThisMonth.reduce((s, [, arr]) => s + arr.length, 0);
const DOW = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return (
Calendar
{monthLabel} · {thisMonthCount} events
{monthLabel}
{days.map((day, i) => {
const ds = day ? toDateStr(year, month, day) : '';
const isToday = ds === todayStr;
return (
);
})}
setModalOpen(false)} title="Add event" onSubmit={handleAdd} loading={saving} submitLabel="Add">
set('title', e.target.value)} />
set('due_date', e.target.value)} />
{form.type === 'task' ? (
) : (
)}
);
}
Object.assign(window, { Calendar });
})();