// Alternative views — Mijn dag, Per klant, Lijst, Team
const { useMemo: useMemoViews, useState: useStateViews } = React;
// ═══ MIJN DAG ═══════════════════════════════════════════
const MijnDag = ({ onPick }) => {
const tasks = window.getMyTasks(window.CURRENT_USER_ID);
const today = window.iso(window.today);
const tomorrow = window.iso(window.addDays(window.today, 1));
const groupBy = (pred) => tasks.filter(pred);
const overdue = groupBy(t => window.daysUntil(t.deadline) < 0);
const todayT = groupBy(t => t.deadline === today);
const tomor = groupBy(t => t.deadline === tomorrow);
const week = groupBy(t => {
const d = window.daysUntil(t.deadline);
return d > 1 && d <= 7;
});
const later = groupBy(t => window.daysUntil(t.deadline) > 7);
const u = window.medewerkerById(window.CURRENT_USER_ID);
return (
{/* Greeting banner */}
ZATERDAG · 18 APRIL 2026
Goedemorgen, Sanne.
Je hebt {todayT.length} taken voor vandaag
{overdue.length > 0 && <>, en {overdue.length} stap{overdue.length>1?'pen':''} loopt te laat>}.
{overdue.length > 0 && (
)}
);
};
const StatTile = ({ label, value, tone }) => {
const c = tone === 'hot' ? 'oklch(0.55 0.18 30)'
: tone === 'alert' ? 'oklch(0.48 0.18 25)'
: 'var(--ink-1)';
return (
{label.toUpperCase()}
{value}
);
};
const TaskSection = ({ title, tasks, onPick, tone, defaultOpen = true }) => {
const [open, setOpen] = useStateViews(defaultOpen);
if (tasks.length === 0) return null;
return (
{open && (
{tasks.map((t, i) => )}
)}
);
};
const TaskRow = ({ t, onPick }) => {
const p = window.PROJECTEN.find(x => x.id === t.projectId);
const b = window.bucketById(t.bucketId);
const klant = window.klantById(t.klant);
const overdue = window.daysUntil(t.deadline) < 0;
return (
onPick(p)} style={{
display: 'flex', alignItems: 'center', gap: 12,
padding: '11px 14px', background: 'var(--paper)',
border: '1px solid var(--line)', borderLeft: `3px solid ${overdue ? 'oklch(0.65 0.18 25)' : klant.kleur}`,
borderRadius: 8, cursor: 'pointer',
}}>
e.stopPropagation()} />
{b.naam}
{t.projectId}
·
{t.projectNaam}
·
{klant.naam}
);
};
// ═══ PER KLANT ═══════════════════════════════════════════
const PerKlant = ({ onPick }) => {
const [expanded, setExpanded] = useStateViews(() => new Set(['hak','remia']));
const grouped = useMemoViews(() => {
const m = {};
window.PROJECTEN.forEach(p => {
if (p.huidigeBucket === 'afgerond' || p.huidigeBucket === 'niet-ok') return;
(m[p.klant] = m[p.klant] || []).push(p);
});
return m;
}, []);
const klantIds = Object.keys(grouped).sort((a, b) => grouped[b].length - grouped[a].length);
return (
{klantIds.map(kid => {
const klant = window.klantById(kid);
const projs = grouped[kid];
const isOpen = expanded.has(kid);
const toggle = () => {
const s = new Set(expanded);
s.has(kid) ? s.delete(kid) : s.add(kid);
setExpanded(s);
};
const urgent = projs.filter(p => p.prio === 'dringend').length;
return (
{isOpen && (
)}
);
})}
);
};
const KlantProjectTable = ({ projs, onPick, klantKleur }) => {
return (
{projs.map(p => (
onPick(p)} style={{
padding: '12px 4px', borderBottom: '1px solid var(--line)',
cursor: 'pointer', display: 'flex', flexDirection: 'column', gap: 8,
}}>
{p.id}
{p.naam}
deadline {window.fmtDate(p.einde)}
{p.sauzen.map((s, i) => {
const b = window.bucketById(s.bucketId);
const prog = window.sausProgress(p, s.bucketId);
return (
);
})}
))}
);
};
// ═══ LIJST ══════════════════════════════════════════════
const Lijst = ({ onPick }) => {
const [sort, setSort] = useStateViews('deadline');
const projs = useMemoViews(() => {
const arr = [...window.PROJECTEN];
arr.sort((a, b) => {
if (sort === 'deadline') return a.einde.localeCompare(b.einde);
if (sort === 'klant') return window.klantById(a.klant).naam.localeCompare(window.klantById(b.klant).naam);
if (sort === 'prio') {
const order = { dringend: 0, belangrijk: 1, gemiddeld: 2, laag: 3 };
return order[a.prio] - order[b.prio];
}
return 0;
});
return arr;
}, [sort]);
return (
Sorteer:
{['deadline','klant','prio'].map(s => (
))}
PROJECT ID
NAAM
KLANT
HUIDIGE STAP
PRIO
TEAM
DEADLINE
{projs.map(p => {
const klant = window.klantById(p.klant);
const b = window.bucketById(p.huidigeBucket);
return (
onPick(p)} style={{
display: 'grid',
gridTemplateColumns: '98px 1.6fr 1fr 180px 110px 90px 120px',
padding: '10px 16px', fontSize: 12.5,
borderBottom: '1px solid var(--line)', cursor: 'pointer',
alignItems: 'center',
}}
onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-1)'}
onMouseLeave={e => e.currentTarget.style.background = 'var(--paper)'}
>
{p.id}
{p.naam}
{klant.naam}
{b.naam}
);
})}
);
};
// ═══ TEAM WORKLOAD ══════════════════════════════════════
const Team = ({ onPick }) => {
const workload = useMemoViews(() => {
const m = {};
window.MEDEWERKERS.forEach(med => m[med.id] = { open: 0, overdue: 0, deze_week: 0, vandaag: 0 });
window.PROJECTEN.forEach(p => {
Object.entries(p.plan).forEach(([bid, step]) => {
if (step.done || !step.owner) return;
const d = window.daysUntil(step.deadline);
const bucket = m[step.owner];
if (!bucket) return;
bucket.open++;
if (d < 0) bucket.overdue++;
if (d === 0) bucket.vandaag++;
if (d >= 0 && d <= 7) bucket.deze_week++;
});
});
return m;
}, []);
const maxOpen = Math.max(...Object.values(workload).map(w => w.open), 1);
return (
Openstaande stappen per medewerker, gesorteerd op bezetting. Rode balk = te laat.
{window.MEDEWERKERS
.map(m => ({ m, w: workload[m.id] }))
.sort((a, b) => b.w.open - a.w.open)
.map(({ m, w }) => (
{w.overdue > 0 && (
)}
{w.vandaag > 0 && (
)}
{Math.max(w.open - w.overdue - w.vandaag, 0) > 0 && (
)}
))}
);
};
Object.assign(window, { MijnDag, PerKlant, Lijst, Team });