/* Manager screens: Dashboard, Management */ // ---------- mini charts ---------- function BarH({ data, color='#dc2626', unit='งาน', money }){ const max = Math.max(1, ...data.map(d=>d.value)); return (
{data.map((d,i)=>(
{d.label}
{d.value}
))}
); } function Donut({ data, size=140 }){ const total = data.reduce((s,d)=>s+d.value,0) || 1; let acc = 0; const stops = data.map(d=>{ const start=acc/total*360; acc+=d.value; const end=acc/total*360; return `${d.color} ${start}deg ${end}deg`; }).join(','); return (
{total}
งาน
{data.filter(d=>d.value>0).map((d,i)=>(
{d.label} {d.value}
))}
); } function ChartCard({ icon, title, children, className='' }){ return (

{title}

{children}
); } function DashboardScreen(){ const { jobs, tweaks, navigate, device } = useCK(); const isMobile = device==='mobile'; const today = CK.TODAY, tomo = CK.addDays(CK.TODAY,1); const month = CK.TODAY.slice(0,7); const inMonth = jobs.filter(j=>j.date.slice(0,7)===month); const cnt = s => jobs.filter(j=>j.status===s).length; const todayJobs = jobs.filter(j=>j.date===today); const stats = [ {icon:'CalendarDays', label:'งานวันนี้', value:todayJobs.length, tone:'red', sub:CK.thDate(today)}, {icon:'CalendarPlus', label:'งานพรุ่งนี้', value:jobs.filter(j=>j.date===tomo).length, tone:'slate'}, {icon:'CalendarRange', label:'งานเดือนนี้', value:inMonth.length, tone:'blue'}, {icon:'Clock', label:'รอติดตั้ง', value:jobs.filter(j=>['confirmed','preparing','ready','assigned'].includes(j.status)).length, tone:'amber'}, {icon:'CircleCheck', label:'เสร็จแล้ว', value:cnt('completed'), tone:'green'}, {icon:'TriangleAlert', label:'ติดปัญหา', value:cnt('issue'), tone:'red'}, {icon:'CalendarClock', label:'เลื่อนนัด', value:cnt('postponed'), tone:'amber'}, {icon:'CalendarX', label:'วันคิวเต็ม (เดือนนี้)', value:Object.keys(CK.CAPACITY).filter(d=>d.slice(0,7)===month && computeDayStatus(jobs,d).status==='full').length, tone:'red'}, ]; const byBranch = CK.BRANCHES.map(b=>({label:b.code, value:jobs.filter(j=>j.branch===b.id).length, color:'#dc2626'})); const byType = CK.JOB_TYPES.map(t=>({label:t.th, value:jobs.filter(j=>j.jobType===t.id).length})).filter(d=>d.value>0).sort((a,b)=>b.value-a.value); const byDistrict = CK.DISTRICTS.map(d=>({label:'อ.'+d, value:jobs.filter(j=>j.district===d).length})).filter(d=>d.value>0).sort((a,b)=>b.value-a.value); const statusData = Object.values(CK.STATUS).map(s=>({label:s.th, value:cnt(s.key), color:s.dot})).filter(d=>d.value>0); const teamLoad = CK.TEAMS.map(t=>({label:t.name.split('—')[0].trim(), value:Math.round(jobs.filter(j=>j.team===t.id&&j.date===today).reduce((s,j)=>s+CK.jobUnits(j),0)*10)/10, color:t.color})); const next7 = Array.from({length:7},(_,i)=>{ const d=CK.addDays(today,i); return {label:CK.thDate(d), value:jobs.filter(j=>j.date===d).length, color:i===0?'#dc2626':'#fca5a5'}; }); const chartsMode = tweaks.dashboardStyle==='charts'; return (
{isMobile ?

ภาพรวมผู้บริหาร

: เชื่อมต่อ real-time }
{stats.map((s,i)=>navigate('queue',{date:today}):undefined}/>)}
{chartsMode ? (
) : (

สรุปงานวันนี้

)}
); } // ================= Management ================= function ManageScreen(){ const { toast, device } = useCK(); const isMobile = device==='mobile'; const [tab,setTab] = React.useState('branches'); const [cap,setCap] = React.useState(()=>JSON.parse(JSON.stringify(CK.CAPACITY))); const tabs = [['branches','สาขา','Building2'],['users','ผู้ใช้','Users'],['jobtypes','ประเภทงาน','Wrench'],['capacity','ความจุคิว','GaugeCircle']]; const th = 'text-left text-[12px] font-medium text-slate-500 px-3 py-2.5'; const td = 'px-3 py-3 text-[13px] text-slate-700 border-t border-slate-100'; return (
{isMobile ?

จัดการระบบ

: }
{tabs.map(([k,l,ic])=>( ))}
{tab==='branches' && (
สาขาทั้งหมด ({CK.BRANCHES.length}) toast('เปิดฟอร์มเพิ่มสาขา')}>เพิ่มสาขา
{CK.BRANCHES.map(b=>( ))}
รหัสชื่อสาขาเบอร์โทรที่อยู่สถานะ
{b.code} {b.name} {b.phone} {b.addr} {b.active?เปิดใช้งาน:ปิด}
)} {tab==='users' && (
ผู้ใช้งาน ({CK.USERS.length}) toast('เปิดฟอร์มเพิ่มผู้ใช้')}>เพิ่มผู้ใช้
{CK.USERS.map(u=>{ const b=CK.branch(u.branch); return ( ); })}
ชื่อสิทธิ์สาขาเบอร์โทรสถานะ
{u.name}
{CK.ROLES[u.role].label} {b?b.code:'—'} {u.phone} {u.active?active:inactive}
)} {tab==='jobtypes' && (
{CK.JOB_TYPES.map(t=>(
{t.th}
น้ำหนักคิว {t.unit} unit/ชิ้น
))} toast('เพิ่มประเภทงาน')} className="p-4 flex items-center justify-center gap-2 text-slate-400 hover:text-brand-600 hover:ring-brand-300 ring-dashed">เพิ่มประเภทงาน
)} {tab==='capacity' && (

ตั้งค่าความจุงานรายวัน

น้ำหนักงาน: ติดตั้งแอร์ = 1 unit · แอร์ 4 ทิศทาง = 2 unit · ล้างแอร์ = 0.5 unit · ถอดแอร์ = 0.5 unit · งานต่างอำเภอไกล +0.5 unit
{Object.keys(cap).filter(d=>d>=CK.TODAY).slice(0,14).map(d=>{ const c = cap[d]; return (
{CK.thDate(d,{withDow:true})}
รับสูงสุด
{c.max}
unit
); })}
{ Object.assign(CK.CAPACITY,cap); toast('บันทึกความจุคิวแล้ว','success'); }}>บันทึก
)}
); } Object.assign(window, { DashboardScreen, ManageScreen, BarH, Donut });