From 6d21444746912fd69187f57dd09f82494b2866fe Mon Sep 17 00:00:00 2001 From: Eduardo Figueroa Date: Fri, 31 Oct 2025 10:10:47 -0700 Subject: [PATCH] Favorites feature --- static/easter-eggs.js | 211 ++++++++++++++++++ static/favorites.js | 304 +++++++++++++++++++++++++ static/style.css | 471 +++++++++++++++++++++++++++++++++++---- templates/base.html | 105 ++++++--- templates/dashboard.html | 8 + 5 files changed, 1024 insertions(+), 75 deletions(-) create mode 100644 static/easter-eggs.js create mode 100644 static/favorites.js diff --git a/static/easter-eggs.js b/static/easter-eggs.js new file mode 100644 index 0000000..abe634b --- /dev/null +++ b/static/easter-eggs.js @@ -0,0 +1,211 @@ +// Easter Eggs for RosterHash +// Date-based theme modifications + +class EasterEggs { + constructor() { + this.today = new Date(); + this.month = this.today.getMonth() + 1; // 1-12 + this.day = this.today.getDate(); + } + + // Check if today is Thanksgiving (4th Thursday of November) + isThanksgiving() { + if (this.month !== 11) return false; // Not November + + const year = this.today.getFullYear(); + let thursdayCount = 0; + + // Count Thursdays in November + for (let day = 1; day <= 30; day++) { + const date = new Date(year, 10, day); // Month 10 = November + if (date.getDay() === 4) { // Thursday + thursdayCount++; + if (thursdayCount === 4 && day === this.day) { + return true; + } + } + } + return false; + } + + // Check if today is April Fools' Day + isAprilFools() { + return this.month === 4 && this.day === 1; + } + + // Get April Fools team name + getAprilFoolsTeamName(abbrev) { + const jokeNames = { + 'SF': '40-Whiners', + 'LAR': 'Lambs', + 'LAC': '🔋 Low Battery', + 'KC': 'Chefs', + 'BUF': 'Buffalo Wild Wings', + 'NE': 'Cheaters', + 'NYJ': 'Just End The Season', + 'NYG': 'Littles', + 'DAL': 'America\'s Team (lol)', + 'PHI': 'Iggles', + 'WAS': 'Commies', + 'GB': 'Cheese Heads', + 'CHI': 'Da Burrs', + 'MIN': 'Purple People Eaters', + 'DET': 'Kitties', + 'TB': 'Tompa Bay', + 'NO': 'Ain\'ts', + 'ATL': 'Dirty Birds', + 'CAR': 'Kittens', + 'ARI': 'Birdinals', + 'SEA': 'Rain City Bird Brains', + 'PIT': 'Stealers', + 'BAL': 'Purple Birds', + 'CLE': 'Factory of Sadness', + 'CIN': 'Bungles', + 'HOU': 'Texas Cows', + 'TEN': 'Tacks', + 'IND': 'Dolts', + 'JAX': 'Jag-wires', + 'LV': 'Traitors', + 'DEN': 'Neigh-bors', + 'MIA': 'Dolphins (but warmer)', + }; + return jokeNames[abbrev] || abbrev; + } + + // Apply Easter eggs + apply() { + // Thanksgiving: Replace stars with turkey emoji + if (this.isThanksgiving()) { + console.log('🦃 Happy Thanksgiving!'); + this.applyThanksgiving(); + } + + // April Fools: Replace team names with joke versions + if (this.isAprilFools()) { + console.log('🤡 April Fools!'); + this.applyAprilFools(); + } + } + + // Apply Thanksgiving theme + applyThanksgiving() { + // Replace star emoji in Favorite Teams sidebar section + const sidebarSummary = document.querySelector('.sidebar-details summary'); + if (sidebarSummary && sidebarSummary.textContent.includes('⭐')) { + sidebarSummary.innerHTML = sidebarSummary.innerHTML.replace('⭐', '🦃'); + } + + // Replace star in main Favorite Teams section + const mainFavoritesTitle = document.querySelector('.favorites-section-main h2'); + if (mainFavoritesTitle && mainFavoritesTitle.textContent.includes('⭐')) { + mainFavoritesTitle.innerHTML = mainFavoritesTitle.innerHTML.replace('⭐', '🦃'); + } + + // Add turkey emoji after "Week X Games" + const scheduleTitle = document.querySelector('.schedule-section h2'); + if (scheduleTitle) { + const text = scheduleTitle.innerHTML; + if (!text.includes('🦃')) { + scheduleTitle.innerHTML = text.replace(/Week (\d+) Games/, 'Week $1 Games 🦃'); + } + } + } + + // Apply April Fools theme + applyAprilFools() { + // Store reference to this for use in setTimeout + const self = this; + + // Wait for favorites to be rendered, then replace team names + setTimeout(() => { + // Replace team names in favorite team rows (main dashboard) + document.querySelectorAll('.favorite-team-name').forEach(el => { + const originalName = el.textContent.trim(); + // Try to find the abbreviation from TEAM_NAMES + let abbrev = null; + for (const [key, value] of Object.entries(TEAM_NAMES)) { + if (value === originalName) { + abbrev = key; + break; + } + } + if (abbrev) { + el.textContent = self.getAprilFoolsTeamName(abbrev); + el.style.fontFamily = '"Comic Sans MS", "Comic Sans", cursive'; + } + }); + + // Replace opponent names + document.querySelectorAll('.favorite-opponent').forEach(el => { + const text = el.textContent.trim(); + const match = text.match(/^(vs|@) (.+)$/); + if (match) { + const prefix = match[1]; + const opponentName = match[2]; + + // Find abbreviation + let abbrev = null; + for (const [key, value] of Object.entries(TEAM_NAMES)) { + if (value === opponentName) { + abbrev = key; + break; + } + } + if (abbrev) { + el.textContent = `${prefix} ${self.getAprilFoolsTeamName(abbrev)}`; + el.style.fontFamily = '"Comic Sans MS", "Comic Sans", cursive'; + } + } + }); + + // Replace team names in sidebar favorites list + document.querySelectorAll('.favorite-game-card .favorite-matchup').forEach(el => { + const originalName = el.textContent.trim(); + let abbrev = null; + for (const [key, value] of Object.entries(TEAM_NAMES)) { + if (value === originalName) { + abbrev = key; + break; + } + } + if (abbrev) { + el.textContent = self.getAprilFoolsTeamName(abbrev); + el.style.fontFamily = '"Comic Sans MS", "Comic Sans", cursive'; + } + }); + + // Add April Fools indicator + const header = document.querySelector('.dashboard-header h1'); + if (header && !header.textContent.includes('🤡')) { + header.innerHTML += ' 🤡'; + } + }, 500); // Wait for favorites to render + } +} + +// Initialize and apply Easter eggs when DOM is ready +document.addEventListener('DOMContentLoaded', function() { + const easterEggs = new EasterEggs(); + easterEggs.apply(); + + // Re-apply when favorites are updated (listen for changes) + const observer = new MutationObserver(() => { + if (easterEggs.isThanksgiving()) { + easterEggs.applyThanksgiving(); + } + if (easterEggs.isAprilFools()) { + easterEggs.applyAprilFools(); + } + }); + + // Observe favorites sections for changes + const favoritesDisplay = document.getElementById('favorites-display'); + const favoritesList = document.getElementById('favorites-list'); + + if (favoritesDisplay) { + observer.observe(favoritesDisplay, { childList: true, subtree: true }); + } + if (favoritesList) { + observer.observe(favoritesList, { childList: true, subtree: true }); + } +}); diff --git a/static/favorites.js b/static/favorites.js new file mode 100644 index 0000000..eb1b6ce --- /dev/null +++ b/static/favorites.js @@ -0,0 +1,304 @@ +// Favorites Management for RosterHash +// Manages up to 4 favorite NFL teams with localStorage persistence (persists across weeks) + +const MAX_FAVORITES = 4; + +// Team abbreviation to common name mapping +const TEAM_NAMES = { + 'ARI': 'Cardinals', + 'ATL': 'Falcons', + 'BAL': 'Ravens', + 'BUF': 'Bills', + 'CAR': 'Panthers', + 'CHI': 'Bears', + 'CIN': 'Bengals', + 'CLE': 'Browns', + 'DAL': 'Cowboys', + 'DEN': 'Broncos', + 'DET': 'Lions', + 'GB': 'Packers', + 'HOU': 'Texans', + 'IND': 'Colts', + 'JAX': 'Jaguars', + 'KC': 'Chiefs', + 'LV': 'Raiders', + 'LAC': 'Chargers', + 'LAR': 'Rams', + 'MIA': 'Dolphins', + 'MIN': 'Vikings', + 'NE': 'Patriots', + 'NO': 'Saints', + 'NYG': 'Giants', + 'NYJ': 'Jets', + 'PHI': 'Eagles', + 'PIT': 'Steelers', + 'SF': '49ers', + 'SEA': 'Seahawks', + 'TB': 'Buccaneers', + 'TEN': 'Titans', + 'WAS': 'Commanders' +}; + +class FavoritesManager { + constructor() { + this.favorites = this.loadFavorites(); + this.games = []; + this.allTeams = new Set(); + this.init(); + } + + // Get team common name + getTeamName(abbrev) { + return TEAM_NAMES[abbrev] || abbrev; + } + + // Get storage key (no week suffix - persists across weeks) + getStorageKey() { + return `rosterhash_favorite_teams`; + } + + // Load favorites from localStorage + loadFavorites() { + try { + const stored = localStorage.getItem(this.getStorageKey()); + return stored ? JSON.parse(stored) : []; + } catch (e) { + console.error('Failed to load favorites:', e); + return []; + } + } + + // Save favorites to localStorage + saveFavorites() { + try { + localStorage.setItem(this.getStorageKey(), JSON.stringify(this.favorites)); + } catch (e) { + console.error('Failed to save favorites:', e); + } + } + + // Initialize favorites system + init() { + // Wait for DOM to be ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => this.setup()); + } else { + this.setup(); + } + } + + // Setup favorites after DOM is loaded + setup() { + this.extractGamesFromPage(); + this.extractAllTeams(); + this.populateTeamsPicker(); + this.renderFavoritesList(); + this.renderFavoritesDisplay(); + } + + // Extract all games from the schedule on the page + extractGamesFromPage() { + this.games = []; + this.allTeams = new Set(); + + // Find all game cards in the schedule + const gameCards = document.querySelectorAll('.game-card'); + + gameCards.forEach((card, index) => { + const timeElement = card.querySelector('.game-time'); + const awayTeamElement = card.querySelector('.away-team'); + const homeTeamElement = card.querySelector('.home-team'); + const awayScoreElement = card.querySelectorAll('.nfl-score')[0]; + const homeScoreElement = card.querySelectorAll('.nfl-score')[1]; + const liveIndicator = card.querySelector('.live-indicator'); + + if (awayTeamElement && homeTeamElement) { + const awayTeam = awayTeamElement.textContent.trim(); + const homeTeam = homeTeamElement.textContent.trim(); + + // Add teams to the set + this.allTeams.add(awayTeam); + this.allTeams.add(homeTeam); + + const game = { + awayTeam: awayTeam, + homeTeam: homeTeam, + awayScore: awayScoreElement ? awayScoreElement.textContent.trim() : null, + homeScore: homeScoreElement ? homeScoreElement.textContent.trim() : null, + time: timeElement ? timeElement.textContent.trim() : 'TBD', + isLive: !!liveIndicator, + matchup: `${awayTeam} @ ${homeTeam}` + }; + + this.games.push(game); + } + }); + } + + // Extract all unique teams + extractAllTeams() { + // Already done in extractGamesFromPage + } + + // Populate the teams picker in the sidebar + populateTeamsPicker() { + const picker = document.getElementById('games-picker'); + if (!picker) return; + + if (this.allTeams.size === 0) { + picker.innerHTML = '

