cdechoch commited on
Commit
0db6acb
·
verified ·
1 Parent(s): 54651a4

Update team.html

Browse files
Files changed (1) hide show
  1. team.html +71 -34
team.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Team - NBA Buzz</title>
7
  <style>
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; padding: 20px; color: #333; }
@@ -19,14 +19,21 @@
19
  .search-result:hover { background: #fff8f3; }
20
  .search-result .name { font-weight: 600; }
21
  .search-result .type { font-size: 12px; color: #888; }
22
- .team-header { background: white; border-radius: 12px; padding: 25px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); text-align: center; }
23
- .team-name { font-size: 32px; font-weight: bold; color: #333; margin-bottom: 15px; }
 
 
 
24
  .mention-count-header { font-size: 48px; font-weight: bold; color: #f97316; }
25
  .mention-count-label { font-size: 14px; color: #888; text-transform: uppercase; margin-bottom: 20px; }
26
- .period-stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-top: 15px; }
27
- .period-stat { background: #f8f9fa; border-radius: 8px; padding: 12px; text-align: center; }
 
 
 
28
  .period-label { font-size: 12px; color: #888; font-weight: 600; margin-bottom: 4px; }
29
  .period-mentions { font-size: 20px; font-weight: bold; color: #f97316; }
 
30
  .period-rank { font-size: 11px; color: #666; margin-top: 2px; }
31
  .section-title { font-size: 18px; font-weight: 600; color: #333; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #f97316; }
32
  .mentions-list { display: flex; flex-direction: column; gap: 15px; margin-bottom: 30px; }
@@ -39,8 +46,6 @@
39
  .author-handle { font-size: 13px; color: #888; text-decoration: none; }
40
  .author-handle:hover { color: #f97316; }
41
  .mention-time { font-size: 12px; color: #aaa; }
42
- .player-tag { display: inline-block; background: #f97316; color: white; padding: 4px 10px; border-radius: 4px; font-size: 12px; font-weight: 600; margin-bottom: 10px; text-decoration: none; }
43
- .player-tag:hover { background: #ea580c; }
44
  .mention-text { font-size: 15px; line-height: 1.6; color: #333; margin-bottom: 12px; white-space: pre-wrap; word-wrap: break-word; }
45
  .mention-text a { color: #f97316; text-decoration: none; }
46
  .mention-text a:hover { text-decoration: underline; }
@@ -54,7 +59,7 @@
54
  .quote-author { font-weight: 600; font-size: 13px; color: #666; margin-bottom: 6px; }
55
  .quote-text { font-size: 14px; line-height: 1.5; color: #555; }
56
  .related-section { background: white; border-radius: 12px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
57
- .related-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; }
58
  .related-link { display: block; padding: 12px 15px; background: #f8f9fa; border-radius: 8px; text-decoration: none; color: #333; font-weight: 500; }
59
  .related-link:hover { background: #fff8f3; color: #f97316; }
60
  .bottom-nav { text-align: center; padding: 20px; border-top: 1px solid #eee; margin-top: 20px; }
@@ -71,29 +76,55 @@
71
  <input type="text" class="search-input" id="searchInput" placeholder="Search players or teams...">
72
  <div class="search-results" id="searchResults"></div>
73
  </div>
74
- <div class="team-header">
75
- <div class="team-name" id="teamName">Loading...</div>
76
- <div class="period-stats" id="periodStats">
77
- <div class="period-stat"><div class="period-label">6h</div><div class="period-mentions">-</div><div class="period-rank"></div></div>
78
- <div class="period-stat"><div class="period-label">12h</div><div class="period-mentions">-</div><div class="period-rank"></div></div>
79
- <div class="period-stat"><div class="period-label">24h</div><div class="period-mentions">-</div><div class="period-rank"></div></div>
80
- <div class="period-stat"><div class="period-label">48h</div><div class="period-mentions">-</div><div class="period-rank"></div></div>
81
- <div class="period-stat"><div class="period-label">7d</div><div class="period-mentions">-</div><div class="period-rank"></div></div>
 
 
 
 
82
  </div>
83
  </div>
84
  <div class="section-title">Recent Mentions</div>
85
  <div class="mentions-list" id="mentionsList"><div class="loading"><div class="spinner"></div><p>Loading...</p></div></div>
86
  <div class="related-section">
87
- <div class="section-title" style="border:none;margin:0 0 15px 0;padding:0;">Roster</div>
88
- <div class="related-grid" id="rosterGrid"><div class="loading">Loading...</div></div>
89
  </div>
90
  <div class="bottom-nav"><a href="/" class="back-link">← Back to Rankings</a></div>
91
  </div>
92
  <script>
93
- const teamName = decodeURIComponent(window.location.pathname.split('/team/')[1] || '');
94
  let allPlayers = [];
95
  let allTeams = [];
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  async function loadPlayerList() {
98
  try { const res = await fetch('/api/players?hours=72&limit=200'); allPlayers = (await res.json()).map(p => p.player); } catch (e) {}
99
  }
@@ -137,15 +168,20 @@
137
  escaped = escaped.replace(/@([a-zA-Z0-9][a-zA-Z0-9._-]*(?:\.[a-zA-Z][a-zA-Z0-9._-]*)+)/g, '<a href="https://bsky.app/profile/$1" target="_blank" class="handle-link">@$1</a>');
138
  // Match player names
139
  for (const player of allPlayers) {
 
140
  const regex = new RegExp(`\\b(${player.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\b`, 'gi');
141
  escaped = escaped.replace(regex, `<a href="/player/${encodeURIComponent(player)}" class="player-link">$1</a>`);
142
  }
143
- // Match team names (skip current team to avoid linking everything)
144
  for (const team of allTeams) {
145
- if (team === teamName) continue;
146
  const regex = new RegExp(`\\b(${team.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\b`, 'gi');
147
  escaped = escaped.replace(regex, `<a href="/team/${encodeURIComponent(team)}" class="team-link">$1</a>`);
148
  }
 
 
 
 
 
149
  return escaped;
150
  }
151
 
@@ -158,30 +194,32 @@
158
  return Math.floor(s/86400) + 'd ago';
159
  }
160
 
161
- async function loadTeam() {
162
- if (!teamName) { document.getElementById('teamName').textContent = 'Not found'; return; }
163
- document.getElementById('teamName').textContent = teamName;
164
- document.title = teamName + ' - NBA Buzz';
165
 
166
  try {
167
- const info = await (await fetch(`/api/team/${encodeURIComponent(teamName)}`)).json();
168
- if (info.players?.length) document.getElementById('rosterGrid').innerHTML = info.players.map(p => `<a href="/player/${encodeURIComponent(p)}" class="related-link">${p}</a>`).join('');
169
- else document.getElementById('rosterGrid').innerHTML = '<p style="color:#888">No players</p>';
 
170
 
171
  // Populate period stats
172
  if (info.period_stats) {
173
  document.getElementById('periodStats').innerHTML = info.period_stats.map(p => `
174
- <div class="period-stat">
175
  <div class="period-label">${p.label}</div>
176
  <div class="period-mentions">${p.mentions}</div>
177
- <div class="period-rank">${p.rank ? '#' + p.rank : '-'}</div>
178
- </div>
 
179
  `).join('');
180
  }
181
  } catch (e) { console.error(e); }
182
 
183
  try {
184
- const mentions = await (await fetch(`/api/team-mentions/${encodeURIComponent(teamName)}?limit=50`)).json();
185
  if (!mentions.length) { document.getElementById('mentionsList').innerHTML = '<div class="empty-state"><h3>No mentions found</h3></div>'; return; }
186
  document.getElementById('mentionsList').innerHTML = mentions.map(m => {
187
  const avatarUrl = m.author_avatar || 'https://cdn.bsky.app/img/avatar/plain/did:plc:default/default@jpeg';
@@ -207,7 +245,6 @@
207
  </div>
208
  <span class="mention-time">${timeAgo(m.created_at)}</span>
209
  </div>
210
- ${m.player ? `<a href="/player/${encodeURIComponent(m.player)}" class="player-tag">${escapeHtml(m.player)}</a>` : ''}
211
  <div class="mention-text">${processText(m.text)}</div>
212
  ${quoteHtml}
213
  <div class="mention-footer"><a href="${m.url}" target="_blank" class="view-original">View on Bluesky →</a></div>
@@ -215,7 +252,7 @@
215
  `}).join('');
216
  } catch (e) { document.getElementById('mentionsList').innerHTML = '<div class="empty-state"><h3>Error loading</h3></div>'; }
217
  }
218
- loadTeam();
219
  </script>
220
  </body>
221
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Player - NBA Buzz</title>
7
  <style>
8
  * { margin: 0; padding: 0; box-sizing: border-box; }
9
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; padding: 20px; color: #333; }
 
19
  .search-result:hover { background: #fff8f3; }
20
  .search-result .name { font-weight: 600; }
21
  .search-result .type { font-size: 12px; color: #888; }
22
+ .player-header { background: white; border-radius: 12px; padding: 25px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); text-align: center; }
23
+ .player-name { font-size: 32px; font-weight: bold; color: #333; margin-bottom: 8px; }
24
+ .player-team { font-size: 18px; color: #f97316; margin-bottom: 15px; }
25
+ .player-team a { color: #f97316; text-decoration: none; }
26
+ .player-team a:hover { text-decoration: underline; }
27
  .mention-count-header { font-size: 48px; font-weight: bold; color: #f97316; }
28
  .mention-count-label { font-size: 14px; color: #888; text-transform: uppercase; margin-bottom: 20px; }
29
+ .period-stats-section { margin-top: 20px; }
30
+ .period-stats-header { font-size: 14px; color: #666; font-weight: 600; margin-bottom: 10px; text-transform: uppercase; letter-spacing: 0.5px; }
31
+ .period-stats { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; }
32
+ .period-stat { background: #f8f9fa; border-radius: 8px; padding: 12px; text-align: center; text-decoration: none; display: block; transition: all 0.2s; border: 2px solid transparent; }
33
+ .period-stat:hover { background: #fff8f3; border-color: #f97316; }
34
  .period-label { font-size: 12px; color: #888; font-weight: 600; margin-bottom: 4px; }
35
  .period-mentions { font-size: 20px; font-weight: bold; color: #f97316; }
36
+ .period-mentions-label { font-size: 9px; color: #999; text-transform: uppercase; }
37
  .period-rank { font-size: 11px; color: #666; margin-top: 2px; }
38
  .section-title { font-size: 18px; font-weight: 600; color: #333; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid #f97316; }
39
  .mentions-list { display: flex; flex-direction: column; gap: 15px; margin-bottom: 30px; }
 
46
  .author-handle { font-size: 13px; color: #888; text-decoration: none; }
47
  .author-handle:hover { color: #f97316; }
48
  .mention-time { font-size: 12px; color: #aaa; }
 
 
49
  .mention-text { font-size: 15px; line-height: 1.6; color: #333; margin-bottom: 12px; white-space: pre-wrap; word-wrap: break-word; }
50
  .mention-text a { color: #f97316; text-decoration: none; }
51
  .mention-text a:hover { text-decoration: underline; }
 
59
  .quote-author { font-weight: 600; font-size: 13px; color: #666; margin-bottom: 6px; }
60
  .quote-text { font-size: 14px; line-height: 1.5; color: #555; }
61
  .related-section { background: white; border-radius: 12px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
62
+ .related-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
63
  .related-link { display: block; padding: 12px 15px; background: #f8f9fa; border-radius: 8px; text-decoration: none; color: #333; font-weight: 500; }
64
  .related-link:hover { background: #fff8f3; color: #f97316; }
65
  .bottom-nav { text-align: center; padding: 20px; border-top: 1px solid #eee; margin-top: 20px; }
 
76
  <input type="text" class="search-input" id="searchInput" placeholder="Search players or teams...">
77
  <div class="search-results" id="searchResults"></div>
78
  </div>
79
+ <div class="player-header">
80
+ <div class="player-name" id="playerName">Loading...</div>
81
+ <div class="player-team" id="playerTeam"></div>
82
+ <div class="period-stats-section">
83
+ <div class="period-stats-header">Mentions by Time Period</div>
84
+ <div class="period-stats" id="periodStats">
85
+ <a href="/?hours=6" class="period-stat"><div class="period-label">6h</div><div class="period-mentions">-</div><div class="period-rank"></div></a>
86
+ <a href="/?hours=12" class="period-stat"><div class="period-label">12h</div><div class="period-mentions">-</div><div class="period-rank"></div></a>
87
+ <a href="/?hours=24" class="period-stat"><div class="period-label">24h</div><div class="period-mentions">-</div><div class="period-rank"></div></a>
88
+ <a href="/?hours=48" class="period-stat"><div class="period-label">48h</div><div class="period-mentions">-</div><div class="period-rank"></div></a>
89
+ <a href="/?hours=168" class="period-stat"><div class="period-label">7d</div><div class="period-mentions">-</div><div class="period-rank"></div></a>
90
+ </div>
91
  </div>
92
  </div>
93
  <div class="section-title">Recent Mentions</div>
94
  <div class="mentions-list" id="mentionsList"><div class="loading"><div class="spinner"></div><p>Loading...</p></div></div>
95
  <div class="related-section">
96
+ <div class="section-title" style="border:none;margin:0 0 15px 0;padding:0;">Teammates</div>
97
+ <div class="related-grid" id="relatedGrid"><div class="loading">Loading...</div></div>
98
  </div>
99
  <div class="bottom-nav"><a href="/" class="back-link">← Back to Rankings</a></div>
100
  </div>
101
  <script>
102
+ const playerName = decodeURIComponent(window.location.pathname.split('/player/')[1] || '');
103
  let allPlayers = [];
104
  let allTeams = [];
105
 
106
+ // Team name aliases for matching
107
+ const TEAM_ALIASES = {
108
+ 'lakers': 'Los Angeles Lakers', 'clippers': 'Los Angeles Clippers',
109
+ 'warriors': 'Golden State Warriors', 'kings': 'Sacramento Kings',
110
+ 'suns': 'Phoenix Suns', 'mavs': 'Dallas Mavericks', 'mavericks': 'Dallas Mavericks',
111
+ 'rockets': 'Houston Rockets', 'spurs': 'San Antonio Spurs', 'san antonio': 'San Antonio Spurs',
112
+ 'grizzlies': 'Memphis Grizzlies', 'pelicans': 'New Orleans Pelicans', 'new orleans': 'New Orleans Pelicans',
113
+ 'thunder': 'Oklahoma City Thunder', 'okc': 'Oklahoma City Thunder', 'oklahoma city': 'Oklahoma City Thunder',
114
+ 'nuggets': 'Denver Nuggets', 'timberwolves': 'Minnesota Timberwolves', 'wolves': 'Minnesota Timberwolves', 'minnesota': 'Minnesota Timberwolves',
115
+ 'blazers': 'Portland Trail Blazers', 'trail blazers': 'Portland Trail Blazers', 'portland': 'Portland Trail Blazers',
116
+ 'jazz': 'Utah Jazz', 'celtics': 'Boston Celtics', 'boston': 'Boston Celtics',
117
+ 'nets': 'Brooklyn Nets', 'brooklyn': 'Brooklyn Nets', 'knicks': 'New York Knicks', 'new york': 'New York Knicks',
118
+ 'sixers': 'Philadelphia 76ers', '76ers': 'Philadelphia 76ers', 'philly': 'Philadelphia 76ers', 'philadelphia': 'Philadelphia 76ers',
119
+ 'raptors': 'Toronto Raptors', 'toronto': 'Toronto Raptors',
120
+ 'bulls': 'Chicago Bulls', 'chicago': 'Chicago Bulls', 'cavs': 'Cleveland Cavaliers', 'cavaliers': 'Cleveland Cavaliers', 'cleveland': 'Cleveland Cavaliers',
121
+ 'pistons': 'Detroit Pistons', 'detroit': 'Detroit Pistons', 'pacers': 'Indiana Pacers', 'indiana': 'Indiana Pacers',
122
+ 'bucks': 'Milwaukee Bucks', 'milwaukee': 'Milwaukee Bucks',
123
+ 'hawks': 'Atlanta Hawks', 'atlanta': 'Atlanta Hawks', 'hornets': 'Charlotte Hornets', 'charlotte': 'Charlotte Hornets',
124
+ 'heat': 'Miami Heat', 'miami': 'Miami Heat', 'magic': 'Orlando Magic', 'orlando': 'Orlando Magic',
125
+ 'wizards': 'Washington Wizards', 'washington': 'Washington Wizards'
126
+ };
127
+
128
  async function loadPlayerList() {
129
  try { const res = await fetch('/api/players?hours=72&limit=200'); allPlayers = (await res.json()).map(p => p.player); } catch (e) {}
130
  }
 
168
  escaped = escaped.replace(/@([a-zA-Z0-9][a-zA-Z0-9._-]*(?:\.[a-zA-Z][a-zA-Z0-9._-]*)+)/g, '<a href="https://bsky.app/profile/$1" target="_blank" class="handle-link">@$1</a>');
169
  // Match player names
170
  for (const player of allPlayers) {
171
+ if (player === playerName) continue;
172
  const regex = new RegExp(`\\b(${player.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\b`, 'gi');
173
  escaped = escaped.replace(regex, `<a href="/player/${encodeURIComponent(player)}" class="player-link">$1</a>`);
174
  }
175
+ // Match full team names first
176
  for (const team of allTeams) {
 
177
  const regex = new RegExp(`\\b(${team.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\b`, 'gi');
178
  escaped = escaped.replace(regex, `<a href="/team/${encodeURIComponent(team)}" class="team-link">$1</a>`);
179
  }
180
+ // Match team aliases (short names like "Lakers", "Celtics")
181
+ for (const [alias, fullName] of Object.entries(TEAM_ALIASES)) {
182
+ const regex = new RegExp(`(?<!<[^>]*)\\b(${alias.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\b(?![^<]*>)`, 'gi');
183
+ escaped = escaped.replace(regex, `<a href="/team/${encodeURIComponent(fullName)}" class="team-link">$1</a>`);
184
+ }
185
  return escaped;
186
  }
187
 
 
194
  return Math.floor(s/86400) + 'd ago';
195
  }
196
 
197
+ async function loadPlayer() {
198
+ if (!playerName) { document.getElementById('playerName').textContent = 'Not found'; return; }
199
+ document.getElementById('playerName').textContent = playerName;
200
+ document.title = playerName + ' - NBA Buzz';
201
 
202
  try {
203
+ const info = await (await fetch(`/api/player/${encodeURIComponent(playerName)}`)).json();
204
+ if (info.team) document.getElementById('playerTeam').innerHTML = `<a href="/team/${encodeURIComponent(info.team)}">${info.team}</a>`;
205
+ if (info.teammates?.length) document.getElementById('relatedGrid').innerHTML = info.teammates.map(t => `<a href="/player/${encodeURIComponent(t)}" class="related-link">${t}</a>`).join('');
206
+ else document.getElementById('relatedGrid').innerHTML = '<p style="color:#888">No teammates</p>';
207
 
208
  // Populate period stats
209
  if (info.period_stats) {
210
  document.getElementById('periodStats').innerHTML = info.period_stats.map(p => `
211
+ <a href="/?hours=${p.hours}" class="period-stat">
212
  <div class="period-label">${p.label}</div>
213
  <div class="period-mentions">${p.mentions}</div>
214
+ <div class="period-mentions-label">mentions</div>
215
+ <div class="period-rank">${p.rank ? 'Rank #' + p.rank : '-'}</div>
216
+ </a>
217
  `).join('');
218
  }
219
  } catch (e) { console.error(e); }
220
 
221
  try {
222
+ const mentions = await (await fetch(`/api/mentions/${encodeURIComponent(playerName)}?limit=50`)).json();
223
  if (!mentions.length) { document.getElementById('mentionsList').innerHTML = '<div class="empty-state"><h3>No mentions found</h3></div>'; return; }
224
  document.getElementById('mentionsList').innerHTML = mentions.map(m => {
225
  const avatarUrl = m.author_avatar || 'https://cdn.bsky.app/img/avatar/plain/did:plc:default/default@jpeg';
 
245
  </div>
246
  <span class="mention-time">${timeAgo(m.created_at)}</span>
247
  </div>
 
248
  <div class="mention-text">${processText(m.text)}</div>
249
  ${quoteHtml}
250
  <div class="mention-footer"><a href="${m.url}" target="_blank" class="view-original">View on Bluesky →</a></div>
 
252
  `}).join('');
253
  } catch (e) { document.getElementById('mentionsList').innerHTML = '<div class="empty-state"><h3>Error loading</h3></div>'; }
254
  }
255
+ loadPlayer();
256
  </script>
257
  </body>
258
  </html>