revert: MakerWorld Statistik-Integration erstmal zurueckgebaut

This commit is contained in:
2026-07-12 11:41:23 +02:00
parent b0d090d85f
commit 5b4b0b6908
2 changed files with 1 additions and 345 deletions

View File

@@ -197,207 +197,6 @@ function OrdersInPeriod({ fromDate, toDate }) {
);
}
// MakerWorld Creator-Center Statistik
function MakerworldTab({ toast }) {
const [state, setState] = useState(null); // { configured, data, fetchedAt }
const [loading, setLoading] = useState(true);
const [tokenInfo, setTokenInfo] = useState(null); // { token, origin }
const [regenerating, setRegenerating] = useState(false);
const [showModels, setShowModels] = useState(false);
const load = () => {
setLoading(true);
api('/tools/makerworld/stats').then(setState).catch(()=>{}).finally(()=>setLoading(false));
};
const loadToken = () => {
api('/tools/makerworld/ingest-token').then(setTokenInfo).catch(()=>{});
};
useEffect(() => { load(); loadToken(); }, []);
const regenerateToken = async () => {
if (!window.confirm('Neues Bookmarklet erzeugen? Das alte funktioniert danach nicht mehr — du musst es neu ins Lesezeichen legen.')) return;
setRegenerating(true);
try {
const r = await api('/tools/makerworld/ingest-token/regenerate', { method:'POST' });
setTokenInfo(r);
toast?.('Neues Bookmarklet erzeugt — bitte neu einrichten');
} catch(e) { toast?.(e.message,'error'); }
finally { setRegenerating(false); }
};
// Skript-Inhalt getrennt von der Verpackung, damit er sauber Base64-kodiert werden kann —
// das eliminiert praktisch alle Leerzeichen/Anführungszeichen im übertragenen Code, die von
// mobilen Tastaturen (intelligente Anführungszeichen) oder URL-Feldern verändert werden könnten.
const bookmarkletInner = tokenInfo ? `(function(){try{var el=document.getElementById('__NEXT_DATA__');if(!el){alert('MakerWorld: __NEXT_DATA__ nicht gefunden. Bist du auf der Statistikseite (/my/data-overview/model)?');return;}var nd=JSON.parse(el.textContent);var sd=nd.props&&nd.props.pageProps&&nd.props.pageProps.statisticalData;if(!sd||!sd.summary){alert('MakerWorld: Keine Statistikdaten auf dieser Seite gefunden.');return;}fetch('${tokenInfo.origin}/api/tools/makerworld/ingest',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token:'${tokenInfo.token}',statisticalData:sd})}).then(function(r){return r.json();}).then(function(j){alert(j.ok?'DickenDock aktualisiert ✓':('Fehler: '+(j.error||'unbekannt')));}).catch(function(e){alert('Fehler: '+e.message);});}catch(e){alert('Fehler: '+e.message);}})();` : '';
const bookmarkletCode = tokenInfo
? (() => {
const b64 = btoa(unescape(encodeURIComponent(bookmarkletInner)));
const urlSafeB64 = b64.replace(/\+/g, '-').replace(/\//g, '_');
return `javascript:eval(atob('${urlSafeB64}'.replace(/-/g,'+').replace(/_/g,'/')))`;
})()
: '';
const codeRef = useRef(null);
const copyBookmarklet = async () => {
// Moderne Clipboard-API zuerst versuchen
try {
await navigator.clipboard.writeText(bookmarkletCode);
toast?.('Bookmarklet-Code kopiert');
return;
} catch {}
// Fallback: klassisches execCommand über eine unsichtbare Textarea
// (Clipboard-API meldet in manchen PWA/Mobile-Kontexten fälschlich Erfolg
// oder schlägt ohne Fehler fehl — dieser Weg ist zuverlässiger)
try {
const ta = document.createElement('textarea');
ta.value = bookmarkletCode;
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.focus();
ta.select();
const ok = document.execCommand('copy');
document.body.removeChild(ta);
if (ok) { toast?.('Bookmarklet-Code kopiert'); return; }
} catch {}
toast?.('Kopieren nicht möglich — bitte Code unten manuell markieren und kopieren','error');
selectAllCode();
};
const selectAllCode = () => {
if (codeRef.current) {
codeRef.current.focus();
codeRef.current.select();
}
};
if (loading) return <div style={{color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:12}}>Lädt</div>;
const d = state?.data;
const fmtInt = v => (v ?? 0).toLocaleString('de-DE');
const fmtAgo = ts => {
if (!ts) return '—';
const min = Math.round((Date.now()-ts)/60000);
if (min < 1) return 'gerade eben';
if (min < 60) return `vor ${min} Min.`;
if (min < 1440) return `vor ${Math.round(min/60)} Std.`;
return `vor ${Math.round(min/1440)} Tag(en)`;
};
return (
<div>
<div style={S.card}>
<div style={S.head}>EINRICHTUNG (EINMALIG)</div>
<div style={{...S.sub,marginBottom:10}}>
Kein Login, kein Passwort nötig nur ein Lesezeichen. Öffne die Seite{' '}
<code style={{color:'#4ecdc4'}}>makerworld.com/de/my/data-overview/model</code>{' '}
in deinem eigenen Browser (wo du eh eingeloggt bist), klicke dann auf das Lesezeichen
die aktuell auf der Seite geladenen Zahlen werden direkt an DickenDock geschickt.
</div>
<div style={{...S.sub,marginBottom:6,fontWeight:700,color:'rgba(255,255,255,0.6)'}}>Am PC:</div>
<div style={{...S.sub,marginBottom:10}}>
Lesezeichenleiste anzeigen, dann diesen Link dorthin ziehen:
</div>
<div style={{marginBottom:14}}>
<a href={bookmarkletCode} onClick={e=>e.preventDefault()} draggable="true"
style={{...S.btn('#4ecdc4'),display:'inline-block',cursor:'grab',textDecoration:'none'}}>
📌 DickenDock Sync
</a>
</div>
<div style={{...S.sub,marginBottom:6,fontWeight:700,color:'rgba(255,255,255,0.6)'}}>Am Handy (Chrome):</div>
<div style={{...S.sub,marginBottom:10}}>
Normale Lesezeichen mit <code style={{color:'#4ecdc4'}}>javascript:</code>-Code werden von mobilen
Browsern beim Antippen mittlerweile blockiert. Zuverlässig funktioniert stattdessen der
"Alternative Suchmaschine"-Trick:
<br/>1. Code unten kopieren
<br/>2. Chrome Einstellungen Suchmaschine Suchmaschinen verwalten "Hinzufügen"
<br/>3. Suchmaschine: z.B. <code style={{color:'#4ecdc4'}}>DickenDock Sync</code>, Suchwort: z.B. <code style={{color:'#4ecdc4'}}>dds</code>, URL: den kopierten Code einfügen
<br/>4. Speichern, dann auf der MakerWorld-Statistikseite in die Adresszeile tippen, <code style={{color:'#4ecdc4'}}>dds</code> eingeben, Leertaste, Enter
</div>
<button onClick={copyBookmarklet} style={{...S.btn('#4ecdc4')}}>
📋 Bookmarklet-Code kopieren
</button>
<div style={{...S.sub,marginTop:10,marginBottom:6}}>
Falls der Button nichts in die Zwischenablage legt (kommt auf manchen Handys/PWAs vor):
hier antippen, Alles auswählen" wählen, dann normal kopieren.
</div>
<textarea ref={codeRef} readOnly value={bookmarkletCode} onClick={selectAllCode}
rows={3} style={{...S.inp,resize:'vertical',fontSize:9,fontFamily:'monospace',wordBreak:'break-all'}}/>
<div style={{...S.sub,marginTop:10}}>
Firefox für Android unterstützt diesen Trick leider nicht zuverlässig — dafür entweder kurz zu
Chrome wechseln, oder den <b>Kiwi Browser</b> installieren (Chromium-basiert, unterstützt
klassische Lesezeichen-Bookmarklets wieder ganz normal).
</div>
<div style={{display:'flex',gap:8,marginTop:14,paddingTop:14,borderTop:'1px solid rgba(255,255,255,0.07)'}}>
<button onClick={regenerateToken} disabled={regenerating} style={{...S.btn('#f87171'),opacity:regenerating?0.5:1}}>
{regenerating?'…':'🔄 Bookmarklet zurücksetzen'}
</button>
</div>
</div>
{!state?.configured && (
<div style={S.card}>
<div style={{color:'rgba(255,255,255,0.4)',fontFamily:'monospace',fontSize:12}}>
Noch keine Daten empfangen — Lesezeichen einrichten und einmal anklicken.
</div>
</div>
)}
{d && (
<>
<div style={S.card}>
<div style={S.head}>CREATOR CENTER {state.fetchedAt ? `· ${fmtAgo(state.fetchedAt)}` : ''}</div>
<div style={{display:'flex',flexWrap:'wrap',gap:20,marginBottom:16}}>
<KPI label="Downloads" value={fmtInt(d.downloads)} color="#4ecdc4"/>
<KPI label="Prints" value={fmtInt(d.prints)} color="#4ecdc4"/>
<KPI label="Likes" value={fmtInt(d.likes)} color="#ff6b9d"/>
<KPI label="Collections" value={fmtInt(d.collections)} color="#ff6b9d"/>
<KPI label="Boosts" value={fmtInt(d.boosts)} color="#ffe66d"/>
<KPI label="Follower" value={fmtInt(d.followers)} color="#ffe66d"/>
</div>
<div style={{borderTop:'1px solid rgba(255,255,255,0.06)',paddingTop:16,display:'flex',flexWrap:'wrap',gap:20}}>
<KPI label="Views" value={fmtInt(d.views)} color="#60a5fa"/>
<KPI label="Impressions" value={fmtInt(d.impressions)} color="#60a5fa"/>
<KPI label="Creator Points" value={fmtInt(d.creatorPoints)} color="#c084fc"/>
<KPI label="Model Points" value={fmtInt(d.modelPoints)} color="#c084fc" sub="aus Modellen"/>
<KPI label="MakerLab Points" value={fmtInt(d.instructionPoints)} color="#c084fc" sub="aus Anleitungen"/>
</div>
</div>
{d.models?.length > 0 && (
<div style={S.card}>
<div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:showModels?10:0}}>
<div style={{...S.head,marginBottom:0}}>MODELLE ({d.models.length})</div>
<button onClick={()=>setShowModels(p=>!p)} style={{...S.btn('#888888'),fontSize:10}}>
{showModels ? '▲ einklappen' : '▼ anzeigen'}
</button>
</div>
{showModels && [...d.models].sort((a,b)=>b.downloads-a.downloads).map(m => (
<div key={m.id} style={{display:'flex',alignItems:'center',gap:10,padding:'8px 0',
borderBottom:'1px solid rgba(255,255,255,0.05)',flexWrap:'wrap'}}>
<span style={{color:'rgba(255,255,255,0.8)',fontFamily:'monospace',fontSize:12,flex:1,minWidth:160}}>
{m.title}
</span>
<span style={{color:'#4ecdc4',fontFamily:"'Space Mono',monospace",fontSize:11}}>↓{fmtInt(m.downloads)}</span>
<span style={{color:'#ff6b9d',fontFamily:"'Space Mono',monospace",fontSize:11}}>♥{fmtInt(m.likes)}</span>
<span style={{color:'#60a5fa',fontFamily:"'Space Mono',monospace",fontSize:11}}>👁{fmtInt(m.views)}</span>
<span style={{color:'#c084fc',fontFamily:"'Space Mono',monospace",fontSize:11}}>{m.points?.toFixed?.(1) ?? m.points}pt</span>
</div>
))}
</div>
)}
</>
)}
</div>
);
}
export default function Statistik({ mobile, user, toast }) {
const [fromDate, setFromDate] = useState('');
const [toDate, setToDate] = useState(todayLocal());
@@ -471,7 +270,7 @@ export default function Statistik({ mobile, user, toast }) {
{/* ── Tabs ── */}
<div style={{display:'flex',gap:4,marginBottom:16,overflowX:'auto',overflowY:'hidden',
scrollbarWidth:'none',WebkitOverflowScrolling:'touch',paddingBottom:2}}>
{[['overview','📈 Übersicht'],['revenue','💰 Einnahmen'],['expenses','💸 Ausgaben'],['orders','📦 Bestellungen'],['makerworld','🖨️ MakerWorld']].map(([id,label])=>(
{[['overview','📈 Übersicht'],['revenue','💰 Einnahmen'],['expenses','💸 Ausgaben'],['orders','📦 Bestellungen']].map(([id,label])=>(
<button key={id} onClick={()=>setTab(id)} style={{
background: tab===id ? 'rgba(255,255,255,0.08)' : 'transparent',
border: `1px solid ${tab===id ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.08)'}`,
@@ -706,11 +505,6 @@ export default function Statistik({ mobile, user, toast }) {
{tab==='orders' && (
<OrdersInPeriod fromDate={fromDate} toDate={toDate}/>
)}
{/* ══ MAKERWORLD ══ */}
{tab==='makerworld' && (
<MakerworldTab toast={toast}/>
)}
</div>
);
}