// Tenney North Park — Project DP (TN-10) component. // Standalone data product opened by the Project tile (no dashboard card). // Same DPA anatomy as every other product: Artifact / Data / Control / Context. // Artifact = interactive tracker (Tasks · Timeline · Calendar lenses on one // task table). Content lives in tabs/tenney_project_content.js (window.TN_PROJECT). function TenneyProject() { const P = window.TN_PROJECT; const [face, setFace] = React.useState('artifact'); // artifact | data | control | context const [lens, setLens] = React.useState('tasks'); // tasks | timeline | calendar const [q, setQ] = React.useState(''); const [wsF, setWsF] = React.useState(''); const [stF, setStF] = React.useState(''); const [detail, setDetail] = React.useState(null); // task id | 'new' | null const [collapsed, setCollapsed] = React.useState({}); const [selDay, setSelDay] = React.useState(null); const [tasks, setTasks] = React.useState(P.tasks); // Framework CSS (sheet renderer etc.) — same injection as the dashboard. React.useEffect(() => { if (!document.getElementById('cx-dash-style') && window.CX_DASH) { const st = document.createElement('style'); st.id = 'cx-dash-style'; st.textContent = window.CX_DASH.css; document.head.appendChild(st); } }, []); const today = new Date(P.today + 'T00:00:00'); const parseD = (d) => new Date(d + 'T00:00:00'); const fmtD = (d) => parseD(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); const overdue = (t) => t.status !== 'Complete' && parseD(t.due) < today; const visible = tasks.filter((t) => (!q || t.name.toLowerCase().includes(q.toLowerCase()) || (P.people[t.owner] || '').toLowerCase().includes(q.toLowerCase())) && (!wsF || t.ws === wsF) && (!stF || t.status === stF)); const detailTask = typeof detail === 'number' ? tasks.find((t) => t.id === detail) : null; return (