Files
ihaleist/src/components/AiJobHistory.jsx
T
2026-09-06 16:59:19 +03:00

38 lines
3.2 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>;
}