fix: KoePi Prospekt-Titel aus advertiser.name statt Freitext, REWE-KW aus Datum berechnet, KoePi oeffentlicher, schreibgeschuetzter Teilen-Link ohne Login, KoePi oeffentlicher Link redirected bei ungueltigem Token zu Google, wie bei den anderen Public-Links

This commit is contained in:
2026-07-18 13:57:45 +02:00
parent 6b4b23ea6c
commit c637560f3f
3 changed files with 384 additions and 7 deletions

View File

@@ -262,6 +262,37 @@ export default function Koepi({ toast }) {
finally { setResolvingStores(false); }
};
const [shareInfo, setShareInfo] = useState(null); // { token, url } | null
const [shareOpen, setShareOpen] = useState(false);
const [shareBusy, setShareBusy] = useState(false);
useEffect(() => {
if (!isAdmin) return;
api('/tools/koepi/share-link').then(setShareInfo).catch(()=>{});
}, [isAdmin]);
const createShareLink = async () => {
setShareBusy(true);
try {
const r = await api('/tools/koepi/share-link', { method:'POST', body:{} });
setShareInfo(r);
toast('🔗 Neuer Link erzeugt — alter Link ist damit ungültig');
} catch(e) { toast?.(e.message||'Fehler','error'); }
finally { setShareBusy(false); }
};
const disableShareLink = async () => {
setShareBusy(true);
try {
await api('/tools/koepi/share-link/disable', { method:'POST', body:{} });
setShareInfo({ token:null, url:null });
toast('🔗 Link deaktiviert');
} catch(e) { toast?.(e.message||'Fehler','error'); }
finally { setShareBusy(false); }
};
const copyShareLink = async () => {
if (!shareInfo?.url) return;
try { await navigator.clipboard.writeText(shareInfo.url); toast('Link kopiert'); }
catch { toast?.('Kopieren nicht möglich — bitte manuell markieren','error'); }
};
return (
<div style={{ maxWidth:700, margin:'0 auto', padding:'0 14px' }}>
{/* Header */}
@@ -279,6 +310,37 @@ export default function Koepi({ toast }) {
{resolvingStores ? '⏳ läuft…' : '🏪 Filial-Liste neu auflösen'}
</button>
<button onClick={clearCache} style={{ ...S.btn('#666666',true), fontSize:10, flexShrink:0, whiteSpace:'nowrap' }}>🗑 Cache leeren</button>
<button onClick={()=>setShareOpen(v=>!v)} style={{ ...S.btn(shareInfo?.token?'#ffe66d':'#888888',true), fontSize:10, flexShrink:0, whiteSpace:'nowrap' }}>
🔗 Teilen{shareInfo?.token ? ' (aktiv)' : ''}
</button>
</div>
)}
{isAdmin && shareOpen && (
<div style={{ ...S.card, marginTop:10 }}>
<div style={{ ...S.sub, marginBottom:10 }}>
Öffentlicher, schreibgeschützter Link ohne Login zeigt zuerst die Prospekte, man kann dort
auch zu den Angeboten wechseln. Keine Admin-Funktionen sichtbar/erreichbar über diesen Link.
</div>
{shareInfo?.token ? (
<>
<div style={{ display:'flex', gap:8, marginBottom:10, flexWrap:'wrap' }}>
<input readOnly value={shareInfo.url} onClick={e=>e.target.select()} style={{ ...S.inp, flex:1, minWidth:200, fontSize:11 }}/>
<button onClick={copyShareLink} style={{ ...S.btn('#4ecdc4') }}>📋 Kopieren</button>
</div>
<div style={{ display:'flex', gap:8, flexWrap:'wrap' }}>
<button onClick={createShareLink} disabled={shareBusy} style={{ ...S.btn('#f59e0b'), opacity:shareBusy?0.5:1 }}>
{shareBusy?'…':'🔄 Neuen Link erzeugen (alter wird ungültig)'}
</button>
<button onClick={disableShareLink} disabled={shareBusy} style={{ ...S.btn('#f87171'), opacity:shareBusy?0.5:1 }}>
{shareBusy?'…':'🚫 Link deaktivieren'}
</button>
</div>
</>
) : (
<button onClick={createShareLink} disabled={shareBusy} style={{ ...S.btn('#4ecdc4'), opacity:shareBusy?0.5:1 }}>
{shareBusy?'…':'🔗 Link erzeugen'}
</button>
)}
</div>
)}
</div>