revert: MakerWorld Statistik-Integration erstmal zurueckgebaut
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user