<div style="margin-left:auto;display:flex;gap:10px;align-items:center">
<div id="currentUserBox" class="muted"></div>
<button id="logoutBtn" class="btn-ghost" style="display:none">ออกจากระบบ</button>
</div>
เลือกบัญชี
บทบาท
-
เข้าสู่ระบบ
สร้างบัญชีใหม่
<div id="createRow" style="display:none;margin-top:12px">
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<div style="flex:1;min-width:220px">
<label>ชื่อผู้ใช้</label>
<input id="newName" placeholder="เช่น khonmek, thongfa" />
</div>
<div style="width:160px">
<label>บทบาท</label>
<select id="newRole"><option value="student">นักเรียน</option><option value="teacher">ครู</option><option value="admin">ผู้บริหาร</option></select>
</div>
<div style="display:flex;align-items:flex-end">
<button id="createBtn" class="btn-ghost">สร้าง</button>
</div>
</div>
</div>
<div style="margin-top:10px" class="muted">เลือกบัญชีตัวอย่างหรือสร้างบัญชีใหม่ ข้อมูลเก็บในเครื่อง (localStorage)</div>
<div class="card">
<div style="display:flex;align-items:center;gap:12px">
<div><strong>บันทึกอารมณ์ประจำวัน</strong></div>
<div class="muted">เลือกอิโมจิน่ารักแล้วเขียนบันทึกสั้น ๆ</div>
<div class="right"><span class="badge">LiteVibe</span></div>
</div>
<div style="margin-top:12px">
<label>อารมณ์วันนี้</label>
<div id="studentMoodButtons" class="emoji-row"></div>
</div>
<div style="margin-top:12px">
<label>ข้อความสั้น ๆ / My diary</label>
<textarea id="studentDiaryText" rows="3" placeholder="เล่าเรื่องสั้น ๆ วันนี้เป็นอย่างไร..."></textarea>
</div>
<div style="display:flex;align-items:center;gap:10px;margin-top:10px">
<button id="saveStudentMoodBtn">บันทึกอารมณ์</button>
<div class="muted">บันทึกล่าสุด: <span id="lastStudentMoodText">-</span></div>
</div>
<div class="card" style="margin-top:12px">
<div style="display:flex;align-items:center;gap:10px"><div><strong>สถิติอารมณ์</strong></div><div class="muted">ดูภาพรวม</div></div>
<div class="periods" style="margin-top:10px">
<button class="periodBtn active" data-period="week">สัปดาห์</button>
<button class="periodBtn" data-period="month">เดือน</button>
<button class="periodBtn" data-period="semester">ภาคการศึกษา</button>
</div>
<div class="chart-wrap"><canvas id="moodPeriodChart" height="170"></canvas></div>
<div style="margin-top:10px" class="muted small">กราฟอัปเดตเมื่อบันทึกอารมณ์</div>
</div>
<div class="card" style="margin-top:12px">
<strong>ระบบแลกดาว</strong>
<div style="margin-top:8px;display:flex;gap:8px;flex-wrap:wrap">
<div class="redeem-item">
<div><strong>พัก 5 นาที</strong><div class="meta">ใช้ 10 ⭐</div></div>
<div style="margin-left:auto"><button class="requestRedeemBtn" data-name="พัก 5 นาที" data-cost="10">ขอแลก</button></div>
</div>
<div class="redeem-item">
<div><strong>คูปองเครื่องเขียน</strong><div class="meta">ใช้ 12 ⭐</div></div>
<div style="margin-left:auto"><button class="requestRedeemBtn" data-name="คูปองเครื่องเขียน" data-cost="12">ขอแลก</button></div>
</div>
<div class="redeem-item">
<div><strong>คูปองอาหาร/เครื่องดื่ม</strong><div class="meta">ใช้ 15 ⭐</div></div>
<div style="margin-left:auto"><button class="requestRedeemBtn" data-name="คูปองอาหาร" data-cost="15">ขอแลก</button></div>
</div>
</div>
<h4 style="margin-top:12px">ประวัติการแลก</h4>
<div id="studentRedeemHistory" class="list" style="margin-top:8px"></div>
<h4 style="margin-top:12px">คำขอแลกที่ส่ง (สถานะ)</h4>
<div id="studentRedeemRequests" class="list" style="margin-top:8px"></div>
</div>
<div class="card" style="margin-top:12px">
<strong>นัดหมายปรึกษา</strong>
<div style="margin-top:8px">
<label>เลือกครูที่ต้องการนัด</label>
<select id="apptTeacherSelect"></select>
<label style="margin-top:8px">ข้อความสำหรับนัด</label>
<input id="apptMsg" placeholder="สาเหตุ/หัวข้อที่ต้องการปรึกษา" />
<div style="margin-top:8px"><button id="requestAppt">ส่งคำขอนัด</button></div>
<div style="margin-top:10px" class="muted small">สถานะคำขอแสดงเป็นสีชัดเจน</div>
<div id="apptHistory" class="list" style="margin-top:8px"></div>
</div>
</div>
<div class="card" style="margin-top:12px">
<strong>ประวัติ My diary</strong>
<div id="studentDiaryHistory" class="list" style="margin-top:8px"></div>
</div>
</div>
</div>
<!-- TEACHER PANEL -->
<div id="teacherPanel" style="display:none">
<div class="card">
<div style="display:flex;align-items:center;gap:12px">
<div><strong>แผงครู</strong></div>
<div class="muted">ดูสถิติ อนุมัติคำขอแลกดาว และนักเรียนเสี่ยง</div>
</div>
</div>
<div class="card" style="margin-top:12px">
<div style="display:flex;gap:12px;align-items:center;flex-wrap:wrap">
<div>
<label>โหมดสถิติ</label>
<div class="segmented" style="margin-top:6px">
<button class="teacherModeBtn active" data-mode="student">รายบุคคล</button>
<button class="teacherModeBtn" data-mode="class">รายห้องเรียน</button>
<button class="teacherModeBtn" data-mode="grade">รายชั้นปี</button>
</div>
</div>
<div style="flex:1">
<label id="teacherSelectLabel">เลือกนักเรียน</label>
<select id="teacherSelect"></select>
</div>
<div style="min-width:140px">
<label>ช่วงเวลา</label>
<select id="teacherPeriod"><option value="week">สัปดาห์</option><option value="month">เดือน</option><option value="semester">ภาคการศึกษา</option></select>
</div>
<div style="display:flex;align-items:center;gap:8px">
<!-- colorful icon buttons for stats -->
<div class="icon-row">
<button class="icon-btn yellow" id="statIcon1" title="แสดงสถิติ"><div class="ico">📊</div><div class="icon-label">สรุป</div></button>
<button class="icon-btn pink" id="statIcon2" title="แสดงสถิติรายวัน"><div class="ico">📈</div><div class="icon-label">รายวัน</div></button>
<button class="icon-btn blue" id="statIcon3" title="แสดงเทรนด์"><div class="ico">📅</div><div class="icon-label">เทรนด์</div></button>
</div>
</div>
</div>
<div style="margin-top:12px" class="chart-wrap">
<canvas id="teacherDetailChart" height="200"></canvas>
</div>
<div class="muted small" style="margin-top:8px">กดไอคอนสีสันด้านบนเพื่อแสดงกราฟ (จะใช้โหมดและตัวเลือกปัจจุบัน)</div>
</div>
<div class="card" style="margin-top:12px">
<h4>คำขอแลกดาวจากนักเรียน</h4>
<div id="teacherRedeemRequests" class="list"></div>
</div>
<div class="card" style="margin-top:12px">
<h4>นักเรียนที่มีความเสี่ยง (Emotion & Behavior)</h4>
<div id="teacherRiskList" class="list"></div>
</div>
<div class="card" style="margin-top:12px">
<h4>กล่องคำขอนัดจากนักเรียน</h4>
<div id="apptRequests" class="list"></div>
</div>
</div>
<!-- ADMIN PANEL -->
<div id="adminPanel" style="display:none">
<div class="card">
<div style="display:flex;align-items:center;gap:12px">
<div><strong>แดชบอร์ดผู้บริหาร</strong></div>
<div class="muted">สถิติภาพรวมโรงเรียน และนักเรียนที่มีความเสี่ยง</div>
</div>
</div>
<div class="card" style="margin-top:12px">
<h4>สรุปอารมณ์นักเรียน (ภาพรวม)</h4>
<div class="chart-wrap"><canvas id="adminMoodChart" height="140"></canvas></div>
</div>
<div class="card" style="margin-top:12px">
<h4>นักเรียนที่มีความเสี่ยง (ภาพรวมโรงเรียน)</h4>
<div id="adminRiskList" class="list"></div>
</div>
<div class="card" style="margin-top:12px">
<h4>สรุปพฤติกรรม / รายงาน</h4>
<div style="display:flex;gap:12px;align-items:center">
<div>
<div class="small muted">จำนวนรายงานรวม</div>
<div id="adminTotalReports" style="font-weight:700;font-size:18px;margin-top:6px">0</div>
</div>
<div>
<div class="small muted">จำนวนการแลกของรางวัลรวม</div>
<div id="adminTotalRedeems" style="font-weight:700;font-size:18px;margin-top:6px">0</div>
</div>
<div>
<div class="small muted">จำนวนนักเรียนทั้งหมด</div>
<div id="adminTotalStudents" style="font-weight:700;font-size:18px;margin-top:6px">0</div>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="card">
<div style="display:flex;align-items:center;gap:12px">
<div class="student-avatar" id="profileAvatar"></div>
<div>
<div id="profileName"><strong>-</strong></div>
<div id="profileRole" class="meta">-</div>
<div id="profileClass" class="meta" style="margin-top:6px"></div>
</div>
<div style="margin-left:auto"><span class="badge" id="profileStarsWrap">⭐ <span id="profileStars">0</span></span></div>
</div>
<div id="profileBox" style="margin-top:12px"></div>
<div style="margin-top:12px">
<label>อัปโหลดรูปประจำตัว (จำลอง)</label>
<input type="file" id="avatarInput" accept="image/*" />
<div style="margin-top:8px"><button id="removeAvatar" class="btn-ghost">ลบรูปประจำตัว</button></div>
<div class="meta" style="margin-top:8px">รูปจะถูกเก็บในเครื่อง (localStorage)</div>
</div>
</div>
<div class="card" style="margin-top:12px">
<h4>แดชบอร์ดด่วน</h4>
<div id="quickPanel"></div>
</div>
<div class="card" style="margin-top:12px">
<h4>กิจกรรมล่าสุด (Log)</h4>
<div id="activityLog" class="list"></div>
</div>
</div>
</div>
<footer style="margin-top:16px" class="muted">LiteVibe — Prototype (เก็บข้อมูลในเครื่อง)</footer>
บันทึกล่าสุด:
`;
if(u.moods && u.moods.length){ const last = u.moods[u.moods.length-1]; html += `${last.time} — ${last.emoji} ${last.label}
${last.note || '-'}
`; }
else html += `ยังไม่มีบันทึก
`;
box.innerHTML = html;
}
/* ---------- Mood UI & Save ---------- */
function renderMoodButtons(containerId){
const container = document.getElementById(containerId);
if(!container) return;
container.innerHTML = '';
emojiChoices.forEach(e=>{
const btn = document.createElement('button'); btn.className = 'emoji-btn'; btn.dataset.key = e.key; btn.innerHTML = `${e.emoji}
${e.label}
`;
btn.style.background = `linear-gradient(180deg, rgba(255,255,255,1), ${hexToRgba(e.color,0.06)})`;
btn.addEventListener('click', ()=>{ Array.from(container.querySelectorAll('.emoji-btn')).forEach(b=>b.classList.remove('selected')); btn.classList.add('selected'); });
container.appendChild(btn);
});
}
renderMoodButtons('studentMoodButtons');
renderMoodButtons('teacherMoodButtons');
document.getElementById('saveStudentMoodBtn').addEventListener('click', ()=>{
if(!currentUser) return alert('กรุณาเข้าสู่ระบบ');
const sel = document.querySelector('#studentMoodButtons .emoji-btn.selected'); if(!sel) return alert('กรุณาเลือกรูปอารมณ์');
const key = sel.dataset.key; const meta = emojiChoices.find(x=>x.key===key);
const note = document.getElementById('studentDiaryText').value.trim();
const now = new Date(); const entry = { iso: now.toISOString(), time: now.toLocaleString(), key, emoji: meta.emoji, label: meta.label, note };
const u = state.users[currentUser]; u.moods = u.moods || []; u.moods.push(entry);
if(note){ u.diaries = u.diaries || []; u.diaries.push({time:entry.time, text:note}); }
saveState(); logActivity(`${currentUser} (นักเรียน) บันทึกอารมณ์: ${meta.emoji} ${meta.label}`); document.getElementById('studentDiaryText').value=''; Array.from(document.querySelectorAll('#studentMoodButtons .emoji-btn')).forEach(b=>b.classList.remove('selected')); renderAll();
});
document.getElementById('saveTeacherMoodBtn')?.addEventListener('click', ()=>{
if(!currentUser) return alert('กรุณาเข้าสู่ระบบ');
const sel = document.querySelector('#teacherMoodButtons .emoji-btn.selected'); if(!sel) return alert('กรุณาเลือกรูปอารมณ์สำหรับครู');
const key = sel.dataset.key; const meta = emojiChoices.find(x=>x.key===key);
const note = document.getElementById('teacherDiaryText')?.value.trim() || '';
const now = new Date(); const entry = { iso: now.toISOString(), time: now.toLocaleString(), key, emoji: meta.emoji, label: meta.label, note };
const u = state.users[currentUser]; u.moods = u.moods || []; u.moods.push(entry);
if(note){ u.diaries = u.diaries || []; u.diaries.push({time:entry.time, text:note}); }
saveState(); logActivity(`${currentUser} (ครู) บันทึกอารมณ์: ${meta.emoji} ${meta.label}`); if(document.getElementById('teacherDiaryText')) document.getElementById('teacherDiaryText').value=''; Array.from(document.querySelectorAll('#teacherMoodButtons .emoji-btn')).forEach(b=>b.classList.remove('selected')); renderAll();
});
/* ---------- Redeem: student requests ---------- */
document.addEventListener('click', (e)=>{
if(e.target && e.target.matches('.requestRedeemBtn')){
if(!currentUser) return alert('กรุณาเข้าสู่ระบบ');
const name = e.target.dataset.name; const cost = parseInt(e.target.dataset.cost);
const u = state.users[currentUser];
if((u.stars||0) < cost){
if(!confirm('ดาวของคุณไม่เพียงพอสำหรับการแลกนี้ ต้องการส่งคำขอและรออนุมัติหรือไม่?')) return;
}
const req = { id: generateId(), student: currentUser, item: name, cost, time: new Date().toLocaleString(), iso: new Date().toISOString(), status:'pending', approvedBy:'', note:'' };
state.redeemRequests = state.redeemRequests || []; state.redeemRequests.push(req);
saveState(); logActivity(`${currentUser} ขอแลก: ${name} (${cost} ⭐)`); alert('ส่งคำขอแลกเรียบร้อย รอการอนุมัติจากครู'); renderAll();
}
});
/* render student's redeem history & pending requests */
function renderStudentRedeems(){
if(!currentUser) return;
const u = state.users[currentUser];
const historyEl = document.getElementById('studentRedeemHistory');
const requestsEl = document.getElementById('studentRedeemRequests');
if(historyEl){
const hist = (u.redeemHistory||[]).slice().reverse();
historyEl.innerHTML = hist.length ? hist.map(h=>`${h.item} (${h.cost} ⭐)
${h.time} — โดย ${h.approvedBy||'ระบบ'}
ยังไม่มีประวัติการแลก
';
}
if(requestsEl){
const myReqs = (state.redeemRequests||[]).filter(r=>r.student === currentUser).slice().reverse();
if(!myReqs.length){ requestsEl.innerHTML = 'ยังไม่มีคำขอแลก
'; return; }
requestsEl.innerHTML = myReqs.map(r=>`${r.item} (${r.cost} ⭐)
ส่ง: ${r.time}
${r.status==='pending'? 'รออนุมัติ' : r.status==='approved'? 'อนุมัติ' : 'ไม่อนุมัติ'} ${r.approvedBy? ' โดย ' + r.approvedBy : ''}
ยังไม่มีการขอนัด
'; return; }
el.innerHTML = u.appts.slice().reverse().map(a=>{
const cls = a.status === 'pending' ? 'status-pending' : (a.status === 'approved' ? 'status-approved' : 'status-rejected');
const label = a.status === 'pending' ? 'รออนุมัติ' : (a.status === 'approved' ? 'อนุมัติ' : 'ปฏิเสธ');
return `${a.time} → ถึง: ${a.teacher}
${a.msg}
${label} หมายเหตุครู: ${a.teacherNote || '-'}
ยังไม่มีคำขอแลกจากนักเรียน
'; return; }
el.innerHTML = pending.slice().reverse().map(r=>{
const s = state.users[r.student];
const avatar = s && s.avatar ? `` : `${(s? s.display : r.student).slice(0,2).toUpperCase()}
`;
return `
${avatar}
`;
}).join('');
document.querySelectorAll('.approveRedeemBtn').forEach(b=>b.addEventListener('click', (e)=> handleApproveRedeem(e.target.dataset.id)));
document.querySelectorAll('.rejectRedeemBtn').forEach(b=>b.addEventListener('click', (e)=> handleRejectRedeem(e.target.dataset.id)));
}
function handleApproveRedeem(id){
if(!currentUser) return alert('กรุณาเข้าสู่ระบบเป็นครูเพื่ออนุมัติ');
const req = (state.redeemRequests||[]).find(r=>r.id === id);
if(!req) return alert('ไม่พบคำขอ');
const student = state.users[req.student];
if(!student) return alert('ไม่พบข้อมูลนักเรียน');
if((student.stars || 0) < req.cost){
if(!confirm(`นักเรียนมีดาวไม่พอ (${student.stars||0} ⭐) จะอนุมัติแล้วหักดาวลงไปหรือไม่?`)) return;
}
student.stars = Math.max(0, (student.stars || 0) - req.cost);
student.redeemHistory = student.redeemHistory || [];
student.redeemHistory.push({ item: req.item, cost: req.cost, time: new Date().toLocaleString(), approvedBy: state.users[currentUser].display || currentUser });
req.status = 'approved';
req.approvedBy = state.users[currentUser].display || currentUser;
saveState(); logActivity(`${currentUser} อนุมัติการแลกของรางวัลของ ${student.name}: ${req.item} (-${req.cost}⭐)`); alert('อนุมัติคำขอเรียบร้อยแล้ว'); renderAll();
}
function handleRejectRedeem(id){
if(!currentUser) return alert('กรุณาเข้าสู่ระบบเป็นครูเพื่ออนุมัติ/ปฏิเสธ');
const req = (state.redeemRequests||[]).find(r=>r.id === id);
if(!req) return alert('ไม่พบคำขอ');
req.status = 'rejected';
req.approvedBy = state.users[currentUser].display || currentUser;
saveState(); logActivity(`${currentUser} ปฏิเสธการแลกของ ${req.student}: ${req.item}`); alert('ปฏิเสธคำขอเรียบร้อยแล้ว'); renderAll();
}
/* ---------- Teacher controls & icon buttons ---------- */
function renderTeacherControls(){
const modeBtns = document.querySelectorAll('.teacherModeBtn');
modeBtns.forEach(b=>b.addEventListener('click', ()=>{
modeBtns.forEach(x=>x.classList.remove('active')); b.classList.add('active'); updateTeacherSelectLabel();
}));
updateTeacherSelectLabel();
// icon buttons: use current mode and selection
document.getElementById('statIcon1').addEventListener('click', ()=> {
const mode = document.querySelector('.teacherModeBtn.active')?.dataset.mode || 'student';
const id = document.getElementById('teacherSelect')?.value;
const period = document.getElementById('teacherPeriod')?.value || 'week';
renderTeacherDetail(mode, id, period);
});
document.getElementById('statIcon2').addEventListener('click', ()=> {
const mode = document.querySelector('.teacherModeBtn.active')?.dataset.mode || 'student';
const id = document.getElementById('teacherSelect')?.value;
// force 'month' view for this icon
renderTeacherDetail(mode, id, 'month');
});
document.getElementById('statIcon3').addEventListener('click', ()=> {
const mode = document.querySelector('.teacherModeBtn.active')?.dataset.mode || 'student';
const id = document.getElementById('teacherSelect')?.value;
// force 'semester' view for this icon
renderTeacherDetail(mode, id, 'semester');
});
document.getElementById('teacherViewBtn')?.addEventListener('click', ()=> { // for backward compatibility if present
const mode = document.querySelector('.teacherModeBtn.active')?.dataset.mode || 'student';
const id = document.getElementById('teacherSelect')?.value;
const period = document.getElementById('teacherPeriod')?.value || 'week';
renderTeacherDetail(mode, id, period);
});
}
function updateTeacherSelectLabel(){
const mode = document.querySelector('.teacherModeBtn.active')?.dataset.mode || 'student';
const label = document.getElementById('teacherSelectLabel');
const sel = document.getElementById('teacherSelect');
if(!label || !sel) return;
sel.innerHTML = '';
if(mode === 'student'){
label.innerText = 'เลือกนักเรียน';
Object.values(state.users).filter(u=>u.role==='student').forEach(s=> {
const opt = document.createElement('option'); opt.value = s.name; opt.innerText = `${s.display||s.name} • ${s.classId || ''} ${s.grade || ''}`; sel.appendChild(opt);
});
} else if(mode === 'class'){
label.innerText = 'เลือกห้องเรียน';
const classes = Array.from(new Set(Object.values(state.users).filter(u=>u.role==='student').map(s=>s.classId || 'ไม่ระบุ')));
classes.forEach(c=>{ const opt = document.createElement('option'); opt.value = c; opt.innerText = c; sel.appendChild(opt); });
} else {
label.innerText = 'เลือกชั้นปี';
const grades = Array.from(new Set(Object.values(state.users).filter(u=>u.role==='student').map(s=>s.grade || 'ไม่ระบุ')));
grades.forEach(g=>{ const opt = document.createElement('option'); opt.value = g; opt.innerText = g; sel.appendChild(opt); });
}
}
function renderTeacherDetail(mode, id, period){
let students = [];
if(mode === 'student'){
if(!id) { alert('กรุณาเลือกนักเรียน'); return; }
const s = state.users[id]; if(!s) return alert('ไม่พบข้อมูลนักเรียน');
students = [s];
} else if(mode === 'class'){
students = Object.values(state.users).filter(u=>u.role==='student' && (u.classId === id));
} else {
students = Object.values(state.users).filter(u=>u.role==='student' && (u.grade === id));
}
const combined = []; students.forEach(s=> { if(s.moods) combined.push(...s.moods); });
const agg = aggregateByPeriod(combined, period);
const datasets = emojiChoices.map(e=>({ label:e.label, data: agg.data.map(d=>d[e.label]||0), backgroundColor: hexToRgba(e.color,0.95), stack:'s1' }));
const ctx = document.getElementById('teacherDetailChart').getContext('2d');
if(teacherDetailChart) teacherDetailChart.destroy();
teacherDetailChart = new Chart(ctx, { type:'bar', data:{ labels: agg.labels, datasets }, options:{ responsive:true, plugins:{legend:{position:'bottom'}}, scales:{ x:{stacked:true}, y:{stacked:true, beginAtZero:true, ticks:{precision:0}} } } });
}
/* ---------- Risk detection & lists (unchanged) ---------- */
function studentRiskInfo(student){
const reasons = [];
const now = new Date();
const moods = student.moods || [];
const negativeLabels = ['เศร้า','โกรธ','เหนื่อย'];
const sevenDaysAgo = new Date(); sevenDaysAgo.setDate(now.getDate()-7);
const recent = moods.filter(m => m.iso && new Date(m.iso) >= sevenDaysAgo);
const negCount = recent.reduce((acc,m)=> acc + (negativeLabels.includes(m.label) ? 1 : 0), 0);
if(negCount >= 2) reasons.push(`มี ${negCount} ครั้งของอารมณ์เชิงลบใน 7 วันล่าสุด`);
const rptCount = (student.reports || []).length;
if(rptCount >= 1) reasons.push(`มี ${rptCount} รายงานพฤติกรรม`);
const q = (student.quiz || []).slice(-3);
const lowRecent = q.filter(x=>x.score !== undefined && x.score <= 1).length;
if(lowRecent >= 1) reasons.push(`คะแนนแบบทดสอบล่าสุดต่ำ (${lowRecent} ครั้ง)`);
let level = null;
if(reasons.length >= 2) level = 'high';
else if(reasons.length === 1) level = 'medium';
else level = null;
return { level, reasons };
}
function buildRiskLists(){
const students = Object.values(state.users).filter(u=>u.role==='student');
const risks = students.map(s => ({ student: s, info: studentRiskInfo(s) })).filter(x => x.info.level);
risks.sort((a,b)=> { const score = l => l==='high' ? 2 : (l==='medium' ? 1 : 0); return score(b.info.level) - score(a.info.level); });
return risks;
}
function renderTeacherRiskList(){
const el = document.getElementById('teacherRiskList'); if(!el) return;
const risks = buildRiskLists();
if(!risks.length){ el.innerHTML = '${s? s.display : r.student} ${s? (s.classId || '') + ' • ' + (s.grade || '') : ''}
${r.item} — ${r.cost} ⭐
ส่ง: ${r.time}
อนุมัติ
ไม่อนุมัติ
ยังไม่มีนักเรียนที่อยู่ในเกณฑ์เสี่ยง
'; return; }
el.innerHTML = risks.map(r=>{ const s = r.student; const avatar = s.avatar ? `` : `${(s.display||s.name).slice(0,2).toUpperCase()}
`; const levelClass = r.info.level === 'high' ? 'risk-high' : 'risk-medium'; const reasons = r.info.reasons.join(' • '); return `${avatar}
`; }).join('');
document.querySelectorAll('.viewProfileBtn').forEach(b=>b.addEventListener('click', e=> viewStudentProfile(e.target.dataset.name)));
}
function renderAdminRiskList(){
const el = document.getElementById('adminRiskList'); if(!el) return;
const risks = buildRiskLists();
if(!risks.length){ el.innerHTML = '${s.display || s.name} ${s.classId || '-'} • ${s.grade || '-'}
${reasons}
${r.info.level === 'high' ? 'เสี่ยงสูง' : 'เสี่ยงปานกลาง'}
ดูโปรไฟล์
ยังไม่มีนักเรียนที่อยู่ในเกณฑ์เสี่ยง
'; return; }
el.innerHTML = risks.map(r=>{ const s = r.student; const levelClass = r.info.level === 'high' ? 'risk-high' : 'risk-medium'; const reasons = r.info.reasons.join(' • '); return `${s.display || s.name}
${s.classId || '-'} • ${s.grade || '-'}
${reasons}
${r.info.level === 'high' ? 'เสี่ยงสูง' : 'เสี่ยงปานกลาง'}
ไม่มีนักเรียน
'; return; } container.innerHTML = students.map(s=>{ const avatarHtml = s.avatar ? `` : `${(s.display||s.name).slice(0,2).toUpperCase()}
`; return `${avatarHtml}
`; }).join(''); }
document.getElementById('searchStudent')?.addEventListener('input', renderStudentsList);
/* appt inbox for teacher (kept) */
function renderApptRequests(){ const el = document.getElementById('apptRequests'); if(!currentUser) return; const inbox = (state.users[currentUser].inbox || []); if(!inbox.length){ el.innerHTML = '${s.display||s.name}
${s.classId || '-'} • ${s.grade || '-'}
ยังไม่มีคำขอนัด
'; return; } el.innerHTML = inbox.map(a=>`${a.time} — จาก: ${a.student}
${a.msg}
${a.status==='approved'?'อนุมัติ':`อนุมัติปฏิเสธ`} หมายเหตุ
ยังไม่มีรายงาน
'; return; } el.innerHTML = all.map(r=>`${r.time} → ${r.student}
${r.text}
บทบาท: ${u.role}
`; if(u.role==='student'){ html += `ดาว: ${u.stars || 0}
`; html += `บันทึก: ${(u.diaries||[]).length} ครั้ง
`; } else if(u.role === 'teacher'){ const pending = (u.inbox||[]).filter(i=>i.status==='pending').length; html += `คำขอนัดรออนุมัติ: ${pending}
`; } else if(u.role === 'admin'){ const students = Object.values(state.users).filter(x=>x.role==='student').length; html += `นักเรียนทั้งหมด: ${students}
`; } el.innerHTML = html; }
function renderActivity(){ const el = document.getElementById('activityLog'); el.innerHTML = state.activity.map(a=>`${a.time}
${a.txt}
ยังไม่มีบันทึก My diary
'; return; }
el.innerHTML = user.diaries.slice().reverse().map(d=>`${d.time}
${escapeHtml(d.text)}