Merge Wave 2B (wiring/data): sportsbook SVG wordmarks + team-logo coverage
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+55
-15
@@ -1,29 +1,69 @@
|
||||
/* Sportsbook brand map (Session 42) — ported from the design's BookChip.dc.html
|
||||
BOOKS table. CommonJS so it's testable + importable from the .tsx chip. */
|
||||
BOOKS table. CommonJS so it's testable + importable from the .tsx chip.
|
||||
|
||||
Wave 2B (data train) — the map now covers EVERY key the odds feed emits
|
||||
(`oddsNormalizer.ALLOWED_BOOKS`): draftkings, fanduel, betmgm, caesars,
|
||||
fanatics, bet365, hardrockbet, pointsbet, betrivers, pinnacle. No live book
|
||||
falls to the neutral-gray default. Entries carry a `slug` = the canonical
|
||||
lowercase book key; `bookSlug()` resolves any input (id/code/name) to it, and
|
||||
BUNDLED_BOOK_SVGS names the 8 books that have a local wordmark SVG under
|
||||
`web/public/books/{slug}.svg` (self-authored styled wordmarks, swappable for
|
||||
official press-kit art without a code change). */
|
||||
|
||||
const BOOKS = {
|
||||
DK: { name: 'DraftKings', mono: 'DK', bg: '#0E1E12', fg: '#53D337', bd: '#53D33755' },
|
||||
DRAFTKINGS: { name: 'DraftKings', mono: 'DK', bg: '#0E1E12', fg: '#53D337', bd: '#53D33755' },
|
||||
FD: { name: 'FanDuel', mono: 'FD', bg: '#0A1830', fg: '#1493FF', bd: '#1493FF55' },
|
||||
FANDUEL: { name: 'FanDuel', mono: 'FD', bg: '#0A1830', fg: '#1493FF', bd: '#1493FF55' },
|
||||
MGM: { name: 'BetMGM', mono: 'MGM', bg: '#1A1405', fg: '#C8A24B', bd: '#C8A24B55' },
|
||||
BETMGM: { name: 'BetMGM', mono: 'MGM', bg: '#1A1405', fg: '#C8A24B', bd: '#C8A24B55' },
|
||||
CZR: { name: 'Caesars', mono: 'CZR', bg: '#0C1A14', fg: '#1A7F5A', bd: '#1A7F5A66' },
|
||||
CAESARS: { name: 'Caesars', mono: 'CZR', bg: '#0C1A14', fg: '#1A7F5A', bd: '#1A7F5A66' },
|
||||
DK: { name: 'DraftKings', mono: 'DK', slug: 'draftkings', bg: '#0E1E12', fg: '#53D337', bd: '#53D33755' },
|
||||
DRAFTKINGS: { name: 'DraftKings', mono: 'DK', slug: 'draftkings', bg: '#0E1E12', fg: '#53D337', bd: '#53D33755' },
|
||||
FD: { name: 'FanDuel', mono: 'FD', slug: 'fanduel', bg: '#0A1830', fg: '#1493FF', bd: '#1493FF55' },
|
||||
FANDUEL: { name: 'FanDuel', mono: 'FD', slug: 'fanduel', bg: '#0A1830', fg: '#1493FF', bd: '#1493FF55' },
|
||||
MGM: { name: 'BetMGM', mono: 'MGM', slug: 'betmgm', bg: '#1A1405', fg: '#C8A24B', bd: '#C8A24B55' },
|
||||
BETMGM: { name: 'BetMGM', mono: 'MGM', slug: 'betmgm', bg: '#1A1405', fg: '#C8A24B', bd: '#C8A24B55' },
|
||||
CZR: { name: 'Caesars', mono: 'CZR', slug: 'caesars', bg: '#0C1A14', fg: '#1A7F5A', bd: '#1A7F5A66' },
|
||||
CAESARS: { name: 'Caesars', mono: 'CZR', slug: 'caesars', bg: '#0C1A14', fg: '#1A7F5A', bd: '#1A7F5A66' },
|
||||
ESPN: { name: 'ESPN BET', mono: 'EB', bg: '#2A0A0A', fg: '#FF4A4A', bd: '#FF4A4A55' },
|
||||
'ESPN BET': { name: 'ESPN BET', mono: 'EB', bg: '#2A0A0A', fg: '#FF4A4A', bd: '#FF4A4A55' },
|
||||
BR: { name: 'BetRivers', mono: 'BR', bg: '#1A0E22', fg: '#B07CFF', bd: '#B07CFF55' },
|
||||
// BetRivers is a blue book (its logo is blue "BetRivers"), not purple.
|
||||
BR: { name: 'BetRivers', mono: 'BR', slug: 'betrivers', bg: '#08121F', fg: '#3E8FD6', bd: '#3E8FD655' },
|
||||
BETRIVERS: { name: 'BetRivers', mono: 'BR', slug: 'betrivers', bg: '#08121F', fg: '#3E8FD6', bd: '#3E8FD655' },
|
||||
PB: { name: 'PrizePicks', mono: 'PP', bg: '#0A1A1A', fg: '#19E0C8', bd: '#19E0C855' },
|
||||
PRIZEPICKS: { name: 'PrizePicks', mono: 'PP', bg: '#0A1A1A', fg: '#19E0C8', bd: '#19E0C855' },
|
||||
FAN: { name: 'Fanatics', mono: 'FAN', bg: '#1A0A0A', fg: '#E84855', bd: '#E8485555' },
|
||||
B365: { name: 'bet365', mono: '365', bg: '#0A1A12', fg: '#2E8B57', bd: '#2E8B5766' },
|
||||
HR: { name: 'Hard Rock', mono: 'HR', bg: '#1A1206', fg: '#D4A24B', bd: '#D4A24B55' },
|
||||
FAN: { name: 'Fanatics', mono: 'FAN', slug: 'fanatics', bg: '#1A0A0A', fg: '#E84855', bd: '#E8485555' },
|
||||
FANATICS: { name: 'Fanatics', mono: 'FAN', slug: 'fanatics', bg: '#1A0A0A', fg: '#E84855', bd: '#E8485555' },
|
||||
B365: { name: 'bet365', mono: '365', slug: 'bet365', bg: '#0A1A12', fg: '#3EA76B', bd: '#3EA76B66' },
|
||||
BET365: { name: 'bet365', mono: '365', slug: 'bet365', bg: '#0A1A12', fg: '#3EA76B', bd: '#3EA76B66' },
|
||||
HR: { name: 'Hard Rock', mono: 'HR', slug: 'hardrockbet', bg: '#1A1206', fg: '#D4A24B', bd: '#D4A24B55' },
|
||||
HARDROCKBET: { name: 'Hard Rock', mono: 'HR', slug: 'hardrockbet', bg: '#1A1206', fg: '#D4A24B', bd: '#D4A24B55' },
|
||||
POINTSBET: { name: 'PointsBet', mono: 'PTS', slug: 'pointsbet', bg: '#1F0808', fg: '#E4344A', bd: '#E4344A55' },
|
||||
PINNACLE: { name: 'Pinnacle', mono: 'PIN', slug: 'pinnacle', bg: '#180C0E', fg: '#C8434F', bd: '#C8434F55' },
|
||||
UD: { name: 'Underdog', mono: 'UD', bg: '#15101F', fg: '#A07CFF', bd: '#A07CFF55' },
|
||||
};
|
||||
|
||||
// The 8 books with a bundled local wordmark SVG (web/public/books/{slug}.svg).
|
||||
// Self-authored styled text wordmarks — NOT copied trademarked logo glyphs.
|
||||
const BUNDLED_BOOK_SVGS = new Set([
|
||||
'draftkings', 'fanduel', 'betmgm', 'caesars', 'bet365', 'pinnacle', 'hardrockbet', 'betrivers',
|
||||
]);
|
||||
|
||||
const DEFAULT_FG = '#B8BCC8';
|
||||
|
||||
function bookInfo(book) {
|
||||
const key = String(book == null ? '' : book).toUpperCase();
|
||||
return BOOKS[key] || { name: key, mono: key.slice(0, 3) || '?', bg: '#14141E', fg: '#B8BCC8', bd: '#23232F' };
|
||||
return BOOKS[key] || { name: key, mono: key.slice(0, 3) || '?', bg: '#14141E', fg: DEFAULT_FG, bd: '#23232F' };
|
||||
}
|
||||
|
||||
/** Canonical lowercase book slug for any input (id/code/name), or null when the
|
||||
* book isn't in the registry. Used to look up the bundled wordmark SVG. */
|
||||
function bookSlug(book) {
|
||||
const info = bookInfo(book);
|
||||
if (info.slug) return info.slug;
|
||||
// Unknown-but-nameable: derive a slug from the resolved name (still not the
|
||||
// raw lowercase feed key). Only bundled slugs matter for the SVG lookup.
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Does `book` have a bundled local wordmark SVG? */
|
||||
function hasBookSvg(book) {
|
||||
const slug = bookSlug(book);
|
||||
return slug != null && BUNDLED_BOOK_SVGS.has(slug);
|
||||
}
|
||||
|
||||
/** Canonical comparison key for a book (Session 49) — resolves "DK"/"draftkings"
|
||||
@@ -39,4 +79,4 @@ function isPreferredBook(book, preferred) {
|
||||
return preferred.some((p) => bookKey(p) === k);
|
||||
}
|
||||
|
||||
module.exports = { BOOKS, bookInfo, bookKey, isPreferredBook };
|
||||
module.exports = { BOOKS, BUNDLED_BOOK_SVGS, bookInfo, bookSlug, hasBookSvg, bookKey, isPreferredBook };
|
||||
|
||||
+10
-1
@@ -195,7 +195,16 @@ function resolveTeam(key, sport) {
|
||||
const up = String(key).toUpperCase().trim();
|
||||
if (table[up]) return { abbr: up, sport: sp, ...table[up] };
|
||||
// statsapi/ESPN alias fallbacks for the two MLB mismatches
|
||||
const ALIAS = { ARI: 'AZ', CHW: 'CWS', OAK: 'ATH', SFG: 'SF', TBR: 'TB', WSN: 'WSH', KCR: 'KC', SDP: 'SD', GS: 'GSW', NO: 'NOP', NYK: 'NYK', UTAH: 'UTA', PHO: 'PHX' };
|
||||
// Global alias table. resolveTeam checks the real abbr FIRST, so an entry
|
||||
// only fires for a sport whose table lacks that key — e.g. WSH resolves
|
||||
// directly to the MLB Nationals, but NBA/WNBA (no WSH key) fall through to
|
||||
// WAS (Wizards/Mystics). ESPN-schedule abbrs the ball sports actually emit
|
||||
// (SA/NY/WSH/BRK for NBA, CONN for WNBA) are covered here.
|
||||
const ALIAS = {
|
||||
ARI: 'AZ', CHW: 'CWS', OAK: 'ATH', SFG: 'SF', TBR: 'TB', WSN: 'WSH', KCR: 'KC', SDP: 'SD',
|
||||
GS: 'GSW', NO: 'NOP', NYK: 'NYK', UTAH: 'UTA', PHO: 'PHX',
|
||||
SA: 'SAS', NY: 'NYK', WSH: 'WAS', BRK: 'BKN', CONN: 'CON',
|
||||
};
|
||||
if (ALIAS[up] && table[ALIAS[up]]) return { abbr: ALIAS[up], sport: sp, ...table[ALIAS[up]] };
|
||||
if (sp === 'soccer' && SOCCER_ALIAS[normKey(key)]) {
|
||||
const a = SOCCER_ALIAS[normKey(key)];
|
||||
|
||||
Reference in New Issue
Block a user