commit melhorias1
This commit is contained in:
@@ -17,13 +17,13 @@ export default function RemunerationsWorkspace({api,context}){
|
||||
const operation=useMemo(()=>new WorkshopStockOperation(api),[context.token]),mounted=useRef(true),generation=useRef(context.token);generation.current=context.token;useEffect(()=>{mounted.current=true;return()=>{mounted.current=false;};},[context.token]);const locked=busy||operation.uncertain,protectedForm=locked||Boolean(form);
|
||||
useEffect(()=>{let active=true;setBusy(true);setData(null);setError('');api('/api/remunerations?'+qs(query)).then(d=>{if(active)setData(checkedRemunerations(d,query));}).catch(e=>{if(active)setError(e.message);}).finally(()=>{if(active)setBusy(false);});return()=>{active=false;};},[query,context.token]);
|
||||
function start(operationName='establish',review=false,newRecord=false){const row=newRecord?null:selected,source=row?.effective?.data.input??row?.data.input,kind=query.kind;const defaults=kind==='worker'?{code:'QA-',status:'active'}:kind==='rules'?{label:'',valid_from:'',valid_to:'',worker_rate:'',employer_rate:'',retention_rate:'',source:''}:{worker_key:'',rules_key:'',period:'',gross:''};setPreview(null);setError('');setForm({kind,action:review?'review':'prepare',record_key:row?.record_key??crypto.randomUUID(),version:row?.version??0,reason:'',data:review?{decision:''}:{...(source??defaults),operation:operationName}});}
|
||||
const change=(key,value)=>{setPreview(null);setForm(f=>({...f,data:{...f.data,[key]:value}}));};
|
||||
const change=(key,value)=>{setPreview(null);setForm(f=>({...f,data:{...f.data,[key]:value,...(key==='worker_key'&&f.data.contract_key?{contract_key:''}:{})}}));};
|
||||
async function prepare(e){e.preventDefault();setBusy(true);setError('');try{const input={...form,data:{...form.data}};if(!input.data.contract_key)delete input.data.contract_key;if(input.kind==='rules'&&input.action==='prepare')for(const k of['worker_rate','employer_rate','retention_rate'])if(input.data[k]==='')input.data[k]=null;const key=crypto.randomUUID(),p=await api('/api/remunerations/preview','POST',input,undefined,key);setPreview({...p,key});}catch(e){setError(e.message);}finally{setBusy(false);}}
|
||||
async function confirm(retry=false){setBusy(true);setError('');try{const out=retry?await operation.retry():await operation.send('/api/remunerations/confirm',{preview:preview.preview},preview.key);setReceiptKey(preview.key);setNotice('Decisão v'+out.version+' · '+states[out.state]+(out.replayed?' · recibo recuperado':''));setSelected(null);setForm(null);setPreview(null);setQuery(q=>({...q,page:1}));}catch(e){setError(e.message);if(!operation.uncertain)setPreview(null);}finally{setBusy(false);}}
|
||||
async function download(row){const token=context.token;setBusy(true);setError('');try{const out=await remunerationDownload({id:row.id,token,sha256:row.data.artifact_sha256,isCurrent:()=>mounted.current&&generation.current===token});const url=URL.createObjectURL(new Blob([out.bytes],{type:'text/csv;charset=utf-8'})),a=document.createElement('a');a.href=url;a.download='sgf-remuneracao-interna-QA-v'+row.version+'.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}catch(e){if(mounted.current&&generation.current===token)setError(e.message);}finally{if(mounted.current&&generation.current===token)setBusy(false);}}
|
||||
async function recover(){setBusy(true);setError('');try{setReceipt(await api('/api/remunerations/receipts/'+encodeURIComponent(receiptKey)));}catch(e){setError(e.message);}finally{setBusy(false);}}
|
||||
const textInput=(key,label,type='text',extra={})=><label key={key}>{label}<input required type={type} value={form.data[key]??''} onChange={e=>change(key,e.target.value)} {...extra}/></label>;
|
||||
return <section className="panel" data-sgf-operation-uncertain={protectedForm?'true':undefined}><p className="eyebrow">RECURSOS HUMANOS</p><h1>Remunerações internas QA</h1><p>Área autónoma, com acessos próprios. Cadastro e regras fictícios, processamento e revisão distinta. Sem salário/dívida/pagamento/lançamento, recibo legal ou submissão. Contratos, assiduidade, férias e conformidade legal continuam pendentes.</p>{error&&<p role="alert">{error}</p>}{notice&&<p role="status">{notice}</p>}
|
||||
return <section className="panel" data-sgf-operation-uncertain={protectedForm?'true':undefined}><p className="eyebrow">RECURSOS HUMANOS</p><h1>Remunerações internas QA</h1><p>Área autónoma, com acessos próprios. Cadastro e regras fictícios, processamento e revisão distinta. Sem salário/dívida/pagamento/lançamento, recibo legal ou submissão. Contratos, assiduidade e férias estão na função própria de RH. A conformidade legal continua pendente.</p>{error&&<p role="alert">{error}</p>}{notice&&<p role="status">{notice}</p>}
|
||||
<nav aria-label="Funções de Recursos Humanos">{Object.entries(kinds).map(([kind,label])=><button key={kind} disabled={protectedForm} aria-current={query.kind===kind?'page':undefined} onClick={()=>{setSelected(null);setFilters({search:'',period:'',state:''});setQuery({kind,page:1,per_page:25});}}>{label}</button>)}</nav>
|
||||
<form data-sgf-read-filters="true" onSubmit={e=>{e.preventDefault();setSelected(null);setQuery(q=>({kind:q.kind,per_page:q.per_page,...filters,page:1}));}}><fieldset disabled={protectedForm}><label>Pesquisa por código / regras<input value={filters.search} onChange={e=>setFilters(f=>({...f,search:e.target.value}))}/></label>{query.kind==='payroll'&&<label>Período<input type="month" value={filters.period} onChange={e=>setFilters(f=>({...f,period:e.target.value}))}/></label>}<label>Estado<select value={filters.state} onChange={e=>setFilters(f=>({...f,state:e.target.value}))}><option value="">Todos</option>{Object.entries(states).map(([k,v])=><option key={k} value={k}>{v}</option>)}</select></label><label>Por página<select value={query.per_page} onChange={e=>{setSelected(null);setQuery(q=>({...q,per_page:Number(e.target.value),page:1}));}}><option value="25">25</option><option value="100">100</option></select></label><button>Consultar</button></fieldset></form>
|
||||
{data&&<>{data.capabilities.prepare&&!query.record_key&&<button disabled={protectedForm} onClick={()=>{setSelected(null);start('establish',false,true);}}>Novo: {kinds[query.kind]}</button>}<p>Página{data.records.current_page}/{data.records.last_page} · {data.records.total} versão/registo(s).</p><div className="table-scroll"><table><thead><tr><th>Registo / período</th><th>Versão e decisão</th><th>Resultado revisto actual</th><th>Autor / fundamento</th><th>Escolha</th></tr></thead><tbody>{data.records.data.map(r=><tr key={r.id}><td>{r.data.input.code??r.data.input.label??r.data.dependencies.worker?.data.input.code}<small>{r.data.input.period??''} · {r.record_key}</small></td><td>v{r.version} · {states[r.state]} · {r.data.input.operation}</td><td>{r.effective?<>v{r.effective.version} · {r.effective.data.status}{r.effective.data.calculation&&<small>Líquido interno{remunerationMoney(r.effective.data.calculation.net_cents)}</small>}</>:'Sem aprovação'}</td><td>QA{r.actor_id} · {r.created_at}<small>{r.reason}</small></td><td><button disabled={protectedForm} onClick={()=>{setSelected(r);setReceipt(null);}}>Escolher este registo</button></td></tr>)}</tbody></table></div>{!data.records.data.length&&<p>Sem registos neste âmbito/filtro.</p>}<button disabled={protectedForm||query.page<=1} onClick={()=>{setSelected(null);setQuery(q=>({...q,page:q.page-1}));}}>Anterior</button><button disabled={protectedForm||query.page>=data.records.last_page} onClick={()=>{setSelected(null);setQuery(q=>({...q,page:q.page+1}));}}>Seguinte</button>{query.record_key&&<button disabled={protectedForm} onClick={()=>{setSelected(null);setQuery({kind:query.kind,page:1,per_page:25});}}>Voltar à lista actual</button>}{query.kind==='payroll'&&!query.record_key&&<p>Somente aprovados activos desta página: {data.page_totals.approved_active_on_page}; bruto{remunerationMoney(data.page_totals.gross_cents)}, líquido{remunerationMoney(data.page_totals.net_cents)}, custo{remunerationMoney(data.page_totals.employer_cost_cents)}. Não são totais do universo nem salários pagos.</p>}</>}
|
||||
|
||||
Reference in New Issue
Block a user