Rich card data

This commit is contained in:
2026-10-19 16:47:27 -04:00
parent f26e38b096
commit 6155e8b3ed
31 changed files with 633 additions and 25 deletions
+44
View File
@@ -0,0 +1,44 @@
import { BookOpen, Eye, Globe2, Tags, TrendingUp } from 'lucide-react';
import { useEnrichment } from './useEnrichment.ts';
import type { 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}`;
export function EnrichmentScores({data}: {data:PageEnrichment}){
return <div className="enrichment-scores" aria-label="Live 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(data.stats.attention)}</b><small>Attention</small></span>
<span title="Depth: logarithmic score from article bytes, not a quality judgment"><BookOpen size={12}/><b>{score(data.stats.depth)}</b><small>Depth</small></span>
<span title="Reach: logarithmic score from interlanguage links"><Globe2 size={12}/><b>{score(data.stats.reach)}</b><small>Reach</small></span>
</div>;
}
export function CardInsights({pageId}:{pageId:number}){
const {data,error,retry}=useEnrichment(pageId);
if(!data)return <section className="article-insights" aria-label="Wikipedia insights"><h3>Wikipedia insights</h3><p role="status">{error?'Live metadata is unavailable. The NFT and its original game stats are unchanged.':'Loading Wikipedia metadata and monthly readership…'}</p>{error&&<button className="text-button" onClick={retry}>Retry live metadata</button>}</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>Wikipedia insights</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>}
<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>;
}