No teams available

'; + return; + } + + picker.innerHTML = ''; + + // Sort teams alphabetically by common name + const sortedTeams = Array.from(this.allTeams).sort((a, b) => { + const nameA = this.getTeamName(a); + const nameB = this.getTeamName(b); + return nameA.localeCompare(nameB); + }); + + sortedTeams.forEach(team => { + const isSelected = this.favorites.includes(team); + const isDisabled = !isSelected && this.favorites.length >= MAX_FAVORITES; + const teamName = this.getTeamName(team); + + const item = document.createElement('div'); + item.className = 'game-picker-item'; + if (isSelected) item.classList.add('selected'); + if (isDisabled) item.classList.add('disabled'); + + item.innerHTML = ` +
${teamName}
+ `; + + if (!isDisabled || isSelected) { + item.addEventListener('click', () => this.toggleFavorite(team)); + } + + picker.appendChild(item); + }); + } + + // Toggle a team as favorite + toggleFavorite(team) { + const index = this.favorites.indexOf(team); + + if (index > -1) { + // Remove from favorites + this.favorites.splice(index, 1); + } else { + // Add to favorites if under limit + if (this.favorites.length < MAX_FAVORITES) { + this.favorites.push(team); + } + } + + this.saveFavorites(); + this.populateTeamsPicker(); + this.renderFavoritesList(); + this.renderFavoritesDisplay(); + } + + // Render favorites list in sidebar + renderFavoritesList() { + const list = document.getElementById('favorites-list'); + if (!list) return; + + if (this.favorites.length === 0) { + list.innerHTML = '

Click teams below to add favorites (max 4)

'; + return; + } + + list.innerHTML = ''; + + this.favorites.forEach(team => { + const teamName = this.getTeamName(team); + const card = document.createElement('div'); + card.className = 'favorite-game-card'; + + card.innerHTML = ` +
+
${teamName}
+ +
+ `; + + list.appendChild(card); + }); + } + + // Render favorites display on main dashboard (stacked rows like fantasy scores) + renderFavoritesDisplay() { + const section = document.getElementById('favorites-section-main'); + const display = document.getElementById('favorites-display'); + + if (!section || !display) return; + + if (this.favorites.length === 0) { + section.classList.remove('has-favorites'); + display.innerHTML = ''; + return; + } + + section.classList.add('has-favorites'); + display.innerHTML = ''; + + this.favorites.forEach(team => { + // Find the game(s) for this team + const teamGames = this.games.filter(g => g.awayTeam === team || g.homeTeam === team); + + teamGames.forEach(game => { + const row = document.createElement('div'); + row.className = 'favorite-team-row'; + + const isHome = game.homeTeam === team; + const opponent = isHome ? game.awayTeam : game.homeTeam; + const teamScore = isHome ? game.homeScore : game.awayScore; + const oppScore = isHome ? game.awayScore : game.homeScore; + + const teamName = this.getTeamName(team); + const opponentName = this.getTeamName(opponent); + + const scoreDisplay = (teamScore && oppScore) + ? `${teamScore} - ${oppScore}` + : ''; + + const liveBadge = game.isLive + ? 'LIVE' + : ''; + + const matchupDisplay = isHome ? `vs ${opponentName}` : `@ ${opponentName}`; + + row.innerHTML = ` +
+ ${teamName} + ${matchupDisplay} +
+
+ ${game.time} + ${liveBadge} + ${scoreDisplay} +
+ `; + + display.appendChild(row); + }); + }); + } + + // Refresh scores for favorite games (called by page refresh) + refreshScores() { + this.extractGamesFromPage(); + this.renderFavoritesList(); + this.renderFavoritesDisplay(); + } +} + +// Initialize favorites manager globally +let favoritesManager; + +document.addEventListener('DOMContentLoaded', function() { + favoritesManager = new FavoritesManager(); +}); diff --git a/static/style.css b/static/style.css index 7ab4e2c..69e82de 100644 --- a/static/style.css +++ b/static/style.css @@ -1,17 +1,146 @@ -.sidebar-link { - display: block; - padding: 12px 16px; - background: var(--bg-secondary); - border-radius: 8px; - color: var(--text-primary); - text-decoration: none; - margin-bottom: 10px; - transition: all 0.2s ease; +.btn-icon { + font-size: 18px; + flex-shrink: 0; } -.sidebar-link:hover { +/* Favorites Section */ +.favorites-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.favorites-empty { + color: var(--text-muted); + font-size: 13px; + text-align: center; + padding: 16px; + background: var(--bg-secondary); + border-radius: 8px; + border: 2px dashed var(--border-primary); +} + +.favorite-game-card { + background: var(--bg-secondary); + border: 2px solid var(--accent); + border-radius: 10px; + padding: 12px; + display: flex; + flex-direction: column; + gap: 6px; + transition: all 0.2s ease; + position: relative; +} + +.favorite-game-card:hover { background: var(--bg-tertiary); - transform: translateX(5px); + transform: translateY(-2px); + box-shadow: var(--shadow-sm); +} + +.favorite-game-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.favorite-matchup { + font-weight: 600; + color: var(--text-primary); + font-size: 14px; +} + +.favorite-remove { + background: transparent; + border: none; + color: var(--text-muted); + cursor: pointer; + padding: 4px; + font-size: 16px; + transition: all 0.2s ease; + line-height: 1; +} + +.favorite-remove:hover { + color: var(--error); + transform: scale(1.2); +} + +.favorite-game-info { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 12px; + color: var(--text-muted); +} + +.favorite-score { + font-weight: 600; + color: var(--accent); +} + +.favorite-live-badge { + background: var(--error); + color: white; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; + animation: pulse 2s infinite; +} + +/* Games Picker Section */ +.games-picker { + display: flex; + flex-direction: column; + gap: 6px; +} + +.game-picker-item { + background: var(--bg-secondary); + border: 2px solid var(--border-primary); + border-radius: 8px; + padding: 10px 12px; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + justify-content: space-between; + align-items: center; +} + +.game-picker-item:hover { + background: var(--bg-tertiary); + border-color: var(--accent); +} + +.game-picker-item.selected { + background: var(--accent); + border-color: var(--accent); + color: white; +} + +.game-picker-item.disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.game-picker-item.disabled:hover { + background: var(--bg-secondary); + border-color: var(--border-primary); +} + +.game-picker-matchup { + font-weight: 600; + font-size: 13px; +} + +.game-picker-time { + font-size: 11px; + color: var(--text-muted); +} + +.game-picker-item.selected .game-picker-time { + color: rgba(255, 255, 255, 0.8); } .about-text { @@ -259,6 +388,99 @@ box-shadow: var(--shadow-sm); } +/* Favorites Section Main */ +.favorites-section-main { + background: var(--bg-primary); + border-radius: 12px; + padding: 15px; + margin-bottom: 30px; + box-shadow: var(--shadow-sm); + display: none; +} + +.favorites-section-main.has-favorites { + display: block; +} + +.favorites-section-main h2 { + color: var(--accent); + margin-bottom: 15px; + font-size: 1.3rem; + font-weight: 600; +} + +.favorites-display { + display: flex; + flex-direction: column; + gap: 0; +} + +.favorite-team-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0; + border-bottom: 1px solid var(--border-light); + transition: background 0.2s ease; +} + +.favorite-team-row:hover { + background: var(--bg-secondary); +} + +.favorite-team-row:last-child { + border-bottom: none; +} + +.favorite-team-info { + display: flex; + align-items: center; + gap: 12px; + min-width: 200px; +} + +.favorite-team-name { + font-weight: 700; + color: var(--accent); + font-size: 14px; + min-width: 45px; +} + +.favorite-opponent { + color: var(--text-muted); + font-size: 13px; +} + +.favorite-team-details { + display: flex; + align-items: center; + gap: 12px; + font-size: 13px; +} + +.favorite-game-time { + color: var(--text-muted); + min-width: 100px; + text-align: right; +} + +.favorite-live-indicator { + background: var(--error); + color: white; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; + animation: pulse 2s infinite; +} + +.favorite-team-score { + font-weight: 700; + color: var(--text-primary); + min-width: 60px; + text-align: right; +} + .score-row { display: flex; justify-content: space-between; @@ -619,12 +841,46 @@ } .sidebar { - width: 85%; - right: -85%; + width: 100%; + max-width: 100%; + right: -100%; } - + + .sidebar-header { + padding: 16px 16px; + } + + .sidebar-title { + font-size: 1.3rem; + } + .sidebar-content { - padding: 50px 20px 20px; + padding: 16px 16px; + } + + .favorites-display { + /* Already stacked, no changes needed */ + } + + .favorite-team-row { + flex-direction: column; + align-items: flex-start; + gap: 6px; + padding: 12px 0; + } + + .favorite-team-details { + width: 100%; + justify-content: space-between; + } + + .favorite-game-time { + min-width: auto; + text-align: left; + } + + .favorite-team-score { + min-width: auto; } .dashboard-header { @@ -857,14 +1113,17 @@ body { .sidebar { position: fixed; top: 0; - right: -400px; - width: 400px; + right: -420px; + width: 420px; height: 100%; background: var(--sidebar-bg); box-shadow: var(--sidebar-shadow); - transition: right 0.3s ease; + transition: right 0.25s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1001; overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; } /* Show sidebar when checkbox is checked */ @@ -878,55 +1137,189 @@ body { visibility: visible; } +/* Sidebar Header */ +.sidebar-header { + position: sticky; + top: 0; + background: var(--bg-primary); + border-bottom: 2px solid var(--border-primary); + padding: 20px 24px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 10; + backdrop-filter: blur(10px); +} + +.sidebar-title { + color: var(--accent); + font-size: 1.5rem; + font-weight: 700; + margin: 0; +} + .close-btn { - position: absolute; - top: 15px; - right: 15px; - background: transparent; - border: none; - font-size: 28px; + background: var(--bg-secondary); + border: 2px solid var(--border-primary); + border-radius: 8px; + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + font-size: 20px; color: var(--text-primary); cursor: pointer; - padding: 5px; + padding: 0; + transition: all 0.2s ease; } .close-btn:hover { - opacity: 0.7; + background: var(--bg-tertiary); + transform: rotate(90deg); } .sidebar-content { - padding: 60px 30px 30px; + padding: 20px 24px; + flex: 1; + overflow-y: auto; } .sidebar-section { - margin-bottom: 35px; + margin-bottom: 28px; } .sidebar-section h3 { color: var(--accent); - margin-bottom: 15px; - font-size: 1.1rem; + margin-bottom: 12px; + font-size: 0.95rem; font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; } -.theme-button { - display: flex; - align-items: center; - gap: 10px; + +/* Sidebar Details (Collapsible) */ +.sidebar-details { background: var(--bg-secondary); border: 2px solid var(--border-primary); - border-radius: 8px; - padding: 12px 20px; + border-radius: 10px; + overflow: hidden; +} + +.sidebar-details summary { + padding: 12px 16px; + cursor: pointer; + font-weight: 600; + color: var(--text-primary); + list-style: none; + display: flex; + justify-content: space-between; + align-items: center; + transition: background 0.2s ease; +} + +.sidebar-details summary::-webkit-details-marker { + display: none; +} + +.sidebar-details summary::after { + content: '▼'; + font-size: 12px; + transition: transform 0.2s ease; +} + +.sidebar-details[open] summary::after { + transform: rotate(-180deg); +} + +.sidebar-details summary:hover { + background: var(--bg-tertiary); +} + +.sidebar-details .about-text { + padding: 12px 16px; + border-top: 1px solid var(--border-primary); +} + +.details-content { + padding: 12px 16px; + border-top: 1px solid var(--border-primary); +} + +.scrollable-content { + max-height: 300px; + overflow-y: auto; + overflow-x: hidden; +} + +/* Custom scrollbar for details content */ +.scrollable-content::-webkit-scrollbar { + width: 6px; +} + +.scrollable-content::-webkit-scrollbar-track { + background: var(--bg-tertiary); + border-radius: 3px; +} + +.scrollable-content::-webkit-scrollbar-thumb { + background: var(--accent); + border-radius: 3px; +} + +.scrollable-content::-webkit-scrollbar-thumb:hover { + background: var(--accent-hover); +} + +/* Favorites subsection */ +.favorites-subsection { + margin-top: 16px; + padding-top: 16px; + border-top: 1px solid var(--border-primary); +} + +.subsection-title { + color: var(--text-muted); + font-size: 0.85rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 12px; +} + +/* Sidebar Actions Section */ +.sidebar-actions { + display: flex; + flex-direction: column; + gap: 10px; +} + +.sidebar-actions form { + width: 100%; +} + +.sidebar-action-btn { + display: flex; + align-items: center; + gap: 12px; + background: var(--bg-secondary); + border: 2px solid var(--border-primary); + border-radius: 10px; + padding: 12px 16px; cursor: pointer; width: 100%; transition: all 0.2s ease; color: var(--text-primary); - font-size: 15px; + font-size: 14px; + font-weight: 500; + text-decoration: none; } -.theme-button:hover { +.sidebar-action-btn:hover { background: var(--bg-tertiary); - transform: translateY(-2px); + transform: translateX(-4px); + border-color: var(--accent); } /* Refresh button */ diff --git a/templates/base.html b/templates/base.html index 5a7a02f..34ffafc 100644 --- a/templates/base.html +++ b/templates/base.html @@ -21,53 +21,80 @@ @@ -186,5 +213,11 @@ }); } + + + + + + diff --git a/templates/dashboard.html b/templates/dashboard.html index 5c8d265..31bc14f 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -80,6 +80,14 @@ {% endfor %} + +
+

⭐ Favorite Teams

+
+ +
+
+

Week {{ week }} Games