67 lines
8.1 KiB
TypeScript
67 lines
8.1 KiB
TypeScript
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>;
|
||
}
|