Session 42: Player Intelligence System — archetypes, stat strips, player profile, enhanced cards (2011 tests)
Built from the Claude Design "VYNDR Player Intelligence" bundle (10 sections). - Archetypes: src/services/archetypeService.js — 41 archetypes (15 NBA / 5 WNBA-unique / 15 MLB / 6 soccer), classify -> primary+secondary+blend. Frontend visual map web/src/lib/archetypes.js (colors verified == backend). ArchetypeBadge (full/ghost/tint + glyphs) + ArchetypeBlend (DNA bar). - StatStrip (compact/expanded): player name once, horizontal mono stats, inline GradeBadge props, onPlayerClick -> profile. - Stats API: extended src/routes/stats.js with /player/:name, /leaders, /game/:id (rate-limited). Aggregation in playerIntelService.js (sanitizes name param; grades cache; graceful on cold cache). Next proxies added. - Player Profile /player/[name]: all 9 design sections, graceful empty states. - Enhanced GameCard (MLB pitchers + player-grouped StatStrips) + GradeResultCard (archetype strip + stat context + VYNDR intelligence, optional/self-hiding via gradeAdapter.buildIntelFields). Player-name links wired everywhere. - Settings page replaces the S41 redirect (account/subscription/notifications/ display/responsible-play/danger-zone with DELETE-gated delete). LINKS to the real /settings/security MFA page — does not replace it. + BookChip. - Bonus: Stats Explorer /explore (real /api/stats/leaders leaderboard); added Explore + Settings to Nav MORE. Deferred (need data pipelines, Session 43): Team Hub, Offseason Intel, Slate redesign, Stats Explorer sub-panels. Backend 1940 -> 2011 tests (+71), 157 suites. Web build clean (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
/* Pure display transforms for the player profile page (Session 42).
|
||||
CommonJS so it's unit-testable + importable from the .tsx page. */
|
||||
|
||||
/** Two-letter mono initials for the hero avatar. */
|
||||
function initials(name) {
|
||||
const parts = String(name || '').trim().split(/\s+/).filter(Boolean);
|
||||
if (parts.length === 0) return '??';
|
||||
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
||||
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
||||
}
|
||||
|
||||
const SPORT_LABELS = { nba: 'NBA', mlb: 'MLB', wnba: 'WNBA', soccer: 'SOC' };
|
||||
function sportLabel(sport) {
|
||||
return SPORT_LABELS[String(sport || '').toLowerCase()] || String(sport || '').toUpperCase();
|
||||
}
|
||||
|
||||
/** Prop DNA → flat list of { prop, state, color } for the grid (reliable first). */
|
||||
function dnaRows(propDNA) {
|
||||
const dna = propDNA || { reliable: [], volatile: [] };
|
||||
const pretty = (s) => String(s).replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
return [
|
||||
...(dna.reliable || []).map((p) => ({ prop: pretty(p), state: 'RELIABLE', color: '#00D4A0' })),
|
||||
...(dna.volatile || []).map((p) => ({ prop: pretty(p), state: 'VOLATILE', color: '#FFB347' })),
|
||||
];
|
||||
}
|
||||
|
||||
/** Short human readout under the DNA bar, e.g. "Primary lane: Power Pull". */
|
||||
function blendReadout(archetype) {
|
||||
if (!archetype || !archetype.primary) return '';
|
||||
const prim = archetype.primary.name;
|
||||
const sec = archetype.secondary ? `, supported by ${title(archetype.secondary.name)}` : '';
|
||||
return `Primary lane: ${title(prim)}${sec}.`;
|
||||
}
|
||||
|
||||
function title(s) {
|
||||
return String(s || '').toLowerCase().replace(/\b\w/g, (c) => c.toUpperCase());
|
||||
}
|
||||
|
||||
module.exports = { initials, sportLabel, dnaRows, blendReadout, title };
|
||||
Reference in New Issue
Block a user