41 lines
3.4 KiB
React
41 lines
3.4 KiB
React
import React, {useEffect,useRef,useState} from 'react';
|
||
import {getToken} from '../services/auth';
|
||
const labels={baslik:'Başlık',kurum:'İdare',tarih:'Tarih',saat:'Saat',durum:'Durum',alimTuru:'Alım türü',aciklama:'İlan metni',documentVersion:'Doküman sürümü'};
|
||
export function TenderSourceHistory({tender}) {
|
||
const [state,setState]=useState({records:[],nextCursor:null,loaded:false,error:null,loading:false});
|
||
const active=useRef(null);
|
||
useEffect(()=>()=>{const pending=active.current;active.current=null;pending?.abort();},[]);
|
||
const load=async(before=null)=>{
|
||
if(active.current)return;
|
||
const controller=new AbortController();active.current=controller;
|
||
const token=getToken(),timer=setTimeout(()=>controller.abort(),30000);
|
||
setState(s=>({...s,loading:true,error:null}));
|
||
try {
|
||
const query=new URLSearchParams({ikn:tender.ikn,kurum:tender.kurum || ''});if(before)query.set('before',before);
|
||
const res=await fetch('/api/tenders/source-history?'+query,{headers:{Authorization:`Bearer ${token}`},signal:controller.signal});
|
||
const data=await res.json();
|
||
if(!res.ok || !data.success || !Array.isArray(data.records))throw new Error(data.error || 'Kaynak geçmişi alınamadı.');
|
||
if(!controller.signal.aborted && getToken()===token)setState(s=>({records:before?[...s.records,...data.records]:data.records,nextCursor:data.nextCursor,loaded:true,error:null,loading:false}));
|
||
} catch(error) {
|
||
if(active.current===controller && getToken()===token)setState(s=>({...s,loading:false,error:controller.signal.aborted?'Kaynak geçmişi zamanında alınamadı. Yeniden deneyin.':error.message}));
|
||
} finally {clearTimeout(timer);if(active.current===controller)active.current=null;}
|
||
};
|
||
return <details className="p-3 border-b text-sm">
|
||
<summary className="cursor-pointer font-semibold">Kaynak değişiklik geçmişi</summary>
|
||
<p className="mt-2 text-xs text-slate-600">Uygulamanın gözlemlediği sürümler gösterilir. Gözlem zamanı resmi değişiklik tarihi değildir; kayıt öncesindeki veya iki kontrol arasındaki değişiklikler bulunmayabilir. Farklı kaynaklar ayrı izlenir.</p>
|
||
{!state.loaded && <button onClick={()=>load()} disabled={state.loading} className="mt-2 px-3 py-2 border rounded">Geçmişi yükle</button>}
|
||
{state.loading && <p role="status">Kaynak geçmişi yükleniyor…</p>}
|
||
{state.error && <div role="alert"><p>{state.error}</p><button onClick={()=>load(state.loaded?state.nextCursor:null)} disabled={state.loading} className="underline py-2">Yeniden dene</button></div>}
|
||
{state.loaded && !state.records.length && <p className="mt-2">Henüz gözlem kaydı yok. Bu, ihale bilgilerinin hiç değişmediğini göstermez.</p>}
|
||
<ol className="space-y-3 mt-3">{state.records.map(record=><li key={record.id} className="border rounded p-2">
|
||
<p>{new Date(record.observedAt).toLocaleString('tr-TR')} · {record.sourceId}</p>
|
||
{!record.changes.length?<p>Bu kaynak için ilk gözlem.</p>:record.changes.map(change=><details key={change.field} className="mt-1">
|
||
<summary className="cursor-pointer">{labels[change.field] || change.field} değişti</summary>
|
||
<p className="whitespace-pre-wrap break-words">Önce: {change.before ?? 'Bilinmiyor'}</p>
|
||
<p className="whitespace-pre-wrap break-words">Sonra: {change.after ?? 'Bilinmiyor'}</p>
|
||
</details>)}
|
||
</li>)}</ol>
|
||
{state.nextCursor && <button onClick={()=>load(state.nextCursor)} disabled={state.loading} className="mt-2 px-3 py-2 border rounded">Daha eski kayıtlar</button>}
|
||
</details>;
|
||
}
|