fix: persist weekly brief preference and expose request failures
This commit is contained in:
@@ -1,48 +1,41 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { CalendarClock, Eye, Loader2 } from 'lucide-react';
|
||||
import { getToken } from '../../services/auth';
|
||||
|
||||
/**
|
||||
* Haftalık brief tercihi (Pazartesi 08:00): bu hafta yapılacak takipli
|
||||
* ihaleler + geçen hafta sonuçlanan takipliler — Telegram ve e-posta.
|
||||
* "Önizle" şu anki içeriği gösterir; anahtar kapatır.
|
||||
*/
|
||||
const iste = (opts = {}) => fetch('/api/me/brief', { headers: { Authorization: `Bearer ${getToken()}`, 'Content-Type': 'application/json' }, ...opts }).then((r) => r.json());
|
||||
|
||||
export const BriefTercihi = () => {
|
||||
const [kapali, setKapali] = useState(false);
|
||||
const [metin, setMetin] = useState(null);
|
||||
const [mesgul, setMesgul] = useState(false);
|
||||
|
||||
useEffect(() => { iste().then((d) => { if (d?.success) setKapali(!!d.kapali); }).catch(() => {}); }, []);
|
||||
|
||||
const degistir = async () => {
|
||||
const yeni = !kapali; setKapali(yeni);
|
||||
try { await iste({ method: 'POST', body: JSON.stringify({ kapali: yeni }) }); } catch { setKapali(!yeni); }
|
||||
};
|
||||
const onizle = async () => {
|
||||
setMesgul(true);
|
||||
try { const d = await iste(); setMetin(d?.metin || 'Önizleme alınamadı.'); } catch { setMetin('Önizleme alınamadı.'); } finally { setMesgul(false); }
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-2xl border border-slate-200 shadow-xs p-5 space-y-3">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 className="font-extrabold text-sm text-slate-900 flex items-center gap-2"><CalendarClock className="w-4 h-4 text-amber-500" /> Haftalık Brief</h3>
|
||||
<p className="text-[11px] text-slate-500 mt-1">Her Pazartesi 08:00: bu hafta yapılacak takipli ihaleler ve geçen hafta sonuçlanan takipliler — Telegram (bağlıysa) ve e-posta ile tek mesaj.</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button onClick={onizle} disabled={mesgul} className="text-[11px] flex items-center gap-1 border border-slate-200 rounded-lg px-2 py-1 hover:bg-slate-50">
|
||||
{mesgul ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />} Önizle
|
||||
</button>
|
||||
<button role="switch" aria-checked={!kapali} onClick={degistir}
|
||||
className={`relative inline-flex h-5 w-9 items-center rounded-full transition ${kapali ? 'bg-slate-300' : 'bg-emerald-500'}`}>
|
||||
<span className={`inline-block h-4 w-4 transform rounded-full bg-white transition ${kapali ? 'translate-x-0.5' : 'translate-x-4'}`} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{metin && <pre className="whitespace-pre-wrap rounded-lg border border-slate-200 bg-slate-50 p-3 text-xs text-slate-700 font-sans leading-relaxed">{metin}</pre>}
|
||||
</div>
|
||||
);
|
||||
import React,{useEffect,useRef,useState} from 'react';
|
||||
import {CalendarClock} from 'lucide-react';
|
||||
import {getToken} from '../../services/auth';
|
||||
import {useApp} from '../../context/AppContext';
|
||||
import {getWeeklyBrief,saveWeeklyBrief} from '../../services/weeklyBrief';
|
||||
export const BriefTercihi=()=>{
|
||||
const {authUser}=useApp();
|
||||
return <BriefPanel key={`${authUser?.email}:${getToken()}`}/>;
|
||||
};
|
||||
function BriefPanel(){
|
||||
const [value,setValue]=useState(null),[metin,setMetin]=useState(null),[busy,setBusy]=useState(true),[error,setError]=useState(null),[attempt,setAttempt]=useState(0);
|
||||
const lifetime=useRef(null),lock=useRef(false);
|
||||
useEffect(()=>{
|
||||
const controller=new AbortController();lifetime.current=controller;lock.current=true;setBusy(true);setError(null);
|
||||
getWeeklyBrief({signal:controller.signal}).then(data=>{if(!controller.signal.aborted)setValue(data);})
|
||||
.catch(e=>{if(!controller.signal.aborted)setError(e.message);})
|
||||
.finally(()=>{if(!controller.signal.aborted){lock.current=false;setBusy(false);}});
|
||||
return()=>controller.abort();
|
||||
},[attempt]);
|
||||
const run=async preview=>{
|
||||
if(lock.current||!value)return;
|
||||
const signal=lifetime.current.signal;lock.current=true;setBusy(true);setError(null);
|
||||
try{
|
||||
const data=preview?await getWeeklyBrief({preview:true,signal}):await saveWeeklyBrief(!value.kapali,value.revision,{signal});
|
||||
if(!signal.aborted){setValue(data);if(preview)setMetin(data.metin);}
|
||||
}catch(e){if(!signal.aborted)setError(e.message);}
|
||||
finally{if(!signal.aborted){lock.current=false;setBusy(false);}}
|
||||
};
|
||||
return <section aria-label="Haftalık özet ayarı" className="bg-white rounded-2xl border border-slate-200 p-5 space-y-3">
|
||||
<h3 className="font-bold text-sm flex gap-2"><CalendarClock className="w-4 h-4"/>Haftalık Brief</h3>
|
||||
<p className="text-xs text-slate-500">Haftanın takipli ihaleleri ve geçen hafta sonuçlanan takipliler için özet tercihi. Gönderim, sunucu zamanlayıcısının ve ilgili e-posta/Telegram hizmetinin etkin olmasına bağlıdır.</p>
|
||||
<div className="flex gap-3 flex-wrap">
|
||||
{value&&<button role="switch" aria-label="Haftalık özet gönderimi" aria-checked={!value.kapali} disabled={busy} onClick={()=>run(false)} className="border rounded px-3 py-2 text-xs">{value.kapali?'Haftalık özet kapalı':'Haftalık özet açık'}</button>}
|
||||
<button disabled={busy||!value} onClick={()=>run(true)} className="border rounded px-3 py-2 text-xs">Önizle</button>
|
||||
<button disabled={busy} onClick={()=>setAttempt(n=>n+1)} className="border rounded px-3 py-2 text-xs">Özet ayarını yenile</button>
|
||||
</div>
|
||||
{busy&&<p role="status" className="text-xs">Haftalık özet işlemi sürüyor…</p>}
|
||||
{error&&<p role="alert" className="text-xs text-red-700">{error}</p>}
|
||||
{metin!==null&&<pre className="whitespace-pre-wrap rounded border bg-slate-50 p-3 text-xs">{metin}</pre>}
|
||||
</section>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user