// Team screen — members by department + workload. (() => { const { Card, Badge, Avatar, ProgressBar } = window.TechyFuelOSDesignSystem_be0222; const DEPT_TONE = { Design: 'violet', Video: 'teal', Marketing: 'success', Development: 'info', Sales: 'warning', Admin: 'neutral', Content: 'teal', Leadership: 'brand' }; const ROLE_LABEL = { owner: 'Owner', admin: 'Admin', member: 'Member' }; function Team() { useLucide(); const [team, setTeam] = React.useState([]); const [loading, setLoading] = React.useState(true); const [taskCounts, setTaskCounts] = React.useState({}); const [progress, setProgress] = React.useState({}); // memberId -> { total, done, pct, secondsThisMonth, secondsAllTime } const [modalOpen, setModalOpen] = React.useState(false); const [saving, setSaving] = React.useState(false); const [form, setForm] = React.useState({ name: '', email: '', department: '', role: 'member' }); function set(k, v) { setForm(f => ({ ...f, [k]: v })); } React.useEffect(() => { if (!window.API) { setLoading(false); return; } window.API.getAllTeamMembers().then(r => { setTeam(r.data || []); }).catch(() => {}).finally(() => setLoading(false)); window.API.getTasks().then(r => { if (!r.data) return; const counts = {}; r.data.filter(t => t.status !== 'done').forEach(t => { if (t.assigned_to) counts[t.assigned_to] = (counts[t.assigned_to] || 0) + 1; }); setTaskCounts(counts); const now = new Date(); const monthStart = new Date(now.getFullYear(), now.getMonth(), 1); (window.API.getAllTimeEntries ? window.API.getAllTimeEntries() : Promise.resolve({ data: [] })).then(er => { const entries = er.data || []; const byMember = {}; r.data.forEach(t => { if (!t.assigned_to) return; if (!byMember[t.assigned_to]) byMember[t.assigned_to] = { total: 0, done: 0, secondsThisMonth: 0, secondsAllTime: 0 }; byMember[t.assigned_to].total++; if (t.status === 'done') byMember[t.assigned_to].done++; }); entries.forEach(e => { if (!e.member_id) return; if (!byMember[e.member_id]) byMember[e.member_id] = { total: 0, done: 0, secondsThisMonth: 0, secondsAllTime: 0 }; byMember[e.member_id].secondsAllTime += e.duration_seconds || 0; if (new Date(e.started_at) >= monthStart) byMember[e.member_id].secondsThisMonth += e.duration_seconds || 0; }); const result = {}; Object.keys(byMember).forEach(id => { const b = byMember[id]; result[id] = { ...b, pct: b.total > 0 ? Math.round((b.done / b.total) * 100) : 0 }; }); setProgress(result); }).catch(() => {}); }).catch(() => {}); }, []); function fmtHours(totalSeconds) { if (!totalSeconds) return '0h'; const h = totalSeconds / 3600; return h < 1 ? Math.round(totalSeconds / 60) + 'm' : h.toFixed(1) + 'h'; } async function handleInviteMember() { if (!form.name.trim()) { alert('Name is required.'); return; } if (!form.email.trim()) { alert('Email is required.'); return; } setSaving(true); try { const payload = { name: form.name.trim(), email: form.email.trim(), role: form.role, status: 'active', }; if (form.department) payload.department = form.department; if (window.API) { const { data, error } = await window.API.addTeamMember(payload); if (error) { const msg = error.message || error.details || JSON.stringify(error); alert('Could not add member:\n\n' + msg); return; } if (data) setTeam(prev => [...prev, data]); } setModalOpen(false); setForm({ name: '', email: '', department: '', role: 'member' }); } catch (err) { alert('Error: ' + (err.message || JSON.stringify(err))); } finally { setSaving(false); } } async function toggleStatus(m, e) { e.stopPropagation(); const suspending = m.status !== 'inactive'; if (suspending) { const warn = m.role === 'owner' ? '\n\nThis member is an Owner — suspending them will block their access too.' : ''; if (!window.confirm(`Suspend ${m.name}? They will lose access to TechyFuel OS immediately.${warn}`)) return; } const newStatus = suspending ? 'inactive' : 'active'; setTeam(prev => prev.map(t => t.id === m.id ? { ...t, status: newStatus } : t)); if (window.API) { try { await window.API.setTeamMemberStatus(m.id, newStatus); } catch {} } } async function removeMember(m, e) { e.stopPropagation(); const warn = m.role === 'owner' ? '\n\nWarning: this member is an Owner.' : ''; if (!window.confirm(`Remove ${m.name} permanently? This deletes their team profile and cannot be undone.${warn}`)) return; const prev = team; setTeam(p => p.filter(t => t.id !== m.id)); if (window.API && window.API.deleteTeamMember) { try { const { error } = await window.API.deleteTeamMember(m.id); if (error) { setTeam(prev); alert('Could not remove member:\n\n' + (error.message || error.details || JSON.stringify(error))); } } catch (err) { setTeam(prev); alert('Error removing member: ' + (err.message || JSON.stringify(err))); } } } const departments = [...new Set(team.map(m => m.department).filter(Boolean))]; const maxTasks = Math.max(...team.map(m => taskCounts[m.id] || 0), 1); return (
{team.length} members · {departments.length} departments