/* Week Board (Kanban) view for Daily Queue — 7 day columns with capacity headers */
// ---- helpers ----
function weekStart(isoStr){
// Monday-start week containing the date
const d = new Date(isoStr+'T00:00:00');
const dow = (d.getDay()+6)%7; // 0=Mon
d.setDate(d.getDate()-dow);
return CK.iso(d);
}
function statusBigColor(status){
return { open:'#16a34a', almost:'#d97706', full:'#dc2626', closed:'#64748b' }[status] || '#64748b';
}
// ===== compact job card (board) =====
function BoardJobCard({ job, seq, onOpen }){
const b = CK.branch(job.branch); const jt = CK.jobType(job.jobType); const it = job.items[0];
const firstSlot = (job.slot||'').startsWith('คิวแรก');
const prod = CK.product(it?.p);
return (
);
}
// ===== capacity headers (4 styles) =====
function MeterHeader({ d, date, isToday, onToggleClosed, onOpenDay }){
const color = statusBigColor(d.status);
const segs = 12; const filled = Math.round(d.pct/100*segs);
return (
{CK.DAYSTATUS[d.status].th}
{d.pct}%
{Array.from({length:segs}).map((_,i)=>(
))}
ใช้ไป {d.used} คิวเต็มที่ {d.max}
);
}
function GaugeHeader({ d, date, isToday, onToggleClosed, onOpenDay }){
const color = statusBigColor(d.status);
const r=40, c=Math.PI*r, off=c*(1-Math.min(100,d.pct)/100);
return (
{CK.DAYSTATUS[d.status].th}
· {d.used}/{d.max} คิว
);
}
function BlueprintHeader({ d, date, isToday, onToggleClosed, onOpenDay }){
const color = statusBigColor(d.status);
const ticks=20; const filled=Math.round(d.pct/100*ticks);
return (
USED {d.used.toFixed(1)}CAP {d.max.toFixed(1)}
{Array.from({length:ticks}).map((_,i)=>(
))}
{ {open:'OPEN',almost:'NEAR',full:'FULL',closed:'CLOSED'}[d.status] }
{d.pct}%
);
}
function CompactHeader({ d, date, isToday, onToggleClosed, onOpenDay }){
const color = statusBigColor(d.status);
return (
);
}
const CAP_HEADERS = { meter:MeterHeader, gauge:GaugeHeader, blueprint:BlueprintHeader, compact:CompactHeader };
// ===== Week Board =====
function WeekBoard({ jobs, filters, onOpenJob, onOpenDay, isMobile }){
const [start,setStart] = React.useState(CK.TODAY);
const [capStyle,setCapStyle] = React.useState('meter');
const [, force] = React.useReducer(x=>x+1,0);
const days = Array.from({length:7},(_,i)=>CK.addDays(start,i));
const Header = CAP_HEADERS[capStyle];
// week stats
const weekJobs = jobs.filter(j=>days.includes(j.date) && j.status!=='cancelled');
const filteredAll = applyFilters(weekJobs, {...filters, date:undefined});
const fullDays = days.filter(d=>computeDayStatus(jobs,d).status==='full').length;
const freeUnits = Math.round(days.reduce((s,d)=>{ const x=computeDayStatus(jobs,d); return s+Math.max(0,x.max-x.used); },0)*10)/10;
function toggleClosed(d){ const c=CK.CAPACITY[d]||{max:10,closed:false}; CK.CAPACITY[d]={...c,closed:!c.closed}; force(); }
const capOpts = [['meter','เมเตอร์'],['gauge','เกจ'],['blueprint','บลูพรินต์'],['compact','กระชับ']];
const VISIBLE = 4;
return (
{/* board controls */}
{CK.thDate(days[0])} – {CK.thDate(days[6])}
มุมมอง
{capOpts.map(([k,l])=>(
))}
{/* legend + week stats */}
{Object.values(CK.DAYSTATUS).map(s=>(
{s.th}
))}
งานสัปดาห์นี้
{filteredAll.length}
ว่างรับเพิ่ม
{freeUnits} คิว
{/* columns */}
{days.map(d=>{
const ds = computeDayStatus(jobs,d);
const isToday = d===CK.TODAY;
const colJobs = sortQueue(applyFilters(jobs.filter(j=>j.date===d && j.status!=='cancelled'), {...filters, date:undefined}));
const shown = colJobs.slice(0,VISIBLE);
const extra = colJobs.length - shown.length;
return (
toggleClosed(d)} onOpenDay={()=>onOpenDay(d)} />
{colJobs.length===0 ? (
ไม่มีงาน
) : (
<>
{shown.map((j,i)=>onOpenJob(j.id)} />)}
{extra>0 && }
>
)}
);
})}
);
}
Object.assign(window, { WeekBoard, weekStart });