frontend: page créateur repensée — métriques calculées + onglet par plateforme + grille contenu images/vidéos
- bandeau KPI multi-plateforme calculé (portée, engagement moyen, cadence, contenus) - onglets: Vue densemble (barres de portée) + un onglet par plateforme - par plateforme: grille KPI détaillée + badges statut + grille de contenu récent (images/vidéos, play/durée, métriques) avec repli icône si CDN expiré Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2 changed files +472 −224
modified
frontend/dist/index.html
+236 −112
@@ -722,61 +722,91 @@ select.f.active{background-color:var(--accent-soft);color:var(--ink);border-colo | ||
| 722 | 722 | background:var(--surface);border:1.5px solid var(--ink);border-radius:var(--r-card); |
| 723 | 723 | padding:13px 17px;box-shadow:var(--shadow-off-soft)} |
| 724 | 724 | .sect-title{font-size:19px;margin:36px 0 15px;display:flex;align-items:center;gap:10px;text-transform:uppercase} |
| 725 | −.acc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px} | |
| 726 | −@media(max-width:640px){.acc-grid{grid-template-columns:1fr}} | |
| 727 | −.acc-card{display:flex;flex-direction:column;background:var(--surface); | |
| 728 | − border:1.5px solid var(--ink);border-radius:var(--r-card); | |
| 729 | − box-shadow:var(--shadow-off-soft);transition:transform .16s ease,box-shadow .16s ease; | |
| 730 | − min-width:0;overflow:hidden} | |
| 731 | −.acc-card:hover{transform:translate(-2px,-2px);box-shadow:var(--shadow-off)} | |
| 732 | −.acc{display:flex;align-items:center;gap:13px;padding:14px 16px;min-width:0} | |
| 733 | −/* rangée de stats riches */ | |
| 734 | −.astat-row{display:flex;flex-wrap:wrap;gap:6px;padding:0 16px 10px} | |
| 735 | −.astat{display:flex;flex-direction:column;background:var(--surface-2); | |
| 736 | − border:1px solid var(--line);border-radius:var(--r-ctl);padding:4px 9px;line-height:1.1} | |
| 737 | −.astat b{font-family:var(--font-display);font-size:13px;color:var(--ink);font-weight:700} | |
| 738 | −.astat span{font-family:var(--font-mono);font-size:8.5px;color:var(--ink-3); | |
| 739 | − text-transform:uppercase;letter-spacing:.05em;font-weight:700;margin-top:1px} | |
| 740 | −.abadge-row{display:flex;flex-wrap:wrap;gap:5px;padding:0 16px 12px} | |
| 741 | −.abadge{display:inline-flex;background:var(--lime-soft);color:var(--green-deep); | |
| 742 | − border:1px solid var(--line);border-radius:var(--r-ctl);padding:2px 8px; | |
| 743 | − font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.02em} | |
| 744 | −.abadge.live{background:#ffe3e0;color:#b0201a;border-color:#b0201a} | |
| 745 | −/* bande de contenu récent */ | |
| 746 | −.cstrip{border-top:1px dashed var(--line);padding:11px 0 13px} | |
| 747 | −.cstrip-h{font-family:var(--font-mono);font-size:9.5px;font-weight:700;color:var(--ink-3); | |
| 748 | − text-transform:uppercase;letter-spacing:.07em;padding:0 16px 8px} | |
| 749 | −.cstrip-scroll{display:flex;gap:9px;overflow-x:auto;padding:0 16px 3px;scroll-snap-type:x proximity} | |
| 750 | −.cstrip-scroll::-webkit-scrollbar{height:5px} | |
| 751 | −.cstrip-scroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px} | |
| 752 | −.cstrip-item{flex:none;width:120px;scroll-snap-align:start;text-decoration:none;color:inherit; | |
| 753 | − display:flex;flex-direction:column;gap:4px} | |
| 754 | −.cstrip-thumb{position:relative;display:block;width:120px;height:120px;border-radius:9px; | |
| 755 | − background-size:cover;background-position:center;background-color:var(--surface-2); | |
| 756 | − border:1px solid var(--line);overflow:hidden} | |
| 757 | −.cstrip-thumb.noimg{display:flex;align-items:center;justify-content:center} | |
| 758 | −.cstrip-thumb.noimg svg{width:34px;height:34px;opacity:.35} | |
| 759 | −.cstrip-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%); | |
| 760 | − color:#fff;font-size:20px;text-shadow:0 1px 4px rgba(0,0,0,.6)} | |
| 761 | −.cstrip-dur{position:absolute;right:5px;bottom:5px;background:rgba(0,0,0,.78);color:#fff; | |
| 762 | − font-family:var(--font-mono);font-size:9px;padding:1px 5px;border-radius:5px} | |
| 763 | −.cstrip-cap{font-size:11px;line-height:1.25;color:var(--ink-2);display:-webkit-box; | |
| 764 | − -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} | |
| 765 | −.cstrip-meta{font-family:var(--font-mono);font-size:9px;color:var(--ink-3);line-height:1.2} | |
| 766 | −.acc .plat{width:44px;height:44px;border-radius:9px;flex:none} | |
| 767 | −.acc .plat svg{width:20px;height:20px} | |
| 768 | −.acc-mid{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden} | |
| 769 | −.acc-name{font-family:var(--font-display);font-weight:700;font-size:14px;display:flex;align-items:center;gap:6px} | |
| 770 | −.acc-handle{font-size:12px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis; | |
| 771 | − white-space:nowrap;display:block;max-width:100%} | |
| 772 | −.acc-right{margin-left:auto;text-align:right;flex:none;padding-left:8px} | |
| 773 | −.acc-right b{display:block;font-family:var(--font-display);font-size:16.5px;color:var(--ink);font-weight:700} | |
| 774 | −.acc-right span{font-family:var(--font-mono);font-size:9px;color:var(--ink-3); | |
| 775 | − text-transform:uppercase;letter-spacing:.06em;font-weight:700} | |
| 776 | −.conf{display:inline-flex;padding:1px 7px;border-radius:var(--r-ctl); | |
| 777 | − font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.03em} | |
| 725 | +/* ===== page créateur : métriques calculées + onglets par plateforme ===== */ | |
| 726 | +.hkpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin:22px 0 6px} | |
| 727 | +.hkpi{background:var(--surface);border:1.5px solid var(--ink);border-radius:var(--r-card); | |
| 728 | + box-shadow:var(--shadow-off-soft);padding:13px 15px} | |
| 729 | +.hkpi b{display:block;font-family:var(--font-display);font-size:22px;font-weight:800;color:var(--ink);line-height:1} | |
| 730 | +.hkpi span{display:block;margin-top:5px;font-family:var(--font-mono);font-size:9.5px;color:var(--ink-3); | |
| 731 | + text-transform:uppercase;letter-spacing:.05em;font-weight:700} | |
| 732 | +/* barre d'onglets */ | |
| 733 | +.ptabs{display:flex;gap:8px;overflow-x:auto;margin:26px 0 0;padding-bottom:2px} | |
| 734 | +.ptabs::-webkit-scrollbar{height:5px}.ptabs::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px} | |
| 735 | +.ptab{flex:none;display:inline-flex;align-items:center;gap:7px;cursor:pointer; | |
| 736 | + background:var(--surface);border:1.5px solid var(--ink);border-bottom:none; | |
| 737 | + border-radius:12px 12px 0 0;padding:10px 15px;font-family:var(--font-display); | |
| 738 | + font-weight:700;font-size:13px;color:var(--ink-2);transition:.14s;margin-bottom:-1.5px} | |
| 739 | +.ptab:hover{background:var(--surface-2)} | |
| 740 | +.ptab.on{background:var(--accent);color:var(--on-accent);border-color:var(--ink)} | |
| 741 | +.ptab.on .ptab-ico{color:var(--on-accent)!important} | |
| 742 | +.ptab-ico{width:16px;height:16px;display:inline-flex}.ptab-ico svg{width:16px;height:16px} | |
| 743 | +.ptab-n{font-family:var(--font-mono);font-size:10px;opacity:.75;font-weight:700} | |
| 744 | +.ppanels{border:1.5px solid var(--ink);border-radius:0 12px 12px 12px; | |
| 745 | + background:var(--surface);box-shadow:var(--shadow-off-soft);padding:20px 18px 22px} | |
| 746 | +.ppanel{display:none}.ppanel.on{display:block;animation:fade .2s ease} | |
| 747 | +@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}} | |
| 748 | +.ppanel-head{display:flex;align-items:center;gap:13px} | |
| 749 | +.ppanel-head .plat{width:46px;height:46px;border-radius:10px;flex:none;display:flex;align-items:center;justify-content:center} | |
| 750 | +.ppanel-head .plat svg{width:22px;height:22px} | |
| 751 | +.ppanel-id{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px} | |
| 752 | +.ppanel-name{font-family:var(--font-display);font-weight:800;font-size:17px;display:flex;align-items:center;gap:6px} | |
| 753 | +.ppanel-handle{font-size:13px;color:var(--green);font-weight:600} | |
| 754 | +.conf{display:inline-flex;padding:2px 9px;border-radius:var(--r-ctl); | |
| 755 | + font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.03em;flex:none} | |
| 778 | 756 | .conf.hi{background:var(--lime-soft);color:var(--green-deep)} |
| 779 | 757 | .conf.mid{background:var(--amber-soft);color:#8a5a12} |
| 758 | +.abadge-row{display:flex;flex-wrap:wrap;gap:6px;margin:12px 0 4px} | |
| 759 | +.abadge{display:inline-flex;background:var(--lime-soft);color:var(--green-deep); | |
| 760 | + border:1px solid var(--line);border-radius:var(--r-ctl);padding:3px 9px; | |
| 761 | + font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.02em} | |
| 762 | +.abadge.ok{background:var(--surface-2);color:var(--ink-2)} | |
| 763 | +.abadge.live{background:#ffe3e0;color:#b0201a;border-color:#b0201a} | |
| 764 | +/* grille KPI par plateforme */ | |
| 765 | +.kpi-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;margin:16px 0 4px} | |
| 766 | +.kpi{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-ctl);padding:11px 13px} | |
| 767 | +.kpi b{display:block;font-family:var(--font-display);font-size:19px;font-weight:800;color:var(--ink);line-height:1} | |
| 768 | +.kpi span{display:block;margin-top:4px;font-family:var(--font-mono);font-size:9px;color:var(--ink-3); | |
| 769 | + text-transform:uppercase;letter-spacing:.04em;font-weight:700} | |
| 770 | +.ppanel-top{display:inline-flex;margin:12px 0 2px;background:var(--surface-2);border:1px solid var(--line); | |
| 771 | + border-radius:var(--r-ctl);padding:8px 13px;font-size:12.5px;font-weight:700;color:var(--ink-2)} | |
| 772 | +.ppanel-top:hover{color:var(--green)} | |
| 773 | +.ppanel-foot{margin-top:14px;font-family:var(--font-mono);font-size:10px;color:var(--ink-3)} | |
| 774 | +/* grille de contenu récent (images/vidéos) */ | |
| 775 | +.cg-title{font-family:var(--font-display);font-size:14px;font-weight:800;margin:20px 0 12px;color:var(--ink)} | |
| 776 | +.cg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:13px} | |
| 777 | +.cg-card{display:flex;flex-direction:column;text-decoration:none;color:inherit; | |
| 778 | + border:1.5px solid var(--ink);border-radius:var(--r-card);overflow:hidden; | |
| 779 | + background:var(--surface);box-shadow:var(--shadow-off-soft);transition:transform .15s,box-shadow .15s} | |
| 780 | +.cg-card:hover{transform:translate(-2px,-2px);box-shadow:var(--shadow-off)} | |
| 781 | +.cg-thumb{position:relative;display:block;width:100%;aspect-ratio:1/1;background:var(--surface-2);overflow:hidden} | |
| 782 | +.cg-thumb img{width:100%;height:100%;object-fit:cover;display:block} | |
| 783 | +.cg-thumb.noimg{display:flex;align-items:center;justify-content:center} | |
| 784 | +.cg-thumb.noimg svg{width:44px;height:44px;opacity:.3} | |
| 785 | +.cg-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff; | |
| 786 | + font-size:26px;text-shadow:0 2px 6px rgba(0,0,0,.7);pointer-events:none} | |
| 787 | +.cg-dur{position:absolute;right:6px;bottom:6px;background:rgba(0,0,0,.8);color:#fff; | |
| 788 | + font-family:var(--font-mono);font-size:10px;padding:2px 6px;border-radius:5px} | |
| 789 | +.cg-pin{position:absolute;left:6px;top:6px;font-size:13px} | |
| 790 | +.cg-body{padding:9px 11px 11px;display:flex;flex-direction:column;gap:5px} | |
| 791 | +.cg-cap{font-size:12px;line-height:1.3;color:var(--ink-2);display:-webkit-box; | |
| 792 | + -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:31px} | |
| 793 | +.cg-cap.dim{color:var(--ink-3)} | |
| 794 | +.cg-meta{font-family:var(--font-mono);font-size:10px;color:var(--ink-3);display:flex; | |
| 795 | + justify-content:space-between;gap:6px;flex-wrap:wrap} | |
| 796 | +.cg-when{color:var(--ink-3);opacity:.8} | |
| 797 | +.cg-empty{padding:26px;text-align:center;color:var(--ink-3);font-size:13px} | |
| 798 | +.cg-empty svg{width:20px;height:20px;vertical-align:-4px;opacity:.4;margin-right:6px} | |
| 799 | +/* vue d'ensemble : barres de portée */ | |
| 800 | +.obars{display:flex;flex-direction:column;gap:11px} | |
| 801 | +.obar{display:flex;align-items:center;gap:11px} | |
| 802 | +.obar-ico{width:32px;height:32px;border-radius:8px;flex:none;display:flex;align-items:center;justify-content:center} | |
| 803 | +.obar-ico svg{width:16px;height:16px} | |
| 804 | +.obar-lab{width:96px;flex:none;font-family:var(--font-display);font-weight:700;font-size:13px} | |
| 805 | +.obar-track{flex:1;height:12px;background:var(--surface-2);border:1px solid var(--line);border-radius:6px;overflow:hidden} | |
| 806 | +.obar-fill{display:block;height:100%;border-radius:6px 0 0 6px;min-width:3px} | |
| 807 | +.obar-val{width:78px;flex:none;text-align:right;font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--ink)} | |
| 808 | +@media(max-width:640px){.obar-lab{width:64px;font-size:11px}.obar-val{width:60px;font-size:11px} | |
| 809 | + .cg-grid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}} | |
| 780 | 810 | .linkinbio{display:inline-flex;align-items:center;gap:10px;margin-top:22px; |
| 781 | 811 | background:var(--accent);color:var(--on-accent);border:1.5px solid var(--ink); |
| 782 | 812 | border-radius:var(--r-ctl);padding:11px 20px;font-family:var(--font-display); |
@@ -1209,26 +1239,127 @@ async function reload(more){ | ||
| 1209 | 1239 | const _relTime=ts=>{if(!ts)return"";if(typeof ts==="string"&&!/^\d+$/.test(ts))return esc(ts.slice(0,10)); |
| 1210 | 1240 | const d=Math.floor((Date.now()/1000-Number(ts))/86400); |
| 1211 | 1241 | return d<=0?"aujourd'hui":d===1?"hier":d<30?d+" j":d<365?Math.floor(d/30)+" mois":Math.floor(d/365)+" ans";}; |
| 1212 | −function contentStrip(a){ | |
| 1213 | − const m=a.metrics||{}; | |
| 1214 | − const items=m.recent_posts||m.recent_videos||m.recent_tweets||[]; | |
| 1215 | − if(!items.length)return""; | |
| 1216 | − const cards=items.slice(0,8).map(it=>{ | |
| 1242 | +const _relTimeC=ts=>_relTime(ts); | |
| 1243 | +/* image de contenu : IG/CDN expirent souvent → onerror bascule sur repli icône */ | |
| 1244 | +window.thumbFail=el=>{const w=el.parentElement;if(w){w.classList.add("noimg"); | |
| 1245 | + w.innerHTML=w.dataset.icon||"";}}; | |
| 1246 | +function _mediaItems(a){const m=a.metrics||{}; | |
| 1247 | + return m.recent_posts||m.recent_videos||m.recent_tweets||[];} | |
| 1248 | + | |
| 1249 | +/* grille de contenu récent, images/vidéos bien affichées */ | |
| 1250 | +function contentGrid(a){ | |
| 1251 | + const items=_mediaItems(a); | |
| 1252 | + if(!items.length)return `<div class="cg-empty">${icon(a.platform)} Aucun contenu récent capté pour ce compte.</div>`; | |
| 1253 | + const ico=icon(a.platform); | |
| 1254 | + const cards=items.slice(0,12).map(it=>{ | |
| 1217 | 1255 | const img=it.display_url||it.thumbnail_url||""; |
| 1218 | − const title=esc((it.caption||it.title||it.text||"").slice(0,90)); | |
| 1256 | + const isVid=it.type==="video"||it.type==="reel"||!!it.duration||it.video_views!=null; | |
| 1257 | + const cap=esc((it.caption||it.title||it.text||"").slice(0,140)); | |
| 1258 | + const when=_relTimeC(it.timestamp||it.published_at||it.published||it.created_at); | |
| 1219 | 1259 | const stats=[ |
| 1220 | − it.views!=null?fmt.format(it.views)+" vues":null, | |
| 1260 | + it.views!=null?fmt.format(it.views)+" vues":(it.video_views!=null?fmt.format(it.video_views)+" vues":null), | |
| 1221 | 1261 | it.likes!=null?fmt.format(it.likes)+" ❤":null, |
| 1222 | 1262 | it.comments!=null?fmt.format(it.comments)+" 💬":null, |
| 1223 | 1263 | it.retweets!=null?fmt.format(it.retweets)+" ↻":null, |
| 1264 | + it.shares!=null?fmt.format(it.shares)+" ↗":null, | |
| 1224 | 1265 | ].filter(Boolean).join(" · "); |
| 1225 | − const when=_relTime(it.timestamp||it.published_at||it.published||it.created_at); | |
| 1226 | − return `<a class="cstrip-item" href="${esc(it.url||a.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()"> | |
| 1227 | − ${img?`<span class="cstrip-thumb" style="background-image:url('${esc(img)}')">${it.type==="video"||it.type==="reel"||it.duration?'<span class="cstrip-play">▶</span>':""}${it.duration?`<span class="cstrip-dur">${esc(it.duration)}</span>`:""}</span>`:`<span class="cstrip-thumb noimg">${icon(a.platform)}</span>`} | |
| 1228 | − ${title?`<span class="cstrip-cap">${title}</span>`:""} | |
| 1229 | − <span class="cstrip-meta">${stats}${when?` · ${when}`:""}</span></a>`}).join(""); | |
| 1230 | − return `<div class="cstrip"><div class="cstrip-h">Contenu récent</div><div class="cstrip-scroll">${cards}</div></div>`; | |
| 1266 | + const thumb=img | |
| 1267 | + ? `<span class="cg-thumb" data-icon='${esc(ico)}'><img loading="lazy" src="${esc(img)}" alt="" referrerpolicy="no-referrer" onerror="thumbFail(this)"> | |
| 1268 | + ${isVid?'<span class="cg-play">▶</span>':""}${it.duration?`<span class="cg-dur">${esc(it.duration)}</span>`:""} | |
| 1269 | + ${it.is_pinned?'<span class="cg-pin">📌</span>':""}</span>` | |
| 1270 | + : `<span class="cg-thumb noimg" data-icon='${esc(ico)}'>${ico}${isVid?'<span class="cg-play">▶</span>':""}</span>`; | |
| 1271 | + return `<a class="cg-card" href="${esc(it.url||a.url)}" target="_blank" rel="noopener"> | |
| 1272 | + ${thumb} | |
| 1273 | + <span class="cg-body">${cap?`<span class="cg-cap">${cap}</span>`:`<span class="cg-cap dim">Publication ${a.platform}</span>`} | |
| 1274 | + <span class="cg-meta">${stats||"—"}${when?`<span class="cg-when">${when}</span>`:""}</span></span></a>`; | |
| 1275 | + }).join(""); | |
| 1276 | + return `<div class="cg-grid">${cards}</div>`; | |
| 1277 | +} | |
| 1278 | + | |
| 1279 | +/* définitions de métriques calculées, par plateforme */ | |
| 1280 | +function platformKPIs(a){ | |
| 1281 | + const m=a.metrics||{}, out=[]; | |
| 1282 | + const push=(v,l,suf)=>{if(v!=null&&v!=="")out.push([typeof v==="number"?fmt.format(v)+(suf||""):esc(v)+(suf||""),l]);}; | |
| 1283 | + push(a.followers,a.platform==="discord"?"membres":a.platform==="patreon"?"patrons":"abonnés"); | |
| 1284 | + push(m.engagement_rate_pct!=null?m.engagement_rate_pct:null,"engagement","%"); | |
| 1285 | + push(m.avg_likes,"j'aime / pub (moy.)"); | |
| 1286 | + push(m.avg_views!=null?m.avg_views:m.avg_video_views,"vues / pub (moy.)"); | |
| 1287 | + push(m.avg_comments,"comm. / pub (moy.)"); | |
| 1288 | + push(m.avg_retweets,"RT / pub (moy.)"); | |
| 1289 | + push(m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week,"pubs / semaine"); | |
| 1290 | + push(m.posts,"publications"); | |
| 1291 | + push(m.videos,"vidéos"); | |
| 1292 | + push(m.likes,a.platform==="tiktok"?"j'aime cumulés":"j'aime"); | |
| 1293 | + push(m.following,"abonnements"); | |
| 1294 | + push(m.online,"en ligne"); | |
| 1295 | + push(m.boosts,"boosts"); | |
| 1296 | + push(m.patrons,"patrons"); | |
| 1297 | + push(m.live_viewers,"spectateurs (live)"); | |
| 1298 | + return out; | |
| 1299 | +} | |
| 1300 | +function platformBadges(a){ | |
| 1301 | + const m=a.metrics||{}; | |
| 1302 | + return [ | |
| 1303 | + a.verified?'<span class="abadge ok">✓ Vérifié</span>':"", | |
| 1304 | + m.is_partner?'<span class="abadge">Partenaire</span>':"", | |
| 1305 | + m.is_affiliate&&!m.is_partner?'<span class="abadge">Affilié</span>':"", | |
| 1306 | + m.partnered?'<span class="abadge">Serveur partenaire</span>':"", | |
| 1307 | + m.is_live_now?'<span class="abadge live">● En direct</span>':"", | |
| 1308 | + m.is_seller?'<span class="abadge">Boutique</span>':"", | |
| 1309 | + m.is_free===true?'<span class="abadge">Gratuit</span>':"", | |
| 1310 | + m.is_monthly?'<span class="abadge">Mensuel</span>':"", | |
| 1311 | + m.has_story?'<span class="abadge">Story active</span>':"", | |
| 1312 | + m.has_spotlight?'<span class="abadge">Spotlight</span>':"", | |
| 1313 | + m.country?`<span class="abadge">${esc(m.country)}</span>`:"", | |
| 1314 | + (m.recent_games||[]).slice(0,2).map(g=>`<span class="abadge">🎮 ${esc(g)}</span>`).join(""), | |
| 1315 | + (m.top_hashtags||[]).slice(0,3).map(h=>`<span class="abadge">#${esc(h)}</span>`).join(""), | |
| 1316 | + ].filter(Boolean).join(""); | |
| 1317 | +} | |
| 1318 | + | |
| 1319 | +/* panneau d'un compte (onglet plateforme) */ | |
| 1320 | +function platformPanel(a,idx,active){ | |
| 1321 | + const p=PLAT[a.platform]||PLAT.autre; | |
| 1322 | + const conf=Math.round((a.confidence||0)*100); | |
| 1323 | + const confCls=(a.confidence||0)>=0.75?"hi":"mid"; | |
| 1324 | + const kpis=platformKPIs(a).map(([v,l])=>`<div class="kpi"><b>${v}</b><span>${l}</span></div>`).join(""); | |
| 1325 | + const badges=platformBadges(a); | |
| 1326 | + const stamp=(a.metrics||{})[a.platform+"_apify_at"]||a.last_checked; | |
| 1327 | + const top=(a.metrics||{}).top_post||(a.metrics||{}).top_video||(a.metrics||{}).top_tweet; | |
| 1328 | + return `<section class="ppanel${active?" on":""}" data-panel="${idx}"> | |
| 1329 | + <div class="ppanel-head"> | |
| 1330 | + <span class="plat" style="background:${p.color}">${icon(a.platform)}</span> | |
| 1331 | + <div class="ppanel-id"><span class="ppanel-name">${p.label}${a.verified?checkSvg:""}</span> | |
| 1332 | + <a class="ppanel-handle" href="${esc(a.url)}" target="_blank" rel="noopener">@${esc(a.handle)} ↗</a></div> | |
| 1333 | + <span class="conf ${confCls}" title="Score de rattachement">${conf}%</span> | |
| 1334 | + </div> | |
| 1335 | + ${badges?`<div class="abadge-row">${badges}</div>`:""} | |
| 1336 | + <div class="kpi-grid">${kpis||'<div class="kpi"><b>—</b><span>pas encore de métriques</span></div>'}</div> | |
| 1337 | + ${top&&top.url?`<a class="ppanel-top" href="${esc(top.url)}" target="_blank" rel="noopener">★ Top contenu — ${top.views!=null?fmt.format(top.views)+" vues":""}${top.likes!=null?" · "+fmt.format(top.likes)+" ❤":""} ↗</a>`:""} | |
| 1338 | + <h3 class="cg-title">Contenu récent</h3> | |
| 1339 | + ${contentGrid(a)} | |
| 1340 | + <div class="ppanel-foot">${stamp?"Dernière vérification "+esc(String(stamp).slice(0,10)):""}</div> | |
| 1341 | + </section>`; | |
| 1342 | +} | |
| 1343 | + | |
| 1344 | +window.showPtab=(i,el)=>{ | |
| 1345 | + document.querySelectorAll(".ppanel").forEach(s=>s.classList.toggle("on",s.dataset.panel==String(i))); | |
| 1346 | + document.querySelectorAll(".ptab").forEach(t=>t.classList.remove("on")); | |
| 1347 | + if(el)el.classList.add("on"); | |
| 1348 | + const ov=document.getElementById("ov-panel");if(ov)ov.classList.toggle("on",i==="ov"); | |
| 1349 | +}; | |
| 1350 | + | |
| 1351 | +function computeAgg(c){ | |
| 1352 | + const P=(c.platforms||[]); | |
| 1353 | + const reach=c.total_reach||P.reduce((s,a)=>s+(a.followers||0),0); | |
| 1354 | + const eng=P.map(a=>(a.metrics||{}).engagement_rate_pct).filter(v=>v!=null); | |
| 1355 | + const avgEng=eng.length?(eng.reduce((s,v)=>s+v,0)/eng.length):null; | |
| 1356 | + const content=P.reduce((s,a)=>s+_mediaItems(a).length,0); | |
| 1357 | + const cad=P.map(a=>{const m=a.metrics||{};return m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week;}).filter(v=>v!=null); | |
| 1358 | + const main=P.slice().sort((x,y)=>(y.followers||0)-(x.followers||0))[0]; | |
| 1359 | + return {reach,avgEng,content,maxCad:cad.length?Math.max(...cad):null, | |
| 1360 | + nPlat:P.length,nVerif:P.filter(a=>a.verified).length,main}; | |
| 1231 | 1361 | } |
| 1362 | + | |
| 1232 | 1363 | async function renderCreator(id){ |
| 1233 | 1364 | app.innerHTML=header()+`<div class="container"><div class="spin"></div></div>`+footer()+tabbar("");bindNav(); |
| 1234 | 1365 | let c;try{c=await api("/api/creators/"+encodeURIComponent(id))} |
@@ -1237,44 +1368,38 @@ async function renderCreator(id){ | ||
| 1237 | 1368 | document.title=`${c.display_name} — Créa-Ka`; |
| 1238 | 1369 | const verified=(c.platforms||[]).some(a=>a.verified); |
| 1239 | 1370 | const niches=(c.niches||[]).map(n=>`<span class="mchip">${NICHE_LBL[n]||n}</span>`).join(""); |
| 1240 | − const M_LBL={following:"abonnements",posts:"publications",likes:"j'aime", | |
| 1241 | − views:"vues",videos:"vidéos",episodes:"épisodes",images:"photos", | |
| 1242 | − friends:"amis",listed:"listes"}; | |
| 1243 | − const accs=(c.platforms||[]).map(a=>{const p=PLAT[a.platform]||PLAT.autre; | |
| 1244 | − const conf=(a.confidence||0)>=0.75?["hi","confiance élevée"]:["mid","à confirmer"]; | |
| 1245 | − const m=a.metrics||{}; | |
| 1246 | − const mx=Object.entries(m).filter(([k,v])=>M_LBL[k]&&v!=null) | |
| 1247 | − .map(([k,v])=>`${fmt.format(v)} ${M_LBL[k]}`).join(" · "); | |
| 1248 | − // rangée de statistiques riches (enrichissement Apify multi-plateforme) | |
| 1249 | − const stat=(v,l)=>v==null?"":`<span class="astat"><b>${typeof v==="number"?fmt.format(v):esc(v)}</b><span>${l}</span></span>`; | |
| 1250 | − const stats=[ | |
| 1251 | − stat(m.engagement_rate_pct!=null?m.engagement_rate_pct+" %":null,"engagement"), | |
| 1252 | − stat(m.avg_likes,"j'aime/moy"), | |
| 1253 | − stat(m.avg_views!=null?m.avg_views:m.avg_video_views,"vues/moy"), | |
| 1254 | − stat(m.avg_retweets,"RT/moy"), | |
| 1255 | − stat(m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week,"/semaine"), | |
| 1256 | − ].filter(Boolean).join(""); | |
| 1257 | − const badges=[ | |
| 1258 | − m.is_partner?'<span class="abadge">Partenaire</span>':"", | |
| 1259 | − m.is_affiliate&&!m.is_partner?'<span class="abadge">Affilié</span>':"", | |
| 1260 | − m.is_live_now?'<span class="abadge live">● En direct</span>':"", | |
| 1261 | − m.is_seller?'<span class="abadge">Boutique</span>':"", | |
| 1262 | − m.is_free===true?'<span class="abadge">Gratuit</span>':"", | |
| 1263 | − m.has_story?'<span class="abadge">Story active</span>':"", | |
| 1264 | − (m.recent_games||[]).length?`<span class="abadge">${esc((m.recent_games||[])[0])}</span>`:"", | |
| 1265 | − (m.top_hashtags||[]).slice(0,2).map(h=>`<span class="abadge">#${esc(h)}</span>`).join(""), | |
| 1266 | − ].filter(Boolean).join(""); | |
| 1267 | − return `<div class="acc-card"> | |
| 1268 | − <a class="acc" href="${esc(a.url)}" target="_blank" rel="noopener"> | |
| 1269 | − <span class="plat" style="background:${p.color}">${icon(a.platform)}</span> | |
| 1270 | − <span class="acc-mid"><span class="acc-name">${p.label}${a.verified?checkSvg:""}</span> | |
| 1271 | − <span class="acc-handle">@${esc(a.handle)}</span> | |
| 1272 | − <span class="acc-handle" style="overflow:visible"><span class="conf ${conf[0]}" title="Score de rattachement ${Math.round((a.confidence||0)*100)} %">${conf[1]}</span>${mx?` <span style="font-size:11px">${mx}</span>`:""}</span></span> | |
| 1273 | − <span class="acc-right">${a.followers?`<b>${fmt.format(a.followers)}</b><span>abonnés</span>`:`<span style="font-size:11px">voir le profil ↗</span>`}</span></a> | |
| 1274 | − ${stats?`<div class="astat-row">${stats}</div>`:""} | |
| 1275 | − ${badges?`<div class="abadge-row">${badges}</div>`:""} | |
| 1276 | − ${contentStrip(a)}</div>`}).join(""); | |
| 1277 | − const checked=(c.platforms||[]).map(a=>(a.metrics||{}).instagram_apify_at||a.last_checked).filter(Boolean).sort().pop()||(c.platforms||[]).map(a=>a.last_checked).filter(Boolean).sort().pop(); | |
| 1371 | + const P=(c.platforms||[]).slice().sort((x,y)=>(y.followers||0)-(x.followers||0)); | |
| 1372 | + const g=computeAgg(c); | |
| 1373 | + // bandeau de métriques CALCULÉES (multi-plateforme) | |
| 1374 | + const kpi=(v,l)=>v==null?"":`<div class="hkpi"><b>${typeof v==="number"?fmt.format(v):esc(v)}</b><span>${l}</span></div>`; | |
| 1375 | + const heroKpis=[ | |
| 1376 | + kpi(g.reach,"abonnés cumulés"), | |
| 1377 | + kpi(g.nPlat,"plateformes"), | |
| 1378 | + kpi(g.avgEng!=null?Math.round(g.avgEng*100)/100+" %":null,"engagement moyen"), | |
| 1379 | + kpi(g.maxCad!=null?g.maxCad:null,"pubs / semaine"), | |
| 1380 | + kpi(g.content||null,"contenus analysés"), | |
| 1381 | + kpi(g.main?(PLAT[g.main.platform]||PLAT.autre).label:null,"plateforme nº 1"), | |
| 1382 | + ].filter(Boolean).join(""); | |
| 1383 | + // barres comparatives de portée par plateforme (vue d'ensemble) | |
| 1384 | + const maxF=Math.max(1,...P.map(a=>a.followers||0)); | |
| 1385 | + const bars=P.map(a=>{const p=PLAT[a.platform]||PLAT.autre;const w=Math.round((a.followers||0)/maxF*100); | |
| 1386 | + return `<div class="obar"><span class="obar-ico" style="background:${p.color}">${icon(a.platform)}</span> | |
| 1387 | + <span class="obar-lab">${p.label}</span> | |
| 1388 | + <span class="obar-track"><span class="obar-fill" style="width:${w}%;background:${p.color}"></span></span> | |
| 1389 | + <span class="obar-val">${a.followers!=null?fmt.format(a.followers):"—"}</span></div>`;}).join(""); | |
| 1390 | + // onglets + panneaux | |
| 1391 | + const tabs=`<button class="ptab on" onclick="showPtab('ov',this)">Vue d'ensemble</button>`+ | |
| 1392 | + P.map((a,i)=>{const p=PLAT[a.platform]||PLAT.autre; | |
| 1393 | + return `<button class="ptab" onclick="showPtab(${i},this)"> | |
| 1394 | + <span class="ptab-ico" style="color:${p.color}">${icon(a.platform)}</span>${p.label} | |
| 1395 | + ${a.followers!=null?`<span class="ptab-n">${fmt.format(a.followers)}</span>`:""}</button>`;}).join(""); | |
| 1396 | + const ovPanel=`<section class="ppanel on" id="ov-panel" data-panel="ov"> | |
| 1397 | + <h3 class="cg-title">Portée par plateforme</h3> | |
| 1398 | + <div class="obars">${bars||"<p class=klabel>Aucune plateforme.</p>"}</div> | |
| 1399 | + ${c.link_in_bio_url?`<a class="linkinbio" href="${esc(c.link_in_bio_url)}" target="_blank" rel="noopener">${icon("linktree")} Sa page de liens officielle ↗</a>`:""} | |
| 1400 | + </section>`; | |
| 1401 | + const panels=ovPanel+P.map((a,i)=>platformPanel(a,i,false)).join(""); | |
| 1402 | + const checked=(c.platforms||[]).map(a=>{const m=a.metrics||{};return m[a.platform+"_apify_at"]||a.last_checked;}).filter(Boolean).sort().pop(); | |
| 1278 | 1403 | app.innerHTML=header()+` |
| 1279 | 1404 | <div class="container cpage"> |
| 1280 | 1405 | <a class="backlink" href="/" data-nav>← Tous les créateurs</a> |
@@ -1283,16 +1408,15 @@ async function renderCreator(id){ | ||
| 1283 | 1408 | <div style="flex:1;min-width:200px"><h1>${esc(c.display_name)}${verified?checkSvg:""}</h1> |
| 1284 | 1409 | <div class="meta"><span class="mchip o">${TIER_LBL[c.audience_tier]||c.audience_tier}</span> |
| 1285 | 1410 | ${niches}${c.region?`<span class="mchip">${esc(c.region)}</span>`:""} |
| 1286 | − ${(c.languages||[]).map(l=>`<span class="mchip">${{fr:"Français",en:"Anglais",bilingue:"Bilingue"}[l]||l}</span>`).join("")}</div></div> | |
| 1287 | − ${c.total_reach?`<div class="cpage-reach"><b>${fmt.format(c.total_reach)}</b><span>abonnés cumulés</span></div>`:""} | |
| 1411 | + ${(c.languages||[]).map(l=>`<span class="mchip">${{fr:"Français",en:"Anglais",bilingue:"Bilingue"}[l]||l}</span>`).join("")}</div> | |
| 1412 | + ${c.bio?`<p class="cpage-bio">${esc(c.bio)}</p>`:""}</div> | |
| 1288 | 1413 | </div> |
| 1289 | − ${c.bio?`<p class="cpage-bio">${esc(c.bio)}</p>`:""} | |
| 1414 | + <div class="hkpi-row">${heroKpis}</div> | |
| 1290 | 1415 | ${c.notes?`<div class="cpage-notes"><span class="klabel">Contexte public</span><br>${esc(c.notes)}</div>`:""} |
| 1291 | − <h2 class="sect-title">Ses plateformes <span class="chip">${(c.platforms||[]).length}</span></h2> | |
| 1292 | − <div class="acc-grid">${accs}</div> | |
| 1293 | − ${c.link_in_bio_url?`<a class="linkinbio" href="${esc(c.link_in_bio_url)}" target="_blank" rel="noopener">${icon("linktree")} Sa page de liens officielle ↗</a>`:""} | |
| 1294 | − <div class="cpage-foot">Sources : listes médias publiques, Wikidata, annuaires publics et profils publics des plateformes. | |
| 1295 | − Audiences approximatives${checked?`, dernière vérification ${esc(checked.slice(0,10))}`:""}. | |
| 1416 | + <div class="ptabs">${tabs}</div> | |
| 1417 | + <div class="ppanels">${panels}</div> | |
| 1418 | + <div class="cpage-foot">Sources : listes médias publiques, Wikidata, annuaires publics et profils publics des plateformes (enrichissement Apify). | |
| 1419 | + Audiences et métriques approximatives${checked?`, dernière vérification ${esc(String(checked).slice(0,10))}`:""}. | |
| 1296 | 1420 | Une erreur dans cette fiche ? <a href="/retrait" data-nav>Signaler / demander un retrait</a>.</div> |
| 1297 | 1421 | </div>`+footer()+tabbar(""); |
| 1298 | 1422 | bindNav();window.scrollTo(0,0); |
modified
frontend/src/index.template.html
+236 −112
@@ -264,61 +264,91 @@ select.f.active{background-color:var(--accent-soft);color:var(--ink);border-colo | ||
| 264 | 264 | background:var(--surface);border:1.5px solid var(--ink);border-radius:var(--r-card); |
| 265 | 265 | padding:13px 17px;box-shadow:var(--shadow-off-soft)} |
| 266 | 266 | .sect-title{font-size:19px;margin:36px 0 15px;display:flex;align-items:center;gap:10px;text-transform:uppercase} |
| 267 | −.acc-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px} | |
| 268 | −@media(max-width:640px){.acc-grid{grid-template-columns:1fr}} | |
| 269 | −.acc-card{display:flex;flex-direction:column;background:var(--surface); | |
| 270 | − border:1.5px solid var(--ink);border-radius:var(--r-card); | |
| 271 | − box-shadow:var(--shadow-off-soft);transition:transform .16s ease,box-shadow .16s ease; | |
| 272 | − min-width:0;overflow:hidden} | |
| 273 | −.acc-card:hover{transform:translate(-2px,-2px);box-shadow:var(--shadow-off)} | |
| 274 | −.acc{display:flex;align-items:center;gap:13px;padding:14px 16px;min-width:0} | |
| 275 | −/* rangée de stats riches */ | |
| 276 | −.astat-row{display:flex;flex-wrap:wrap;gap:6px;padding:0 16px 10px} | |
| 277 | −.astat{display:flex;flex-direction:column;background:var(--surface-2); | |
| 278 | − border:1px solid var(--line);border-radius:var(--r-ctl);padding:4px 9px;line-height:1.1} | |
| 279 | −.astat b{font-family:var(--font-display);font-size:13px;color:var(--ink);font-weight:700} | |
| 280 | −.astat span{font-family:var(--font-mono);font-size:8.5px;color:var(--ink-3); | |
| 281 | − text-transform:uppercase;letter-spacing:.05em;font-weight:700;margin-top:1px} | |
| 282 | −.abadge-row{display:flex;flex-wrap:wrap;gap:5px;padding:0 16px 12px} | |
| 283 | −.abadge{display:inline-flex;background:var(--lime-soft);color:var(--green-deep); | |
| 284 | − border:1px solid var(--line);border-radius:var(--r-ctl);padding:2px 8px; | |
| 285 | − font-family:var(--font-mono);font-size:9.5px;font-weight:700;letter-spacing:.02em} | |
| 286 | −.abadge.live{background:#ffe3e0;color:#b0201a;border-color:#b0201a} | |
| 287 | −/* bande de contenu récent */ | |
| 288 | −.cstrip{border-top:1px dashed var(--line);padding:11px 0 13px} | |
| 289 | −.cstrip-h{font-family:var(--font-mono);font-size:9.5px;font-weight:700;color:var(--ink-3); | |
| 290 | − text-transform:uppercase;letter-spacing:.07em;padding:0 16px 8px} | |
| 291 | −.cstrip-scroll{display:flex;gap:9px;overflow-x:auto;padding:0 16px 3px;scroll-snap-type:x proximity} | |
| 292 | −.cstrip-scroll::-webkit-scrollbar{height:5px} | |
| 293 | −.cstrip-scroll::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px} | |
| 294 | −.cstrip-item{flex:none;width:120px;scroll-snap-align:start;text-decoration:none;color:inherit; | |
| 295 | − display:flex;flex-direction:column;gap:4px} | |
| 296 | −.cstrip-thumb{position:relative;display:block;width:120px;height:120px;border-radius:9px; | |
| 297 | − background-size:cover;background-position:center;background-color:var(--surface-2); | |
| 298 | − border:1px solid var(--line);overflow:hidden} | |
| 299 | −.cstrip-thumb.noimg{display:flex;align-items:center;justify-content:center} | |
| 300 | −.cstrip-thumb.noimg svg{width:34px;height:34px;opacity:.35} | |
| 301 | −.cstrip-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%); | |
| 302 | − color:#fff;font-size:20px;text-shadow:0 1px 4px rgba(0,0,0,.6)} | |
| 303 | −.cstrip-dur{position:absolute;right:5px;bottom:5px;background:rgba(0,0,0,.78);color:#fff; | |
| 304 | − font-family:var(--font-mono);font-size:9px;padding:1px 5px;border-radius:5px} | |
| 305 | −.cstrip-cap{font-size:11px;line-height:1.25;color:var(--ink-2);display:-webkit-box; | |
| 306 | − -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} | |
| 307 | −.cstrip-meta{font-family:var(--font-mono);font-size:9px;color:var(--ink-3);line-height:1.2} | |
| 308 | −.acc .plat{width:44px;height:44px;border-radius:9px;flex:none} | |
| 309 | −.acc .plat svg{width:20px;height:20px} | |
| 310 | −.acc-mid{min-width:0;flex:1;display:flex;flex-direction:column;gap:2px;overflow:hidden} | |
| 311 | −.acc-name{font-family:var(--font-display);font-weight:700;font-size:14px;display:flex;align-items:center;gap:6px} | |
| 312 | −.acc-handle{font-size:12px;color:var(--ink-3);overflow:hidden;text-overflow:ellipsis; | |
| 313 | − white-space:nowrap;display:block;max-width:100%} | |
| 314 | −.acc-right{margin-left:auto;text-align:right;flex:none;padding-left:8px} | |
| 315 | −.acc-right b{display:block;font-family:var(--font-display);font-size:16.5px;color:var(--ink);font-weight:700} | |
| 316 | −.acc-right span{font-family:var(--font-mono);font-size:9px;color:var(--ink-3); | |
| 317 | − text-transform:uppercase;letter-spacing:.06em;font-weight:700} | |
| 318 | −.conf{display:inline-flex;padding:1px 7px;border-radius:var(--r-ctl); | |
| 319 | − font-family:var(--font-mono);font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.03em} | |
| 267 | +/* ===== page créateur : métriques calculées + onglets par plateforme ===== */ | |
| 268 | +.hkpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:10px;margin:22px 0 6px} | |
| 269 | +.hkpi{background:var(--surface);border:1.5px solid var(--ink);border-radius:var(--r-card); | |
| 270 | + box-shadow:var(--shadow-off-soft);padding:13px 15px} | |
| 271 | +.hkpi b{display:block;font-family:var(--font-display);font-size:22px;font-weight:800;color:var(--ink);line-height:1} | |
| 272 | +.hkpi span{display:block;margin-top:5px;font-family:var(--font-mono);font-size:9.5px;color:var(--ink-3); | |
| 273 | + text-transform:uppercase;letter-spacing:.05em;font-weight:700} | |
| 274 | +/* barre d'onglets */ | |
| 275 | +.ptabs{display:flex;gap:8px;overflow-x:auto;margin:26px 0 0;padding-bottom:2px} | |
| 276 | +.ptabs::-webkit-scrollbar{height:5px}.ptabs::-webkit-scrollbar-thumb{background:var(--line);border-radius:3px} | |
| 277 | +.ptab{flex:none;display:inline-flex;align-items:center;gap:7px;cursor:pointer; | |
| 278 | + background:var(--surface);border:1.5px solid var(--ink);border-bottom:none; | |
| 279 | + border-radius:12px 12px 0 0;padding:10px 15px;font-family:var(--font-display); | |
| 280 | + font-weight:700;font-size:13px;color:var(--ink-2);transition:.14s;margin-bottom:-1.5px} | |
| 281 | +.ptab:hover{background:var(--surface-2)} | |
| 282 | +.ptab.on{background:var(--accent);color:var(--on-accent);border-color:var(--ink)} | |
| 283 | +.ptab.on .ptab-ico{color:var(--on-accent)!important} | |
| 284 | +.ptab-ico{width:16px;height:16px;display:inline-flex}.ptab-ico svg{width:16px;height:16px} | |
| 285 | +.ptab-n{font-family:var(--font-mono);font-size:10px;opacity:.75;font-weight:700} | |
| 286 | +.ppanels{border:1.5px solid var(--ink);border-radius:0 12px 12px 12px; | |
| 287 | + background:var(--surface);box-shadow:var(--shadow-off-soft);padding:20px 18px 22px} | |
| 288 | +.ppanel{display:none}.ppanel.on{display:block;animation:fade .2s ease} | |
| 289 | +@keyframes fade{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}} | |
| 290 | +.ppanel-head{display:flex;align-items:center;gap:13px} | |
| 291 | +.ppanel-head .plat{width:46px;height:46px;border-radius:10px;flex:none;display:flex;align-items:center;justify-content:center} | |
| 292 | +.ppanel-head .plat svg{width:22px;height:22px} | |
| 293 | +.ppanel-id{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px} | |
| 294 | +.ppanel-name{font-family:var(--font-display);font-weight:800;font-size:17px;display:flex;align-items:center;gap:6px} | |
| 295 | +.ppanel-handle{font-size:13px;color:var(--green);font-weight:600} | |
| 296 | +.conf{display:inline-flex;padding:2px 9px;border-radius:var(--r-ctl); | |
| 297 | + font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.03em;flex:none} | |
| 320 | 298 | .conf.hi{background:var(--lime-soft);color:var(--green-deep)} |
| 321 | 299 | .conf.mid{background:var(--amber-soft);color:#8a5a12} |
| 300 | +.abadge-row{display:flex;flex-wrap:wrap;gap:6px;margin:12px 0 4px} | |
| 301 | +.abadge{display:inline-flex;background:var(--lime-soft);color:var(--green-deep); | |
| 302 | + border:1px solid var(--line);border-radius:var(--r-ctl);padding:3px 9px; | |
| 303 | + font-family:var(--font-mono);font-size:10px;font-weight:700;letter-spacing:.02em} | |
| 304 | +.abadge.ok{background:var(--surface-2);color:var(--ink-2)} | |
| 305 | +.abadge.live{background:#ffe3e0;color:#b0201a;border-color:#b0201a} | |
| 306 | +/* grille KPI par plateforme */ | |
| 307 | +.kpi-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px;margin:16px 0 4px} | |
| 308 | +.kpi{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-ctl);padding:11px 13px} | |
| 309 | +.kpi b{display:block;font-family:var(--font-display);font-size:19px;font-weight:800;color:var(--ink);line-height:1} | |
| 310 | +.kpi span{display:block;margin-top:4px;font-family:var(--font-mono);font-size:9px;color:var(--ink-3); | |
| 311 | + text-transform:uppercase;letter-spacing:.04em;font-weight:700} | |
| 312 | +.ppanel-top{display:inline-flex;margin:12px 0 2px;background:var(--surface-2);border:1px solid var(--line); | |
| 313 | + border-radius:var(--r-ctl);padding:8px 13px;font-size:12.5px;font-weight:700;color:var(--ink-2)} | |
| 314 | +.ppanel-top:hover{color:var(--green)} | |
| 315 | +.ppanel-foot{margin-top:14px;font-family:var(--font-mono);font-size:10px;color:var(--ink-3)} | |
| 316 | +/* grille de contenu récent (images/vidéos) */ | |
| 317 | +.cg-title{font-family:var(--font-display);font-size:14px;font-weight:800;margin:20px 0 12px;color:var(--ink)} | |
| 318 | +.cg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:13px} | |
| 319 | +.cg-card{display:flex;flex-direction:column;text-decoration:none;color:inherit; | |
| 320 | + border:1.5px solid var(--ink);border-radius:var(--r-card);overflow:hidden; | |
| 321 | + background:var(--surface);box-shadow:var(--shadow-off-soft);transition:transform .15s,box-shadow .15s} | |
| 322 | +.cg-card:hover{transform:translate(-2px,-2px);box-shadow:var(--shadow-off)} | |
| 323 | +.cg-thumb{position:relative;display:block;width:100%;aspect-ratio:1/1;background:var(--surface-2);overflow:hidden} | |
| 324 | +.cg-thumb img{width:100%;height:100%;object-fit:cover;display:block} | |
| 325 | +.cg-thumb.noimg{display:flex;align-items:center;justify-content:center} | |
| 326 | +.cg-thumb.noimg svg{width:44px;height:44px;opacity:.3} | |
| 327 | +.cg-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#fff; | |
| 328 | + font-size:26px;text-shadow:0 2px 6px rgba(0,0,0,.7);pointer-events:none} | |
| 329 | +.cg-dur{position:absolute;right:6px;bottom:6px;background:rgba(0,0,0,.8);color:#fff; | |
| 330 | + font-family:var(--font-mono);font-size:10px;padding:2px 6px;border-radius:5px} | |
| 331 | +.cg-pin{position:absolute;left:6px;top:6px;font-size:13px} | |
| 332 | +.cg-body{padding:9px 11px 11px;display:flex;flex-direction:column;gap:5px} | |
| 333 | +.cg-cap{font-size:12px;line-height:1.3;color:var(--ink-2);display:-webkit-box; | |
| 334 | + -webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:31px} | |
| 335 | +.cg-cap.dim{color:var(--ink-3)} | |
| 336 | +.cg-meta{font-family:var(--font-mono);font-size:10px;color:var(--ink-3);display:flex; | |
| 337 | + justify-content:space-between;gap:6px;flex-wrap:wrap} | |
| 338 | +.cg-when{color:var(--ink-3);opacity:.8} | |
| 339 | +.cg-empty{padding:26px;text-align:center;color:var(--ink-3);font-size:13px} | |
| 340 | +.cg-empty svg{width:20px;height:20px;vertical-align:-4px;opacity:.4;margin-right:6px} | |
| 341 | +/* vue d'ensemble : barres de portée */ | |
| 342 | +.obars{display:flex;flex-direction:column;gap:11px} | |
| 343 | +.obar{display:flex;align-items:center;gap:11px} | |
| 344 | +.obar-ico{width:32px;height:32px;border-radius:8px;flex:none;display:flex;align-items:center;justify-content:center} | |
| 345 | +.obar-ico svg{width:16px;height:16px} | |
| 346 | +.obar-lab{width:96px;flex:none;font-family:var(--font-display);font-weight:700;font-size:13px} | |
| 347 | +.obar-track{flex:1;height:12px;background:var(--surface-2);border:1px solid var(--line);border-radius:6px;overflow:hidden} | |
| 348 | +.obar-fill{display:block;height:100%;border-radius:6px 0 0 6px;min-width:3px} | |
| 349 | +.obar-val{width:78px;flex:none;text-align:right;font-family:var(--font-mono);font-size:12px;font-weight:700;color:var(--ink)} | |
| 350 | +@media(max-width:640px){.obar-lab{width:64px;font-size:11px}.obar-val{width:60px;font-size:11px} | |
| 351 | + .cg-grid{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}} | |
| 322 | 352 | .linkinbio{display:inline-flex;align-items:center;gap:10px;margin-top:22px; |
| 323 | 353 | background:var(--accent);color:var(--on-accent);border:1.5px solid var(--ink); |
| 324 | 354 | border-radius:var(--r-ctl);padding:11px 20px;font-family:var(--font-display); |
@@ -751,26 +781,127 @@ async function reload(more){ | ||
| 751 | 781 | const _relTime=ts=>{if(!ts)return"";if(typeof ts==="string"&&!/^\d+$/.test(ts))return esc(ts.slice(0,10)); |
| 752 | 782 | const d=Math.floor((Date.now()/1000-Number(ts))/86400); |
| 753 | 783 | return d<=0?"aujourd'hui":d===1?"hier":d<30?d+" j":d<365?Math.floor(d/30)+" mois":Math.floor(d/365)+" ans";}; |
| 754 | −function contentStrip(a){ | |
| 755 | − const m=a.metrics||{}; | |
| 756 | − const items=m.recent_posts||m.recent_videos||m.recent_tweets||[]; | |
| 757 | − if(!items.length)return""; | |
| 758 | − const cards=items.slice(0,8).map(it=>{ | |
| 784 | +const _relTimeC=ts=>_relTime(ts); | |
| 785 | +/* image de contenu : IG/CDN expirent souvent → onerror bascule sur repli icône */ | |
| 786 | +window.thumbFail=el=>{const w=el.parentElement;if(w){w.classList.add("noimg"); | |
| 787 | + w.innerHTML=w.dataset.icon||"";}}; | |
| 788 | +function _mediaItems(a){const m=a.metrics||{}; | |
| 789 | + return m.recent_posts||m.recent_videos||m.recent_tweets||[];} | |
| 790 | + | |
| 791 | +/* grille de contenu récent, images/vidéos bien affichées */ | |
| 792 | +function contentGrid(a){ | |
| 793 | + const items=_mediaItems(a); | |
| 794 | + if(!items.length)return `<div class="cg-empty">${icon(a.platform)} Aucun contenu récent capté pour ce compte.</div>`; | |
| 795 | + const ico=icon(a.platform); | |
| 796 | + const cards=items.slice(0,12).map(it=>{ | |
| 759 | 797 | const img=it.display_url||it.thumbnail_url||""; |
| 760 | − const title=esc((it.caption||it.title||it.text||"").slice(0,90)); | |
| 798 | + const isVid=it.type==="video"||it.type==="reel"||!!it.duration||it.video_views!=null; | |
| 799 | + const cap=esc((it.caption||it.title||it.text||"").slice(0,140)); | |
| 800 | + const when=_relTimeC(it.timestamp||it.published_at||it.published||it.created_at); | |
| 761 | 801 | const stats=[ |
| 762 | − it.views!=null?fmt.format(it.views)+" vues":null, | |
| 802 | + it.views!=null?fmt.format(it.views)+" vues":(it.video_views!=null?fmt.format(it.video_views)+" vues":null), | |
| 763 | 803 | it.likes!=null?fmt.format(it.likes)+" ❤":null, |
| 764 | 804 | it.comments!=null?fmt.format(it.comments)+" 💬":null, |
| 765 | 805 | it.retweets!=null?fmt.format(it.retweets)+" ↻":null, |
| 806 | + it.shares!=null?fmt.format(it.shares)+" ↗":null, | |
| 766 | 807 | ].filter(Boolean).join(" · "); |
| 767 | − const when=_relTime(it.timestamp||it.published_at||it.published||it.created_at); | |
| 768 | − return `<a class="cstrip-item" href="${esc(it.url||a.url)}" target="_blank" rel="noopener" onclick="event.stopPropagation()"> | |
| 769 | − ${img?`<span class="cstrip-thumb" style="background-image:url('${esc(img)}')">${it.type==="video"||it.type==="reel"||it.duration?'<span class="cstrip-play">▶</span>':""}${it.duration?`<span class="cstrip-dur">${esc(it.duration)}</span>`:""}</span>`:`<span class="cstrip-thumb noimg">${icon(a.platform)}</span>`} | |
| 770 | − ${title?`<span class="cstrip-cap">${title}</span>`:""} | |
| 771 | − <span class="cstrip-meta">${stats}${when?` · ${when}`:""}</span></a>`}).join(""); | |
| 772 | − return `<div class="cstrip"><div class="cstrip-h">Contenu récent</div><div class="cstrip-scroll">${cards}</div></div>`; | |
| 808 | + const thumb=img | |
| 809 | + ? `<span class="cg-thumb" data-icon='${esc(ico)}'><img loading="lazy" src="${esc(img)}" alt="" referrerpolicy="no-referrer" onerror="thumbFail(this)"> | |
| 810 | + ${isVid?'<span class="cg-play">▶</span>':""}${it.duration?`<span class="cg-dur">${esc(it.duration)}</span>`:""} | |
| 811 | + ${it.is_pinned?'<span class="cg-pin">📌</span>':""}</span>` | |
| 812 | + : `<span class="cg-thumb noimg" data-icon='${esc(ico)}'>${ico}${isVid?'<span class="cg-play">▶</span>':""}</span>`; | |
| 813 | + return `<a class="cg-card" href="${esc(it.url||a.url)}" target="_blank" rel="noopener"> | |
| 814 | + ${thumb} | |
| 815 | + <span class="cg-body">${cap?`<span class="cg-cap">${cap}</span>`:`<span class="cg-cap dim">Publication ${a.platform}</span>`} | |
| 816 | + <span class="cg-meta">${stats||"—"}${when?`<span class="cg-when">${when}</span>`:""}</span></span></a>`; | |
| 817 | + }).join(""); | |
| 818 | + return `<div class="cg-grid">${cards}</div>`; | |
| 819 | +} | |
| 820 | + | |
| 821 | +/* définitions de métriques calculées, par plateforme */ | |
| 822 | +function platformKPIs(a){ | |
| 823 | + const m=a.metrics||{}, out=[]; | |
| 824 | + const push=(v,l,suf)=>{if(v!=null&&v!=="")out.push([typeof v==="number"?fmt.format(v)+(suf||""):esc(v)+(suf||""),l]);}; | |
| 825 | + push(a.followers,a.platform==="discord"?"membres":a.platform==="patreon"?"patrons":"abonnés"); | |
| 826 | + push(m.engagement_rate_pct!=null?m.engagement_rate_pct:null,"engagement","%"); | |
| 827 | + push(m.avg_likes,"j'aime / pub (moy.)"); | |
| 828 | + push(m.avg_views!=null?m.avg_views:m.avg_video_views,"vues / pub (moy.)"); | |
| 829 | + push(m.avg_comments,"comm. / pub (moy.)"); | |
| 830 | + push(m.avg_retweets,"RT / pub (moy.)"); | |
| 831 | + push(m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week,"pubs / semaine"); | |
| 832 | + push(m.posts,"publications"); | |
| 833 | + push(m.videos,"vidéos"); | |
| 834 | + push(m.likes,a.platform==="tiktok"?"j'aime cumulés":"j'aime"); | |
| 835 | + push(m.following,"abonnements"); | |
| 836 | + push(m.online,"en ligne"); | |
| 837 | + push(m.boosts,"boosts"); | |
| 838 | + push(m.patrons,"patrons"); | |
| 839 | + push(m.live_viewers,"spectateurs (live)"); | |
| 840 | + return out; | |
| 841 | +} | |
| 842 | +function platformBadges(a){ | |
| 843 | + const m=a.metrics||{}; | |
| 844 | + return [ | |
| 845 | + a.verified?'<span class="abadge ok">✓ Vérifié</span>':"", | |
| 846 | + m.is_partner?'<span class="abadge">Partenaire</span>':"", | |
| 847 | + m.is_affiliate&&!m.is_partner?'<span class="abadge">Affilié</span>':"", | |
| 848 | + m.partnered?'<span class="abadge">Serveur partenaire</span>':"", | |
| 849 | + m.is_live_now?'<span class="abadge live">● En direct</span>':"", | |
| 850 | + m.is_seller?'<span class="abadge">Boutique</span>':"", | |
| 851 | + m.is_free===true?'<span class="abadge">Gratuit</span>':"", | |
| 852 | + m.is_monthly?'<span class="abadge">Mensuel</span>':"", | |
| 853 | + m.has_story?'<span class="abadge">Story active</span>':"", | |
| 854 | + m.has_spotlight?'<span class="abadge">Spotlight</span>':"", | |
| 855 | + m.country?`<span class="abadge">${esc(m.country)}</span>`:"", | |
| 856 | + (m.recent_games||[]).slice(0,2).map(g=>`<span class="abadge">🎮 ${esc(g)}</span>`).join(""), | |
| 857 | + (m.top_hashtags||[]).slice(0,3).map(h=>`<span class="abadge">#${esc(h)}</span>`).join(""), | |
| 858 | + ].filter(Boolean).join(""); | |
| 773 | 859 | } |
| 860 | + | |
| 861 | +/* panneau d'un compte (onglet plateforme) */ | |
| 862 | +function platformPanel(a,idx,active){ | |
| 863 | + const p=PLAT[a.platform]||PLAT.autre; | |
| 864 | + const conf=Math.round((a.confidence||0)*100); | |
| 865 | + const confCls=(a.confidence||0)>=0.75?"hi":"mid"; | |
| 866 | + const kpis=platformKPIs(a).map(([v,l])=>`<div class="kpi"><b>${v}</b><span>${l}</span></div>`).join(""); | |
| 867 | + const badges=platformBadges(a); | |
| 868 | + const stamp=(a.metrics||{})[a.platform+"_apify_at"]||a.last_checked; | |
| 869 | + const top=(a.metrics||{}).top_post||(a.metrics||{}).top_video||(a.metrics||{}).top_tweet; | |
| 870 | + return `<section class="ppanel${active?" on":""}" data-panel="${idx}"> | |
| 871 | + <div class="ppanel-head"> | |
| 872 | + <span class="plat" style="background:${p.color}">${icon(a.platform)}</span> | |
| 873 | + <div class="ppanel-id"><span class="ppanel-name">${p.label}${a.verified?checkSvg:""}</span> | |
| 874 | + <a class="ppanel-handle" href="${esc(a.url)}" target="_blank" rel="noopener">@${esc(a.handle)} ↗</a></div> | |
| 875 | + <span class="conf ${confCls}" title="Score de rattachement">${conf}%</span> | |
| 876 | + </div> | |
| 877 | + ${badges?`<div class="abadge-row">${badges}</div>`:""} | |
| 878 | + <div class="kpi-grid">${kpis||'<div class="kpi"><b>—</b><span>pas encore de métriques</span></div>'}</div> | |
| 879 | + ${top&&top.url?`<a class="ppanel-top" href="${esc(top.url)}" target="_blank" rel="noopener">★ Top contenu — ${top.views!=null?fmt.format(top.views)+" vues":""}${top.likes!=null?" · "+fmt.format(top.likes)+" ❤":""} ↗</a>`:""} | |
| 880 | + <h3 class="cg-title">Contenu récent</h3> | |
| 881 | + ${contentGrid(a)} | |
| 882 | + <div class="ppanel-foot">${stamp?"Dernière vérification "+esc(String(stamp).slice(0,10)):""}</div> | |
| 883 | + </section>`; | |
| 884 | +} | |
| 885 | + | |
| 886 | +window.showPtab=(i,el)=>{ | |
| 887 | + document.querySelectorAll(".ppanel").forEach(s=>s.classList.toggle("on",s.dataset.panel==String(i))); | |
| 888 | + document.querySelectorAll(".ptab").forEach(t=>t.classList.remove("on")); | |
| 889 | + if(el)el.classList.add("on"); | |
| 890 | + const ov=document.getElementById("ov-panel");if(ov)ov.classList.toggle("on",i==="ov"); | |
| 891 | +}; | |
| 892 | + | |
| 893 | +function computeAgg(c){ | |
| 894 | + const P=(c.platforms||[]); | |
| 895 | + const reach=c.total_reach||P.reduce((s,a)=>s+(a.followers||0),0); | |
| 896 | + const eng=P.map(a=>(a.metrics||{}).engagement_rate_pct).filter(v=>v!=null); | |
| 897 | + const avgEng=eng.length?(eng.reduce((s,v)=>s+v,0)/eng.length):null; | |
| 898 | + const content=P.reduce((s,a)=>s+_mediaItems(a).length,0); | |
| 899 | + const cad=P.map(a=>{const m=a.metrics||{};return m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week;}).filter(v=>v!=null); | |
| 900 | + const main=P.slice().sort((x,y)=>(y.followers||0)-(x.followers||0))[0]; | |
| 901 | + return {reach,avgEng,content,maxCad:cad.length?Math.max(...cad):null, | |
| 902 | + nPlat:P.length,nVerif:P.filter(a=>a.verified).length,main}; | |
| 903 | +} | |
| 904 | + | |
| 774 | 905 | async function renderCreator(id){ |
| 775 | 906 | app.innerHTML=header()+`<div class="container"><div class="spin"></div></div>`+footer()+tabbar("");bindNav(); |
| 776 | 907 | let c;try{c=await api("/api/creators/"+encodeURIComponent(id))} |
@@ -779,44 +910,38 @@ async function renderCreator(id){ | ||
| 779 | 910 | document.title=`${c.display_name} — Créa-Ka`; |
| 780 | 911 | const verified=(c.platforms||[]).some(a=>a.verified); |
| 781 | 912 | const niches=(c.niches||[]).map(n=>`<span class="mchip">${NICHE_LBL[n]||n}</span>`).join(""); |
| 782 | − const M_LBL={following:"abonnements",posts:"publications",likes:"j'aime", | |
| 783 | − views:"vues",videos:"vidéos",episodes:"épisodes",images:"photos", | |
| 784 | − friends:"amis",listed:"listes"}; | |
| 785 | − const accs=(c.platforms||[]).map(a=>{const p=PLAT[a.platform]||PLAT.autre; | |
| 786 | − const conf=(a.confidence||0)>=0.75?["hi","confiance élevée"]:["mid","à confirmer"]; | |
| 787 | − const m=a.metrics||{}; | |
| 788 | − const mx=Object.entries(m).filter(([k,v])=>M_LBL[k]&&v!=null) | |
| 789 | − .map(([k,v])=>`${fmt.format(v)} ${M_LBL[k]}`).join(" · "); | |
| 790 | − // rangée de statistiques riches (enrichissement Apify multi-plateforme) | |
| 791 | − const stat=(v,l)=>v==null?"":`<span class="astat"><b>${typeof v==="number"?fmt.format(v):esc(v)}</b><span>${l}</span></span>`; | |
| 792 | − const stats=[ | |
| 793 | − stat(m.engagement_rate_pct!=null?m.engagement_rate_pct+" %":null,"engagement"), | |
| 794 | − stat(m.avg_likes,"j'aime/moy"), | |
| 795 | − stat(m.avg_views!=null?m.avg_views:m.avg_video_views,"vues/moy"), | |
| 796 | − stat(m.avg_retweets,"RT/moy"), | |
| 797 | − stat(m.posts_per_week!=null?m.posts_per_week:m.tweets_per_week,"/semaine"), | |
| 798 | − ].filter(Boolean).join(""); | |
| 799 | − const badges=[ | |
| 800 | − m.is_partner?'<span class="abadge">Partenaire</span>':"", | |
| 801 | − m.is_affiliate&&!m.is_partner?'<span class="abadge">Affilié</span>':"", | |
| 802 | − m.is_live_now?'<span class="abadge live">● En direct</span>':"", | |
| 803 | − m.is_seller?'<span class="abadge">Boutique</span>':"", | |
| 804 | − m.is_free===true?'<span class="abadge">Gratuit</span>':"", | |
| 805 | − m.has_story?'<span class="abadge">Story active</span>':"", | |
| 806 | − (m.recent_games||[]).length?`<span class="abadge">${esc((m.recent_games||[])[0])}</span>`:"", | |
| 807 | − (m.top_hashtags||[]).slice(0,2).map(h=>`<span class="abadge">#${esc(h)}</span>`).join(""), | |
| 808 | − ].filter(Boolean).join(""); | |
| 809 | − return `<div class="acc-card"> | |
| 810 | − <a class="acc" href="${esc(a.url)}" target="_blank" rel="noopener"> | |
| 811 | − <span class="plat" style="background:${p.color}">${icon(a.platform)}</span> | |
| 812 | − <span class="acc-mid"><span class="acc-name">${p.label}${a.verified?checkSvg:""}</span> | |
| 813 | − <span class="acc-handle">@${esc(a.handle)}</span> | |
| 814 | − <span class="acc-handle" style="overflow:visible"><span class="conf ${conf[0]}" title="Score de rattachement ${Math.round((a.confidence||0)*100)} %">${conf[1]}</span>${mx?` <span style="font-size:11px">${mx}</span>`:""}</span></span> | |
| 815 | − <span class="acc-right">${a.followers?`<b>${fmt.format(a.followers)}</b><span>abonnés</span>`:`<span style="font-size:11px">voir le profil ↗</span>`}</span></a> | |
| 816 | − ${stats?`<div class="astat-row">${stats}</div>`:""} | |
| 817 | − ${badges?`<div class="abadge-row">${badges}</div>`:""} | |
| 818 | − ${contentStrip(a)}</div>`}).join(""); | |
| 819 | − const checked=(c.platforms||[]).map(a=>(a.metrics||{}).instagram_apify_at||a.last_checked).filter(Boolean).sort().pop()||(c.platforms||[]).map(a=>a.last_checked).filter(Boolean).sort().pop(); | |
| 913 | + const P=(c.platforms||[]).slice().sort((x,y)=>(y.followers||0)-(x.followers||0)); | |
| 914 | + const g=computeAgg(c); | |
| 915 | + // bandeau de métriques CALCULÉES (multi-plateforme) | |
| 916 | + const kpi=(v,l)=>v==null?"":`<div class="hkpi"><b>${typeof v==="number"?fmt.format(v):esc(v)}</b><span>${l}</span></div>`; | |
| 917 | + const heroKpis=[ | |
| 918 | + kpi(g.reach,"abonnés cumulés"), | |
| 919 | + kpi(g.nPlat,"plateformes"), | |
| 920 | + kpi(g.avgEng!=null?Math.round(g.avgEng*100)/100+" %":null,"engagement moyen"), | |
| 921 | + kpi(g.maxCad!=null?g.maxCad:null,"pubs / semaine"), | |
| 922 | + kpi(g.content||null,"contenus analysés"), | |
| 923 | + kpi(g.main?(PLAT[g.main.platform]||PLAT.autre).label:null,"plateforme nº 1"), | |
| 924 | + ].filter(Boolean).join(""); | |
| 925 | + // barres comparatives de portée par plateforme (vue d'ensemble) | |
| 926 | + const maxF=Math.max(1,...P.map(a=>a.followers||0)); | |
| 927 | + const bars=P.map(a=>{const p=PLAT[a.platform]||PLAT.autre;const w=Math.round((a.followers||0)/maxF*100); | |
| 928 | + return `<div class="obar"><span class="obar-ico" style="background:${p.color}">${icon(a.platform)}</span> | |
| 929 | + <span class="obar-lab">${p.label}</span> | |
| 930 | + <span class="obar-track"><span class="obar-fill" style="width:${w}%;background:${p.color}"></span></span> | |
| 931 | + <span class="obar-val">${a.followers!=null?fmt.format(a.followers):"—"}</span></div>`;}).join(""); | |
| 932 | + // onglets + panneaux | |
| 933 | + const tabs=`<button class="ptab on" onclick="showPtab('ov',this)">Vue d'ensemble</button>`+ | |
| 934 | + P.map((a,i)=>{const p=PLAT[a.platform]||PLAT.autre; | |
| 935 | + return `<button class="ptab" onclick="showPtab(${i},this)"> | |
| 936 | + <span class="ptab-ico" style="color:${p.color}">${icon(a.platform)}</span>${p.label} | |
| 937 | + ${a.followers!=null?`<span class="ptab-n">${fmt.format(a.followers)}</span>`:""}</button>`;}).join(""); | |
| 938 | + const ovPanel=`<section class="ppanel on" id="ov-panel" data-panel="ov"> | |
| 939 | + <h3 class="cg-title">Portée par plateforme</h3> | |
| 940 | + <div class="obars">${bars||"<p class=klabel>Aucune plateforme.</p>"}</div> | |
| 941 | + ${c.link_in_bio_url?`<a class="linkinbio" href="${esc(c.link_in_bio_url)}" target="_blank" rel="noopener">${icon("linktree")} Sa page de liens officielle ↗</a>`:""} | |
| 942 | + </section>`; | |
| 943 | + const panels=ovPanel+P.map((a,i)=>platformPanel(a,i,false)).join(""); | |
| 944 | + const checked=(c.platforms||[]).map(a=>{const m=a.metrics||{};return m[a.platform+"_apify_at"]||a.last_checked;}).filter(Boolean).sort().pop(); | |
| 820 | 945 | app.innerHTML=header()+` |
| 821 | 946 | <div class="container cpage"> |
| 822 | 947 | <a class="backlink" href="/" data-nav>← Tous les créateurs</a> |
@@ -825,16 +950,15 @@ async function renderCreator(id){ | ||
| 825 | 950 | <div style="flex:1;min-width:200px"><h1>${esc(c.display_name)}${verified?checkSvg:""}</h1> |
| 826 | 951 | <div class="meta"><span class="mchip o">${TIER_LBL[c.audience_tier]||c.audience_tier}</span> |
| 827 | 952 | ${niches}${c.region?`<span class="mchip">${esc(c.region)}</span>`:""} |
| 828 | − ${(c.languages||[]).map(l=>`<span class="mchip">${{fr:"Français",en:"Anglais",bilingue:"Bilingue"}[l]||l}</span>`).join("")}</div></div> | |
| 829 | − ${c.total_reach?`<div class="cpage-reach"><b>${fmt.format(c.total_reach)}</b><span>abonnés cumulés</span></div>`:""} | |
| 953 | + ${(c.languages||[]).map(l=>`<span class="mchip">${{fr:"Français",en:"Anglais",bilingue:"Bilingue"}[l]||l}</span>`).join("")}</div> | |
| 954 | + ${c.bio?`<p class="cpage-bio">${esc(c.bio)}</p>`:""}</div> | |
| 830 | 955 | </div> |
| 831 | − ${c.bio?`<p class="cpage-bio">${esc(c.bio)}</p>`:""} | |
| 956 | + <div class="hkpi-row">${heroKpis}</div> | |
| 832 | 957 | ${c.notes?`<div class="cpage-notes"><span class="klabel">Contexte public</span><br>${esc(c.notes)}</div>`:""} |
| 833 | − <h2 class="sect-title">Ses plateformes <span class="chip">${(c.platforms||[]).length}</span></h2> | |
| 834 | − <div class="acc-grid">${accs}</div> | |
| 835 | − ${c.link_in_bio_url?`<a class="linkinbio" href="${esc(c.link_in_bio_url)}" target="_blank" rel="noopener">${icon("linktree")} Sa page de liens officielle ↗</a>`:""} | |
| 836 | − <div class="cpage-foot">Sources : listes médias publiques, Wikidata, annuaires publics et profils publics des plateformes. | |
| 837 | − Audiences approximatives${checked?`, dernière vérification ${esc(checked.slice(0,10))}`:""}. | |
| 958 | + <div class="ptabs">${tabs}</div> | |
| 959 | + <div class="ppanels">${panels}</div> | |
| 960 | + <div class="cpage-foot">Sources : listes médias publiques, Wikidata, annuaires publics et profils publics des plateformes (enrichissement Apify). | |
| 961 | + Audiences et métriques approximatives${checked?`, dernière vérification ${esc(String(checked).slice(0,10))}`:""}. | |
| 838 | 962 | Une erreur dans cette fiche ? <a href="/retrait" data-nav>Signaler / demander un retrait</a>.</div> |
| 839 | 963 | </div>`+footer()+tabbar(""); |
| 840 | 964 | bindNav();window.scrollTo(0,0); |
| 841 | 965 | |