import React,{useMemo,useState,useEffect}from'react';import{createRoot}from'react-dom/client';import{Search,Bell,User,LayoutDashboard,CalendarDays,ClipboardCheck,Users,Settings,Plus,FileDown,Save,Lock,Unlock,Database,ShieldCheck,BarChart3,CheckCircle2,AlertTriangle,Upload,History,Filter,ChevronRight,School}from'lucide-react';import'./style.css';
const APP='THCS Trưng Vương Đà Nẵng';const USE_FIREBASE=false;
const LS='tvt_eval_enterprise_v2';
const ranks=[{id:'HTXSNV',name:'Hoàn thành xuất sắc',min:90,color:'blue'},{id:'HTTNV',name:'Hoàn thành tốt',min:75,color:'green'},{id:'HTNV',name:'Hoàn thành nhiệm vụ',min:50,color:'amber'},{id:'KHTNV',name:'Không hoàn thành',min:0,color:'red'}];
const seed={settings:{year:'2025-2026',period:'Tháng 02/2026',locked:false,quotaTeam:10,quotaCouncil:10,plusMax:6,school:APP},roles:['Admin','Hiệu trưởng','Phó Hiệu trưởng','Hội đồng','Tổ trưởng','Giáo viên','Nhân viên','Kế toán'],departments:['Ban giám hiệu','Tổ Toán - Tin','Tổ Ngữ Văn','Tổ Ngoại Ngữ','Tổ KHTN','Tổ KHXH','Văn phòng'],criteria:[{g:'Tiêu chí chung',max:30,items:[['Phẩm chất chính trị, đạo đức, lối sống',10],['Ý thức kỷ luật, kỷ cương',5],['Năng lực chuyên môn, nghiệp vụ',10],['Thái độ phục vụ và phối hợp',5]]},{g:'Kết quả thực hiện nhiệm vụ',max:70,items:[['Khối lượng công việc/sản phẩm hoàn thành',20],['Tiến độ hoàn thành nhiệm vụ',20],['Chất lượng sản phẩm, hồ sơ, bài giảng',30]]}],staff:[['EP-0001','Nguyễn Văn An','Tổ Toán - Tin','Giáo viên','an.nv@trungvuong.edu.vn',98.5,'HTXSNV'],['EP-0002','Trần Thị Bích','Tổ Ngữ Văn','Tổ trưởng','bich.tt@trungvuong.edu.vn',95,'HTXSNV'],['EP-0003','Lê Hoàng Nam','Tổ Ngoại Ngữ','Giáo viên','nam.lh@trungvuong.edu.vn',88.5,'HTTNV'],['EP-0004','Phạm Văn Dũng','Tổ KHTN','Giáo viên','dung.pv@trungvuong.edu.vn',76,'HTTNV'],['EP-0005','Đặng Thị Hoa','Văn phòng','Nhân viên','hoa.dt@trungvuong.edu.vn',68,'HTNV']].map((x,i)=>({id:x[0],name:x[1],dept:x[2],position:x[3],email:x[4],phone:'090 • 345 • 67'+(80+i),score:x[5],rank:x[6],status:i<3?'Đã tự đánh giá':'Chưa nộp',avatar:x[1].split(' ').slice(-2).map(w=>w[0]).join(''),party:i%2?'Không':'Đảng viên'})),tasks:[['Chuẩn bị giáo án môn Toán Đại số khối 8','Giáo án hoàn thiện','08/02 - 10/02','12h','Đã xong'],['Chấm bài kiểm tra giữa kì khối 9','Kết quả chấm bài','11/02 - 13/02','8h','Đã xong'],['Viết sáng kiến kinh nghiệm ứng dụng CNTT','Bản thảo sáng kiến','01/02 - 28/02','20h','Đang làm'],['Báo cáo chuyên đề đổi mới phương pháp','Báo cáo tổng kết','14/02','4h','Muộn'],['Dự giờ đồng nghiệp','Phiếu đánh giá dự giờ','09/02','2h','Đã xong']].map((x,i)=>({id:'T'+(i+1),name:x[0],product:x[1],time:x[2],hours:x[3],status:x[4],owner:i%2?'Trần Thị Bích':'Nguyễn Văn An',team:i%2?'Tổ Ngữ Văn':'Tổ Toán - Tin',quality:i===3?'Chưa đảm bảo':'Đảm bảo'})),evaluations:[],audits:[]};
function load(){try{return JSON.parse(localStorage.getItem(LS))||seed}catch{return seed}}function save(d){localStorage.setItem(LS,JSON.stringify(d))}function rankOf(s){return ranks.find(r=>s>=r.min)?.id||'KHTNV'}function pct(n,d){return d?Math.round(n*100/d):0}function addAudit(data,action,detail){return{...data,audits:[{time:new Date().toLocaleString('vi-VN'),user:'Admin',action,detail},...data.audits].slice(0,200)}}
function App(){const[data,setData]=useState(load);const[page,setPage]=useState('dashboard');const[q,setQ]=useState('');const[modal,setModal]=useState(false);useEffect(()=>save(data),[data]);const stats=useMemo(()=>{let s=data.staff;return{total:s.length,submitted:s.filter(x=>x.status==='Đã tự đánh giá').length,excellent:s.filter(x=>x.rank==='HTXSNV').length,good:s.filter(x=>x.rank==='HTTNV').length,avg:Math.round(s.reduce((a,b)=>a+b.score,0)/s.length)}} ,[data]);const menu=[['dashboard','Dashboard',LayoutDashboard],['staff','Hồ sơ cán bộ',User],['planning','Lập kế hoạch',CalendarDays],['self','Tự đánh giá',ClipboardCheck],['peer','Ý kiến đồng nghiệp',Users],['council','Hội đồng liên tịch',ShieldCheck],['vote','Bình chọn HTXSNV',CheckCircle2],['reports','Báo cáo',BarChart3],['catalog','Danh mục',Database],['audit','Audit Log',History],['settings','Cài đặt',Settings]];return <div className="app"><aside><div className="brand"><div className="logo"><School size={23}/></div><div><b>{APP}</b><span>Management System</span></div></div><button className="new" onClick={()=>setModal(true)}><Plus size={18}/> Nhiệm vụ mới</button>{menu.map(([id,name,Icon])=><button key={id} onClick={()=>setPage(id)} className={page===id?'active':''}><Icon size={20}/>{name}</button>)}<div className="sidefoot">Help<br/>Logout</div></aside><main><header><div className="search"><Search size={20}/><input value={q} onChange={e=>setQ(e.target.value)} placeholder="Tìm kiếm trong hệ thống..."/></div><div className="top"><span className="role">Role: Admin</span><Bell/><User/></div></header>{page==='dashboard'&&<Dashboard data={data} stats={stats} q={q}/>} {page==='staff'&&<Staff data={data} setData={setData} q={q}/>} {page==='planning'&&<Planning data={data} setData={setData} open={()=>setModal(true)}/>} {page==='self'&&<SelfAssessment data={data} setData={setData}/>} {page==='peer'&&<Peer data={data}/>} {page==='council'&&<Council data={data} setData={setData}/>} {page==='vote'&&<Vote data={data} setData={setData}/>} {page==='reports'&&<Reports data={data}/>} {page==='catalog'&&<Catalog data={data} setData={setData}/>} {page==='audit'&&<Audit data={data}/>} {page==='settings'&&<SettingsPage data={data} setData={setData}/>} </main>{modal&&<TaskModal onClose={()=>setModal(false)} onSave={t=>{setData(addAudit({...data,tasks:[{id:'T'+Date.now(),...t},...data.tasks]},'Thêm nhiệm vụ',t.name));setModal(false)}}/>}</div>}
function Card({title,value,sub,icon}){return <div className="card"><div>{icon}</div><div><p>{title}</p><h2>{value}</h2><span>{sub}</span></div></div>}
function Dashboard({data,stats,q}){const list=data.staff.filter(x=>(x.name+x.dept+x.rank+x.status).toLowerCase().includes(q.toLowerCase()));return <section><h1>Tổng hợp kết quả cuối cùng</h1><p className="muted">Kỳ đánh giá: {data.settings.period} • Năm học {data.settings.year}</p><div className="cards"><Card title="Tổng số cán bộ" value={stats.total} sub={`Đã đánh giá ${pct(stats.submitted,stats.total)}%`} icon={<Users/>}/><Card title="Hoàn thành xuất sắc" value={`${pct(stats.excellent,stats.total)}%`} sub={`${stats.excellent} người`} icon={<CheckCircle2/>}/><Card title="Hoàn thành tốt" value={`${pct(stats.good,stats.total)}%`} sub={`${stats.good} người`} icon={<BarChart3/>}/><Card title="Điểm trung bình" value={stats.avg} sub="Theo thang 100" icon={<ClipboardCheck/>}/></div><div className="panel"><div className="panel-head"><b>Hội đồng liên tịch - Đề xuất</b><button><FileDown size={16}/> Xuất PDF</button></div><table><thead><tr><th>STT</th><th>Họ và tên</th><th>Tổ chuyên môn</th><th>Điểm</th><th>Đánh giá của Tổ</th><th>Đề xuất Hội đồng</th><th>Ghi chú</th></tr></thead><tbody>{list.map((s,i)=><tr key={s.id}><td>{i+1}</td><td><b>{s.name}</b><br/><small>{s.id}</small></td><td>{s.dept}</td><td>{s.score}</td><td><Badge text={ranks.find(r=>r.id===s.rank)?.name}/></td><td><Badge text={ranks.find(r=>r.id===s.rank)?.name}/></td><td>{s.score>=90?'Đồng thuận':s.score<75?'Cần theo dõi':'Đồng thuận'}</td></tr>)}</tbody></table></div></section>}
function Staff({data,setData,q}){let list=data.staff.filter(s=>(s.name+s.id+s.dept+s.position+s.email).toLowerCase().includes(q.toLowerCase()));return <section><h1>Thông tin cá nhân & hồ sơ cán bộ</h1><div className="toolbar"><button><Filter size={16}/> Bộ lọc</button><button onClick={()=>alert('Chức năng import Excel đã đặt sẵn trong bản triển khai.')}>Import Excel</button><button><FileDown size={16}/> Xuất hồ sơ PDF</button></div><div className="grid2">{list.map(s=><div className="profile" key={s.id}><div className="avatar">{s.avatar}</div><div><span>MÃ CÁN BỘ: {s.id}</span><h2>{s.name}</h2><p>{s.position} • {s.dept}</p><p>{s.email} • {s.phone}</p><b>Điểm: {s.score} / 100</b> <Badge text={ranks.find(r=>r.id===s.rank)?.name}/></div></div>)}</div></section>}
function Planning({data,open}){return <section><h1>Lập kế hoạch công tác</h1><p className="muted">Quản lý và theo dõi các nhiệm vụ trong năm học.</p><div className="cards"><Card title="Tổng nhiệm vụ" value={data.tasks.length} sub="Theo kỳ hiện tại" icon={<CalendarDays/>}/><Card title="Số giờ dự kiến" value="128h" sub="Tổng hợp từ nhiệm vụ" icon={<History/>}/></div><div className="panel"><div className="panel-head"><b>Tuần 15: 08/02 - 14/02</b><button onClick={open}><Plus size={16}/> Thêm nhiệm vụ mới</button></div><TaskTable tasks={data.tasks}/></div></section>}
function TaskTable({tasks}){return <table><thead><tr><th>Tên nhiệm vụ</th><th>Sản phẩm</th><th>Thời gian</th><th>Người phụ trách</th><th>Chất lượng</th><th>Ghi chú</th></tr></thead><tbody>{tasks.map(t=><tr key={t.id}><td><b>{t.name}</b></td><td><Badge text={t.product}/></td><td>{t.time} ({t.hours})</td><td>{t.owner}</td><td>{t.quality}</td><td>{t.status}</td></tr>)}</tbody></table>}
function SelfAssessment({data,setData}){const[score,setScore]=useState({c1:10,c2:5,c3:10,c4:5,k1:18,k2:18,k3:28,plus:0,minus:0});const total=Object.values(score).reduce((a,b)=>a+(+b||0),0);function upd(k,v){setScore({...score,[k]:Math.max(0,Number(v))})}function submit(){if(score.plus>data.settings.plusMax)return alert('Điểm cộng không được vượt '+data.settings.plusMax);if(total>106)return alert('Tổng điểm bất thường, cần kiểm tra lại.');setData(addAudit(data,'Lưu tự đánh giá',`Tổng điểm ${total}`));alert('Đã kiểm tra dữ liệu và lưu bản tự đánh giá.')}return <section><h1>Đánh giá, xếp loại chất lượng CBCCVC</h1><p className="muted">Bản tự đánh giá kết quả thực hiện nhiệm vụ của cán bộ, công chức, viên chức</p><div className="scorebox"><b>Tổng điểm tự đánh giá</b><h2>{total}/100</h2><Badge text={ranks.find(r=>rankOf(total)===r.id)?.name}/><button onClick={submit}><Save size={16}/> Lưu bản nháp</button></div><table><thead><tr><th>STT</th><th>Nội dung đánh giá</th><th>Điểm tối đa</th><th>Tự chấm</th><th>Ghi chú / Minh chứng</th></tr></thead><tbody>{data.criteria[0].items.map((it,i)=><tr><td>1.{i+1}</td><td>{it[0]}</td><td>{it[1]}</td><td><input type="number" value={score['c'+(i+1)]} onChange={e=>upd('c'+(i+1),e.target.value)} max={it[1]}/></td><td><input placeholder="Minh chứng..."/></td></tr>)}{data.criteria[1].items.map((it,i)=><tr><td>2.{i+1}</td><td>{it[0]}</td><td>{it[1]}</td><td><input type="number" value={score['k'+(i+1)]} onChange={e=>upd('k'+(i+1),e.target.value)} max={it[1]}/></td><td><input placeholder="Minh chứng..."/></td></tr>)}<tr><td>3.1</td><td>Điểm cộng thành tích đột xuất</td><td>+{data.settings.plusMax}</td><td><input type="number" value={score.plus} onChange={e=>upd('plus',e.target.value)}/></td><td><input placeholder="Lý do điểm cộng..."/></td></tr><tr><td>3.2</td><td>Điểm trừ vi phạm/chậm tiến độ</td><td>-</td><td><input type="number" value={score.minus} onChange={e=>upd('minus',e.target.value)}/></td><td><input placeholder="Lý do điểm trừ..."/></td></tr></tbody></table><div className="grid2"><textarea placeholder="Ưu điểm"/><textarea placeholder="Hạn chế, khuyết điểm"/></div></section>}
function Peer({data}){return <section><h1>Đánh giá, đóng góp đồng nghiệp</h1><div className="panel"><div className="panel-head"><select><option>-- Chọn giáo viên --</option>{data.staff.map(s=><option>{s.name}</option>)}</select><b>Tổng điểm dự kiến 0/30</b></div><table><thead><tr><th>Tiêu chí đánh giá</th><th>Điểm tối đa</th><th>Điểm đánh giá</th><th>Ghi chú / Góp ý</th></tr></thead><tbody>{['Tuân thủ pháp luật, chính sách và quy định ngành','Tác phong, đạo đức nghề nghiệp và đoàn kết nội bộ','Mức độ hoàn thành kế hoạch giảng dạy, chất lượng bài giảng','Kỹ năng đánh giá học sinh, hồ sơ sổ sách chuyên môn','Ứng dụng CNTT, phương pháp dạy học mới','Tham gia nghiên cứu khoa học, sáng kiến kinh nghiệm'].map(x=><tr><td>{x}</td><td>5</td><td><input type="number"/></td><td><input placeholder="Nhập nhận xét..."/></td></tr>)}</tbody></table><textarea placeholder="Nhận xét tổng quan về ưu điểm, nhược điểm và đề xuất giúp đồng nghiệp phát triển tốt hơn..."/></div></section>}
function Council({data,setData}){return <section><h1>Hội đồng liên tịch</h1><p className="muted">Đối chiếu đánh giá tổ chuyên môn, tự đánh giá và đề xuất hội đồng. Dữ liệu chênh lệch được cảnh báo.</p><div className="panel"><table><thead><tr><th>Cán bộ</th><th>Tự ĐG</th><th>Tổ ĐG</th><th>Hội đồng</th><th>Cảnh báo</th><th>Thao tác</th></tr></thead><tbody>{data.staff.map((s,i)=><tr><td><b>{s.name}</b><br/><small>{s.dept}</small></td><td>{s.score}</td><td>{s.score-(i===2?8:0)}</td><td><select defaultValue={s.rank}>{ranks.map(r=><option value={r.id}>{r.name}</option>)}</select></td><td>{i===2?<span className="warn"><AlertTriangle size={15}/> Chênh lệch</span>:'Đồng thuận'}</td><td><button onClick={()=>setData(addAudit(data,'Hội đồng duyệt',s.name))}>Duyệt</button></td></tr>)}</tbody></table></div></section>}
function Vote({data,setData}){const[v,setV]=useState({});let max=Math.ceil(data.staff.length*(data.settings.quotaTeam+data.settings.quotaCouncil)/100);let count=Object.values(v).filter(Boolean).length;return <section><h1>Bình chọn cá nhân xuất sắc</h1><p className="muted">Quota tối đa {data.settings.quotaTeam+data.settings.quotaCouncil}%: {count}/{max} người</p><div className="panel"><table><thead><tr><th>STT</th><th>Họ và tên</th><th>Tổng điểm TĐG</th><th>Xếp loại đề xuất</th><th>Bình chọn XS</th></tr></thead><tbody>{data.staff.map((s,i)=><tr><td>{i+1}</td><td><b>{s.name}</b><br/><small>{s.dept}</small></td><td>{s.score}</td><td><Badge text={ranks.find(r=>r.id===s.rank)?.name}/></td><td><input type="checkbox" checked={!!v[s.id]} onChange={e=>{if(e.target.checked&&count>=max)return alert('Đã vượt quota bình chọn.');setV({...v,[s.id]:e.target.checked})}}/></td></tr>)}</tbody></table><button onClick={()=>setData(addAudit(data,'Xác nhận bình chọn',`${count}/${max}`))}>Xác nhận bình chọn Hội đồng</button></div></section>}
function Reports({data}){return <section><h1>Báo cáo</h1><div className="cards">{['Tổng hợp tháng','Tổng hợp năm','Theo tổ chuyên môn','Thu nhập tăng thêm','Danh sách chưa nộp','Minh chứng','Điểm cộng/trừ','Audit Log'].map(x=><div className="report"><FileDown/><b>{x}</b><span>Xuất Excel/PDF</span></div>)}</div></section>}
function Catalog({data,setData}){return <section><h1>Danh mục hệ thống</h1><div className="grid2"><div className="panel"><h3>Tổ chuyên môn</h3>{data.departments.map(x=><p className="row">{x}<ChevronRight size={16}/></p>)}</div><div className="panel"><h3>Tiêu chí đánh giá</h3>{data.criteria.map(g=><div><b>{g.g} ({g.max} điểm)</b>{g.items.map(i=><p className="row">{i[0]}<span>{i[1]}đ</span></p>)}</div>)}</div><div className="panel"><h3>Mức xếp loại</h3>{ranks.map(r=><p className="row">{r.name}<span>≥ {r.min}</span></p>)}</div><div className="panel"><h3>Vai trò</h3>{data.roles.map(r=><p className="row">{r}<ShieldCheck size={16}/></p>)}</div></div></section>}
function Audit({data}){return <section><h1>Audit Log</h1><div className="panel"><table><thead><tr><th>Thời gian</th><th>Người dùng</th><th>Hành động</th><th>Chi tiết</th></tr></thead><tbody>{(data.audits.length?data.audits:[{time:new Date().toLocaleString('vi-VN'),user:'System',action:'Khởi tạo',detail:'Dữ liệu mẫu THCS Trưng Vương Đà Nẵng'}]).map(a=><tr><td>{a.time}</td><td>{a.user}</td><td>{a.action}</td><td>{a.detail}</td></tr>)}</tbody></table></div></section>}
function SettingsPage({data,setData}){return <section><h1>Cài đặt hệ thống</h1><div className="panel form"><label>Tên phần mềm<input value={data.settings.school} onChange={e=>setData({...data,settings:{...data.settings,school:e.target.value}})}/></label><label>Năm học<input value={data.settings.year} onChange={e=>setData({...data,settings:{...data.settings,year:e.target.value}})}/></label><label>Kỳ đánh giá<input value={data.settings.period} onChange={e=>setData({...data,settings:{...data.settings,period:e.target.value}})}/></label><label>Khóa dữ liệu tháng<button onClick={()=>setData(addAudit({...data,settings:{...data.settings,locked:!data.settings.locked}},data.settings.locked?'Mở khóa tháng':'Khóa tháng',data.settings.period))}>{data.settings.locked?<Unlock/>:<Lock/>}{data.settings.locked?'Mở khóa':'Khóa tháng'}</button></label></div></section>}
function TaskModal({onClose,onSave}){const[t,setT]=useState({name:'',product:'',team:'Tổ Toán - Tin',owner:'Nguyễn Văn An',time:'',hours:'',quality:'Đảm bảo',status:'Đang làm'});return <div className="overlay"><div className="modal"><div className="modal-head"><h2>Thêm nhiệm vụ mới</h2><button onClick={onClose}>×</button></div><label>Tên nhiệm vụ *<input value={t.name} onChange={e=>setT({...t,name:e.target.value})} placeholder="Nhập tên nhiệm vụ..."/></label><div className="grid2"><label>Nhóm nhiệm vụ<select value={t.team} onChange={e=>setT({...t,team:e.target.value})}><option>Công tác giảng dạy</option><option>Nghiên cứu & chuyên đề</option><option>Phát triển chuyên môn</option></select></label><label>Thời gian thực hiện<input value={t.time} onChange={e=>setT({...t,time:e.target.value})} placeholder="Từ ngày - đến ngày"/></label></div><label>Sản phẩm dự kiến<input value={t.product} onChange={e=>setT({...t,product:e.target.value})} placeholder="VD: Báo cáo, giáo án..."/></label><label>Đính kèm minh chứng<div className="drop"><Upload/> Kéo thả tệp hoặc chọn từ máy tính<br/><small>PDF, DOCX, JPG, PNG, XLSX</small></div></label><textarea placeholder="Ghi chú"/><div className="modal-foot"><button onClick={onClose}>Hủy</button><button onClick={()=>{if(!t.name)return alert('Vui lòng nhập tên nhiệm vụ');onSave(t)}}><Save size={16}/> Lưu nhiệm vụ</button></div></div></div>}
function Badge({text}){return <span className="badge">{text}</span>}
createRoot(document.getElementById('root')).render(<App/>);
