import React,{useEffect,useMemo,useState}from'react'; import{WorkshopStockOperation}from'./workshop-stock-operation.js'; const day=value=>new Date(value).toISOString().slice(0,10); const display=value=>new Intl.DateTimeFormat('pt-MZ',{dateStyle:'short',timeStyle:'short',timeZone:'Africa/Maputo'}).format(new Date(value)); const local=value=>{const d=new Date(value);return new Date(d.getTime()+120*60000).toISOString().slice(0,16);}; const utc=value=>new Date(value+':00+02:00').toISOString(); export default function WorkshopScheduleWorkspace({api,context,can}){ const now=new Date(),[filters,setFilters]=useState({from:day(now),to:day(new Date(now.getTime()+7*86400000)),state:'',search:'',per_page:25,page:1}),[applied,setApplied]=useState(null),[data,setData]=useState(null),[options,setOptions]=useState(null),[optionQuery,setOptionQuery]=useState({search:'',page:1,per_page:25}),[optionSearch,setOptionSearch]=useState(''),[form,setForm]=useState(null),[preview,setPreview]=useState(null),[error,setError]=useState(''),[notice,setNotice]=useState(''),[busy,setBusy]=useState(false),[history,setHistory]=useState(null),[historyKey,setHistoryKey]=useState(null); const command=useMemo(()=>new WorkshopStockOperation(api),[context.token]),manage=can('workshop.assign',context.unit?.id),locked=busy||command.uncertain; const query=value=>new URLSearchParams(Object.fromEntries(Object.entries(value).filter(([,v])=>v!==''&&v!==null))).toString(); async function load(f=applied??filters){setData(await api('/api/workshop-schedule?'+query(f)));} useEffect(()=>{setApplied(filters);let current=true;api('/api/workshop-schedule?'+query(filters)).then(v=>{if(current)setData(v);}).catch(e=>{if(current)setError(e.message);});return()=>{current=false;};},[context.token]); useEffect(()=>{if(!manage)return;let current=true;api('/api/workshop-schedule/options?'+query(optionQuery)).then(v=>{if(current)setOptions(v.orders);}).catch(e=>{if(current)setError(e.message);});return()=>{current=false;};},[context.token,optionQuery,manage]); const start=()=>{setForm({action:'book',booking_key:crypto.randomUUID(),version:0,order_id:'',technician_id:'',starts_at:'',ends_at:'',reason:''});setPreview(null);setNotice('');setError('');}; const select=(row,action)=>{setForm({action,booking_key:row.booking_key,version:row.version,order_id:row.order_id,technician_id:row.technician_id,starts_at:local(row.starts_at),ends_at:local(row.ends_at),reason:''});setPreview(null);setError('');}; const update=(key,value)=>{setPreview(null);setForm(f=>({...f,[key]:value}));}; async function prepare(e){e.preventDefault();setBusy(true);setError('');try{const input={...form,order_id:Number(form.order_id),technician_id:Number(form.technician_id),starts_at:utc(form.starts_at),ends_at:utc(form.ends_at)};const result=await api('/api/workshop-schedule/preview','POST',input,undefined,crypto.randomUUID());setPreview(result);}catch(e){setError(e.message);}finally{setBusy(false);}} async function confirm(retry=false){setBusy(true);setError('');try{const r=retry?await command.retry():await command.send('/api/workshop-schedule/confirm',{preview:preview.preview},crypto.randomUUID());setNotice(`Agendamento v${r.version} ${r.state==='cancelled'?'cancelado':'confirmado'}${r.replayed?' · recibo recuperado':''}.`);setPreview(null);setForm(null);await load();}catch(e){setError(e.message);if(!command.uncertain)setPreview(null);}finally{setBusy(false);}} async function openHistory(key,page=1){setError('');try{const result=await api('/api/workshop-schedule/'+key+'/history?page='+page);setHistory(result.events);setHistoryKey(key);}catch(e){setError(e.message);}} const reminders={within24h:'Nas próximas24h',in_progress:'Intervalo em curso',elapsed:'Intervalo decorrido — não presume trabalho concluído',planned:'Planeado',cancelled:'Cancelado'}; return

Agenda e disponibilidade

Horários de Moçambique (UTC+02). Liga ordens já existentes ao técnico atribuído e ao equipamento. Agendar não autoriza trabalho nem altera stock, custos ou facturação. Lembretes apenas neste ecrã.

{error&&

{error}

}{notice&&

{notice}

}
{e.preventDefault();setApplied(filters);load(filters).catch(e=>setError(e.message));}}>
{manage&&} {data&&<>
{data.bookings.data.map(row=>)}
Ordem/equipamentoTécnicoIntervaloSituaçãoAcções
{row.reference}{row.equipment}{row.technician}{display(row.starts_at)}até {display(row.ends_at)}{reminders[row.reminder]}{manage&&row.state==='scheduled'&&<>}
{!data.bookings.data.length&&

Sem agendamentos visíveis nos filtros seleccionados.

}
Página {data.bookings.current_page}/{data.bookings.last_page}
} {form&&

{form.action==='book'?'Novo agendamento':form.action==='cancel'?'Cancelar agendamento':'Reagendar'}

{form.action==='book'&&
{e.preventDefault();setOptionQuery({...optionQuery,search:optionSearch,page:1});}}>
{options&&<>}
}
{form.action==='book'?:

Ordem #{form.order_id} · versão {form.version}

}

O técnico é o já atribuído na ordem; alterar a agenda não reatribui trabalho.