50 lines
5.1 KiB
React
50 lines
5.1 KiB
React
import React,{useEffect,useRef,useState} from 'react';
|
||
import {useApp} from '../../context/AppContext';
|
||
import {getToken} from '../../services/auth';
|
||
import {accountRequest,requireArray} from '../../services/accountRequest';
|
||
export function DeliveryQueue({weekly=false}){
|
||
const {isAdmin,authUser}=useApp();
|
||
return isAdmin?<Queue weekly={weekly} key={`${authUser.email}:${getToken()}:${weekly}`}/>:null;
|
||
}
|
||
function Queue({weekly}){
|
||
const endpoint=weekly?'/api/admin/brief/outbox':'/api/admin/notify/outbox';
|
||
const [rows,setRows]=useState(null),[error,setError]=useState(null),[busy,setBusy]=useState(false);
|
||
const alive=useRef(true),locked=useRef(false);
|
||
const load=async()=>{
|
||
const data=await accountRequest(endpoint);
|
||
const received=requireArray(data,'deliveries');
|
||
const rows=received.filter(row=>row && typeof row==='object' && typeof row.id==='string' && row.id.trim() && typeof row.state==='string' && (row.subject == null || typeof row.subject==='string') && (row.to == null || ['string','number'].includes(typeof row.to)) && (row.error == null || typeof row.error==='string') && (row.providerId == null || typeof row.providerId==='string') && (row.review == null || (typeof row.review==='object' && typeof row.review.actor==='string' && typeof row.review.note==='string' && Number.isFinite(Date.parse(row.review.at)))));
|
||
if(rows.length!==received.length)throw new Error('Sunucu gönderim kayıtlarından birini geçersiz döndürdü. Liste yenilenemedi.');
|
||
if(alive.current)setRows(rows);
|
||
};
|
||
const act=async fn=>{if(locked.current)return;locked.current=true;setBusy(true);setError(null);try{await fn();}catch(e){if(alive.current)setError(e.message);}finally{locked.current=false;if(alive.current)setBusy(false);}};
|
||
useEffect(()=>{alive.current=true;void act(load);return()=>{alive.current=false;};},[]);
|
||
return <section role="region" aria-label={weekly?'Haftalık özet gönderim incelemesi':'E-posta gönderim incelemesi'} aria-busy={busy} className="bg-white border rounded-xl p-5 space-y-3">
|
||
<h3 className="font-bold">{weekly?'Haftalık özet gönderim incelemesi':'E-posta gönderim incelemesi'} · Yönetici</h3>
|
||
<p className="text-sm">{weekly?'Haftalık özetin bekleyen mesajları ve inceleme kararları gösterilir.':'Bekleyen işler ve son inceleme kararları gösterilir (en fazla 50).'} Belirsiz gönderimi sağlayıcı kaydıyla karşılaştırın. Bu işlemler yeni mesaj göndermez.</p>
|
||
<button type="button" disabled={busy} onClick={()=>act(load)} className="border rounded px-3 py-2">Gönderim listesini yenile</button>
|
||
{busy&&<p role="status">Gönderim kayıtları işleniyor…</p>}
|
||
{error&&<p role="alert" className="text-red-700">{error}</p>}
|
||
{rows?.length===0&&<p>İncelenecek veya bekleyen gönderim yok.</p>}
|
||
{rows?.map(row=><Review key={row.id} row={row} weekly={weekly} busy={busy} resolve={input=>act(async()=>{await accountRequest(endpoint,{method:'POST',body:JSON.stringify({id:row.id,...input})});await load();})}/>)}
|
||
</section>;
|
||
}
|
||
function Review({row,weekly,busy,resolve}){
|
||
const [note,setNote]=useState(''),[providerId,setProviderId]=useState(''),[confirmed,setConfirmed]=useState(false);
|
||
const labels={pending:'Bekliyor',sending:'Gönderiliyor',uncertain:'Sonuç belirsiz',sent_manual:'Gönderildiği yönetici tarafından doğrulandı',skipped:'Yeniden gönderilmeden kapatıldı'};
|
||
return <div className="border rounded p-3 space-y-2 text-sm">
|
||
<p className="font-bold">{row.subject}</p><p>{String(row.to)} · {labels[row.state]}</p><p className="break-all">Kayıt: {row.id}</p>
|
||
{row.error&&<p>{row.error}</p>}
|
||
{row.providerId&&<p className="break-all">Sağlayıcı kaydı: {row.providerId}</p>}
|
||
{row.review&&<p>{row.review.actor} · {new Date(row.review.at).toLocaleString('tr-TR', { timeZone: 'Europe/Istanbul' })} · {row.review.note}</p>}
|
||
{row.state==='uncertain'&&<fieldset disabled={busy} className="space-y-2">
|
||
<label className="block">Kontrol açıklaması<textarea className="block border w-full p-2" value={note} onChange={e=>setNote(e.target.value)} minLength={20}/></label>
|
||
<label className="block">Sağlayıcı gönderim kimliği<input className="block border w-full p-2" value={providerId} onChange={e=>setProviderId(e.target.value)}/></label>
|
||
<label className="block"><input type="checkbox" checked={confirmed} onChange={e=>setConfirmed(e.target.checked)}/> Sağlayıcı kaydında bu gönderimin kabul edildiğini kontrol ettim.</label>
|
||
<button type="button" disabled={note.trim().length<20||!providerId.trim()||!confirmed} onClick={()=>resolve({action:'confirmed_sent',note,providerId,confirmed})} className="border rounded p-2 mr-2">Gönderildi olarak kapat</button>
|
||
<button type="button" disabled={note.trim().length<20} onClick={()=>resolve({action:'skip',note})} className="border rounded p-2">Yeniden göndermeden kapat</button>
|
||
<p>{weekly?'İkinci seçenek bu mesajı yeniden göndermeden kapatır; teslim edildiği iddia edilmez. Diğer bekleyen mesajlar ve sonraki haftalar bu kararla kapatılmaz.':'İkinci seçenek bu grubu tekrar göndermez; teslim edildiği iddia edilmez. Sonraki yeni ihaleler için abonelik devam eder.'}</p>
|
||
</fieldset>}
|
||
</div>;
|
||
}
|