38 lines
3.2 KiB
React
38 lines
3.2 KiB
React
import React,{useEffect,useState,useRef} from 'react';
|
||
import {listAiJobs,getAiJob,cancelAiJob,retryAiJob} from '../services/aiJobs';
|
||
const labels={queued:'Sırada',running:'İşleniyor',completed:'Tamamlandı',failed:'Hata',interrupted:'Kesildi',cancelled:'İptal edildi'};
|
||
const operations={'analiz':'Şartname analizi','belge-sor':'Belge sorusu','teklif-denetim':'Taslak denetimi','teklif-taslak':'Taslak üretimi'};
|
||
export function AiJobHistory({ikn,onOpen,revision,locked=false,operationFilter='offer'}) {
|
||
const [jobs,setJobs]=useState([]),[error,setError]=useState(null),[busy,setBusy]=useState(false);
|
||
const alive=useRef(true);
|
||
useEffect(()=>{alive.current=true;return()=>{alive.current=false;};},[]);
|
||
const act=async fn=>{
|
||
if(busy)return;setBusy(true);setError(null);
|
||
try {await fn();}catch(e){if(alive.current)setError(e.message);}finally {if(alive.current)setBusy(false);}
|
||
};
|
||
const refresh=async()=>{const data=await listAiJobs(ikn);if(!Array.isArray(data.jobs))throw new Error('İş listesi alınamadı.');if(alive.current)setJobs(data.jobs.filter(job=>operationFilter==='offer'?job.operation!=='analiz':job.operation===operationFilter));};
|
||
useEffect(()=>{
|
||
let current=true;setBusy(true);setError(null);
|
||
listAiJobs(ikn).then(data=>{
|
||
if(!Array.isArray(data.jobs))throw new Error('İş listesi alınamadı.');
|
||
if(current)setJobs(data.jobs.filter(job=>operationFilter==='offer'?job.operation!=='analiz':job.operation===operationFilter));
|
||
}).catch(e=>{if(current)setError(e.message);}).finally(()=>{if(current)setBusy(false);});
|
||
return()=>{current=false;};
|
||
},[ikn,revision,operationFilter]);
|
||
return <details aria-busy={busy} className="border rounded-xl p-3 text-xs">
|
||
<summary className="font-bold cursor-pointer">Kayıtlı AI işleri</summary>
|
||
<p className="mt-2">Ekran kapansa da sunucudaki işler devam eder. Son 50 iş gösterilir. Kesilen işi yeniden başlatmak sağlayıcıya yeni istek gönderir. Eski sonucu açmak asistanın mevcut metinlerini değiştirir.</p>
|
||
<button type="button" onClick={()=>act(refresh)} disabled={busy} className="border rounded px-3 py-2 mt-2">İş listesini yenile</button>
|
||
{busy&&<p role="status">İş bilgileri alınıyor…</p>}
|
||
{error&&<p role="alert">{error}</p>}
|
||
{!jobs.length&&!busy&&!error&&<p>Bu ihale için kayıtlı AI işi yok.</p>}
|
||
<ul className="space-y-2 mt-2">{jobs.map(job=><li key={job.id} className="border rounded p-2">
|
||
<p>{operations[job.operation]} · {labels[job.state]} · {new Date(job.createdAt).toLocaleString('tr-TR')}</p>
|
||
{job.error&&<p>{job.error}</p>}
|
||
{job.state==='completed'&&<button type="button" disabled={busy || locked} className="underline py-2 mr-3" onClick={()=>act(async()=>{const data=await getAiJob(job.id);if(alive.current)await onOpen(data.job);})}>Kaynak ve sonucu aç</button>}
|
||
{['failed','interrupted'].includes(job.state)&&<button type="button" disabled={busy} className="underline py-2 mr-3" onClick={()=>act(async()=>{await retryAiJob(job.id);await refresh();})}>İşi yeniden başlat</button>}
|
||
{['queued','running'].includes(job.state)&&<button type="button" disabled={busy} className="underline py-2" onClick={()=>act(async()=>{await cancelAiJob(job.id);await refresh();})}>İşi iptal et</button>}
|
||
</li>)}</ul>
|
||
</details>;
|
||
}
|