Files
wikideck/src/CardInsights.tsx
T
2026-10-19 18:52:49 -04:00

67 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { BookOpen, Eye, Globe2, Tags, TrendingUp } from 'lucide-react';
import { useEnrichment } from './useEnrichment.ts';
import type { MintStatsSnapshot, PageEnrichment } from '../shared/enrichment.ts';
import './enrichment.css';
const compact=new Intl.NumberFormat('en',{notation:'compact',maximumFractionDigits:1});
const full=new Intl.NumberFormat('en',{maximumFractionDigits:0});
const month=(value:string)=>new Date(`${value}-01T00:00:00Z`).toLocaleDateString('en',{month:'short',year:'numeric',timeZone:'UTC'});
const score=(value:number|null)=>value===null?'—':`${value}`;
function ScoreStrip({stats,label}: {stats:PageEnrichment['stats'];label:string}){
return <div className="enrichment-scores" aria-label={`${label} Wikipedia-derived scores out of 100`}>
<span title="Attention: logarithmic score from the latest complete month's human page views"><Eye size={12}/><b>{score(stats.attention)}</b><small>Attention</small></span>
<span title="Depth: logarithmic score from article bytes, not a quality judgment"><BookOpen size={12}/><b>{score(stats.depth)}</b><small>Depth</small></span>
<span title="Reach: logarithmic score from interlanguage links"><Globe2 size={12}/><b>{score(stats.reach)}</b><small>Reach</small></span>
</div>;
}
export function EnrichmentScores({data}: {data:PageEnrichment}){
return <ScoreStrip stats={data.stats} label="Current"/>;
}
export function MintStatsScores({snapshot,compactView=false}: {snapshot:MintStatsSnapshot;compactView?:boolean}){
const latest=snapshot.pageviews.months.at(-1);
if(compactView)return <div className="mint-snapshot-compact"><span>AT MINT</span><ScoreStrip stats={snapshot.stats} label="At mint"/><small>{latest?`${compact.format(latest.views)} views · ${snapshot.pageBytes.toLocaleString()} bytes`:'Page views unavailable'} · {new Date(snapshot.capturedAt).toLocaleDateString()}</small></div>;
return <section className="mint-snapshot" aria-label="Wikipedia statistics snapshot at mint">
<div className="insights-heading"><h3>Statistics at mint</h3><span>{new Date(snapshot.capturedAt).toLocaleString()}</span></div>
<ScoreStrip stats={snapshot.stats} label="At mint"/>
<dl className="article-fields">
<div><dt><Eye size={12}/> Monthly views</dt><dd>{latest?full.format(latest.views):'Unavailable'}<small>{month(snapshot.pageviews.latestMonth)} · complete month</small></dd></div>
<div><dt>6-month views</dt><dd>{snapshot.pageviews.total===null?'Unavailable':full.format(snapshot.pageviews.total)}</dd></div>
<div><dt><TrendingUp size={12}/> Monthly change</dt><dd>{snapshot.pageviews.trendPercent===null?'Unavailable':`${snapshot.pageviews.trendPercent>=0?'+':''}${snapshot.pageviews.trendPercent.toFixed(1)}%`}</dd></div>
<div><dt>Daily average</dt><dd>{snapshot.pageviews.dailyAverage===null?'Unavailable':full.format(snapshot.pageviews.dailyAverage)}<small>latest complete month</small></dd></div>
<div><dt>Article size</dt><dd>{full.format(snapshot.pageBytes)} bytes</dd></div>
<div><dt>Languages</dt><dd>{snapshot.languagesTruncated?'≥ ':''}{full.format(snapshot.languageCount)}<small>other-language links</small></dd></div>
<div><dt>Revision at capture</dt><dd><a href={`https://en.wikipedia.org/w/index.php?oldid=${snapshot.revisionId}`} target="_blank" rel="noreferrer">{snapshot.revisionId}</a></dd></div>
</dl>
{snapshot.pageviews.status==='available'?<div className="mint-months" aria-label="Six-month pageview snapshot">{snapshot.pageviews.months.map(item=><span key={item.month}>{month(item.month)}: {full.format(item.views)}</span>)}</div>:<p className="insights-unavailable">Page views were unavailable when this NFT was minted. Missing counts were not recorded as zero.</p>}
</section>;
}
export function CardInsights({pageId,snapshot}:{pageId:number;snapshot?:MintStatsSnapshot}){
const {data,error,retry}=useEnrichment(pageId);
if(!data)return <section className="article-insights" aria-label="Wikipedia insights"><h3>Current Wikipedia statistics</h3><p role="status">{error?'Current metadata is unavailable. The mint-time statistics remain on the NFT.':'Loading current Wikipedia metadata and readership…'}</p>{error&&<button className="text-button" onClick={retry}>Retry live metadata</button>}{snapshot&&<MintStatsScores snapshot={snapshot}/>}</section>;
const views=data.pageviews;
const latest=views.months.at(-1);
const maximum=Math.max(1,...views.months.map(m=>m.views));
const trend=views.trendPercent;
return <section className="article-insights" aria-label="Wikipedia insights">
<div className="insights-heading"><h3>Current Wikipedia statistics</h3><span>LIVE · OFF-CHAIN</span></div>
{data.description&&<p>{data.description}</p>}
<EnrichmentScores data={data}/>
<dl className="article-fields">
<div><dt><Eye size={12}/> Monthly views</dt><dd>{latest?full.format(latest.views):'Unavailable'}<small>{month(views.latestMonth)} · human traffic</small></dd></div>
<div><dt><TrendingUp size={12}/> Monthly change</dt><dd>{trend===null?'Unavailable':`${trend>=0?'+':''}${trend.toFixed(1)}%`}<small>vs. preceding month</small></dd></div>
<div><dt>6-month views</dt><dd>{views.total===null?'Unavailable':full.format(views.total)}</dd></div>
<div><dt>Daily average</dt><dd>{views.dailyAverage===null?'Unavailable':full.format(views.dailyAverage)}<small>latest complete month</small></dd></div>
<div><dt>Article size</dt><dd>{full.format(data.pageBytes)} bytes</dd></div>
<div><dt>Languages</dt><dd>{data.languagesTruncated?'≥ ':''}{full.format(data.languageCount)}<small>other-language links</small></dd></div>
<div><dt>Live revision</dt><dd><a href={`https://en.wikipedia.org/w/index.php?oldid=${data.revisionId}`} target="_blank" rel="noreferrer">{data.revisionId}</a></dd></div>
<div><dt>Wikidata</dt><dd>{data.wikidataId?<a href={`https://www.wikidata.org/wiki/${data.wikidataId}`} target="_blank" rel="noreferrer">{data.wikidataId}</a>:'Not linked'}</dd></div>
</dl>
{views.status==='available'?<figure className="pageview-history"><figcaption>Monthly readership · six complete UTC months</figcaption><div className="pageview-bars">{views.months.map(item=><div key={item.month} title={`${month(item.month)}: ${full.format(item.views)} views`}><span>{compact.format(item.views)}</span><div className="pageview-track"><i style={{height:`${Math.max(1,item.views/maximum*100)}%`}}/></div><small>{month(item.month)}</small></div>)}</div><table className="visually-hidden"><caption>Monthly human page views</caption><tbody>{views.months.map(item=><tr key={item.month}><th>{month(item.month)}</th><td>{item.views}</td></tr>)}</tbody></table></figure>:<p className="insights-unavailable">Page views are unavailable or incomplete. Unknown counts are not treated as zero.</p>}
{snapshot&&<MintStatsScores snapshot={snapshot}/>}
<h4 className="insights-category-heading"><Tags size={13}/> Wikipedia categories{data.categoriesTruncated?' · first 50':''}</h4>
<div className="article-categories">{data.categories.length?data.categories.map(category=><a key={category} href={`https://en.wikipedia.org/wiki/${encodeURIComponent(`Category:${category.replace(/ /g,'_')}`)}`} target="_blank" rel="noreferrer">{category}</a>):<span>No visible categories returned</span>}</div>
<details className="insights-method"><summary>How are these scores calculated?</summary><p>Scores range from 0–100, capped and rounded: Attention = log₁₀(monthly views + 1) ÷ 6 × 100; Depth = log₁₀(article bytes + 1) ÷ 6 × 100; Reach = log₁₀(language links + 1) ÷ 3 × 100. Depth measures size, not accuracy or quality. Scores can change and do not affect proof-derived rarity, NFT ownership, or consensus.</p></details>
<p className="insights-attribution">Retrieved {new Date(data.fetchedAt).toLocaleString()}. Live revision may differ from the minted snapshot. Sources: <a href={`https://en.wikipedia.org/?curid=${pageId}`} target="_blank" rel="noreferrer">Wikipedia contributors</a> (CC BY-SA 4.0) and Wikimedia Analytics (CC0). Views use all-access, user traffic and exclude classified bots. Article moves may affect title-based view history.</p>
</section>;
}