/* AAU CRM — Giảng viên · hồ sơ + chỉnh sửa phí/buổi + LỊCH THANH TOÁN giảng viên Thù lao tính từ số buổi giảng viên thực dạy × phí/buổi (lấy từ lịch lớp). */ const INS_GROUP_C = id => (AAU.courseGroupById ? (AAU.courseGroupById(id) || {}).color : '#616161') || '#616161'; // các đợt thanh toán cho 1 lớp: tạm ứng 30% (khai giảng) · giữa khóa 40% · kết thúc 30% function insClassPayments(insId) { const fee = (AAU.instructorById(insId) || {}).feePerSession || 0; return AAU.classes .filter(c => c.sessions.some(s => s.instructor === insId)) .map(c => { const taught = c.sessions.filter(s => s.instructor === insId).length; const total = fee * taught; const mid = c.sessions[Math.floor((c.sessions.length - 1) / 2)] || c.sessions[0]; const last = c.sessions[c.sessions.length - 1]; const a1 = Math.round(total * 0.3), a2 = Math.round(total * 0.4), a3 = total - a1 - a2; const milestones = [ { key: c.id + '-0', name: 'Tạm ứng 30%', due: c.start, amount: a1 }, { key: c.id + '-1', name: 'Giữa khóa 40%', due: (mid || {}).date || c.start, amount: a2 }, { key: c.id + '-2', name: 'Kết thúc 30%', due: (last || {}).date || c.start, amount: a3 }, ]; return { cls: c, fee, taught, total, milestones }; }); } function insDueState(due) { const [y, m, d] = due.split('-').map(Number); const days = Math.round((new Date(y, m - 1, d) - AAU.TODAY) / 86400000); if (days < 0) return 'paid'; // quá khứ → coi như đã chi (seed) if (days <= 10) return 'due'; return 'upcoming'; } function InstructorsPage() { const [tick, setTick] = useState(0); const [sel, setSel] = useState(null); // trạng thái thanh toán đã ghi nhận thủ công (ngoài seed theo ngày) const [paidSet, setPaidSet] = useState(() => new Set()); const vndM = AAU.fmtVNDm, vnd = AAU.fmtVND; const isPaid = (key, due) => paidSet.has(key) || (!paidSet.has('!' + key) && insDueState(due) === 'paid'); const markPaid = (key) => setPaidSet(s => { const n = new Set(s); n.add(key); n.delete('!' + key); return n; }); const markUnpaid = (key) => setPaidSet(s => { const n = new Set(s); n.delete(key); n.add('!' + key); return n; }); const data = AAU.instructors.map(ins => { const pays = insClassPayments(ins.id); const total = pays.reduce((s, p) => s + p.total, 0); const paid = pays.reduce((s, p) => s + p.milestones.filter(m => isPaid(m.key, m.due)).reduce((a, m) => a + m.amount, 0), 0); return { ins, pays, total, paid, due: total - paid, classes: pays.length }; }); const sumTotal = data.reduce((s, d) => s + d.total, 0); const sumPaid = data.reduce((s, d) => s + d.paid, 0); const sumDue = sumTotal - sumPaid; const selData = sel && data.find(d => d.ins.id === sel); function saveFee(insId, fee) { const ins = AAU.instructors.find(i => i.id === insId); if (ins) ins.feePerSession = Number(String(fee).replace(/\D/g, '')) || 0; setTick(t => t + 1); } return (