feat: review uncertain weekly brief deliveries with audit history

This commit is contained in:
2026-09-06 14:37:32 +03:00
parent 99a7f2f571
commit 7a01e4bed7
11 changed files with 141 additions and 15 deletions
+11 -10
View File
@@ -2,27 +2,28 @@ 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(){
export function DeliveryQueue({weekly=false}){
const {isAdmin,authUser}=useApp();
return isAdmin?<Queue key={`${authUser.email}:${getToken()}`}/>:null;
return isAdmin?<Queue weekly={weekly} key={`${authUser.email}:${getToken()}:${weekly}`}/>:null;
}
function Queue(){
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('/api/admin/notify/outbox');const rows=requireArray(data,'deliveries');if(alive.current)setRows(rows);};
const load=async()=>{const data=await accountRequest(endpoint);const rows=requireArray(data,'deliveries');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 className="bg-white border rounded-xl p-5 space-y-3">
<h3 className="font-bold">E-posta gönderim incelemesi · Yönetici</h3>
<p className="text-sm">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 e-posta göndermez.</p>
return <section aria-label={weekly?'Haftalık özet gönderim incelemesi':'E-posta gönderim incelemesi'} 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 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} busy={busy} resolve={input=>act(async()=>{await accountRequest('/api/admin/notify/outbox',{method:'POST',body:JSON.stringify({id:row.id,...input})});await load();})}/>)}
{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,busy,resolve}){
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">
@@ -36,7 +37,7 @@ function Review({row,busy,resolve}){
<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 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 disabled={note.trim().length<20} onClick={()=>resolve({action:'skip',note})} className="border rounded p-2">Yeniden göndermeden kapat</button>
<p>İkinci seçenek bu grubu tekrar göndermez; teslim edildiği iddia edilmez. Sonraki yeni ihaleler için abonelik devam eder.</p>
<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>;
}
+1
View File
@@ -301,6 +301,7 @@ const PwaPanel = () => {
{/* Otomatik bildirim abonelikleri */}
<NotifySubscriptions />
<DeliveryQueue />
<DeliveryQueue weekly />
</div>
);