import { decodeEntities } from '@wordpress/html-entities'; import { __ } from '@wordpress/i18n'; const ROW_LIMIT = 10; // Subset of the /accelerate/v1/top post shape this tile needs. export type TopPost = { id: number; title: string; views: number; }; // Matches the BreakdownPanel HostList row contract (key + value), with an // optional label override so the row can show a decoded title while keeping // the post id as its stable rank/React identity. export type ContentItem = { key: string; value: number; label: string; }; /** * Map view-ranked /top posts to HostList rows. Pure (no fetch, no React) so * the decode + fallback + slice logic is unit-testable in isolation, like * prettyLabel. `/top` already returns posts ordered by views, so we just cap * the list. Post id is the row key (stable identity); the decoded title is the * display label, falling back to a localized "(no title)" for untitled posts. */ export function toContentItems( posts: TopPost[], limit: number = ROW_LIMIT ): ContentItem[] { return posts .slice( 0, limit ) .map( post => ( { key: String( post.id ), value: post.views, label: decodeEntities( post.title || '' ) || __( '(no title)', 'altis' ), } ) ); }