/* ============================================================ page-tasks.jsx — Tasks parsed from standup check-in messages ============================================================ */ /* Re-bind parseDetail — set by primitives.jsx */ const parseDetail = window.parseDetail || ((body, status) => (body && (body.detail || body.message)) || `HTTP ${status}`); const STATUS_TABS = [ { id: 'all', label: 'All' }, { id: 'active', label: 'Active' }, // A8 fix: in-progress tasks now visible { id: 'blocked', label: 'Blocked' }, { id: 'stalled', label: 'Stalled' }, { id: 'repeated', label: 'Repeated' }, { id: 'completed', label: 'Completed' }, ]; const STATUS_COLORS = { active: { bg: 'var(--brand-dim, #4a6cf7)', text: '#fff' }, blocked: { bg: 'var(--err)', text: '#fff' }, stalled: { bg: 'var(--warn)', text: '#000' }, repeated: { bg: 'var(--brand)', text: '#fff' }, completed: { bg: 'var(--ok)', text: '#fff' }, }; function StatusPillTask({ s }) { const c = STATUS_COLORS[s] || { bg: 'var(--bg-elev-2)', text: 'var(--text-1)' }; return ( {(window.appT||(s=>s))(s)} ); } function TasksPage() { const [activeTab, setActiveTab] = useState('all'); const [days, setDays] = useState(7); const [data, setData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const toast = useToast ? useToast() : { push: () => {} }; const fetchTasks = async (tab, d) => { setLoading(true); setError(null); try { const r = await fetch(`/api/tasks/by-status?status=${tab}&days=${d}`, { credentials: 'same-origin' }); if (!r.ok) { const body = await r.json().catch(() => null); throw new Error(parseDetail(body, r.status)); } setData(await r.json()); } catch (e) { setError(e.message); toast.push && toast.push({ msg: ((window.appLang&&window.appLang()==='ar') ? `فشل جلب المهام: ${e.message}` : `Tasks fetch failed: ${e.message}`), kind: 'err' }); } finally { setLoading(false); } }; useEffect(() => { fetchTasks(activeTab, days); }, [activeTab, days]); const counts = {}; STATUS_TABS.slice(1).forEach(t => { counts[t.id] = data.filter(r => r.status === t.id).length; }); const displayData = activeTab === 'all' ? data : data.filter(r => r.status === activeTab); return (
{/* Header */}

{(window.appT||(s=>s))('Tasks')}

{(window.appT||(s=>s))('Tasks parsed from standup check-ins — blocked, stalled, repeated, and completed items.')}

{/* Period picker */}
{(window.appT||(s=>s))('Period:')} {[ { label: '1d', value: 1 }, { label: '7d', value: 7 }, { label: '30d', value: 30 }, ].map(opt => ( ))}
{/* Status tabs */}
{STATUS_TABS.map(tab => { const cnt = tab.id === 'all' ? data.length : counts[tab.id]; const isActive = activeTab === tab.id; return ( ); })}
{/* Table */} {loading ? (
{(window.appT||(s=>s))('Loading tasks…')}
) : error ? (
{error}
) : displayData.length === 0 ? (
{(window.appLang&&window.appLang()==='ar') ? `لا توجد مهام ${activeTab === 'all' ? '' : (window.appT||(s=>s))(activeTab) + ' '}في آخر ${days} ${days !== 1 ? 'أيام' : 'يوم'}.` : `No ${activeTab === 'all' ? '' : activeTab + ' '}tasks found in the last ${days} day${days !== 1 ? 's' : ''}.`}
) : (
{['User', 'Task', 'Status', 'First seen', 'Last seen', 'Occurrences'].map(col => ( ))} {displayData.map((row, i) => /* eslint-disable react/no-array-index-key */ ( ))}
{(window.appT||(s=>s))(col)}
{row.display_name} {row.task_text} {row.first_seen} {row.last_seen} 1 ? 'var(--brand)' : 'var(--text-3)', fontWeight: row.occurrences > 1 ? 600 : 400 }}> {row.occurrences}
)}
); } window.TasksPage = TasksPage;