diff --git a/app/admin/page.tsx b/app/admin/page.tsx index 685e6bc..563a9a9 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -11,7 +11,7 @@ interface Song { activations: number; } -type SortField = 'title' | 'artist' | 'createdAt'; +type SortField = 'id' | 'title' | 'artist' | 'createdAt'; type SortDirection = 'asc' | 'desc'; export default function AdminPage() { @@ -210,8 +210,14 @@ export default function AdminPage() { ); const sortedSongs = [...filteredSongs].sort((a, b) => { - const valA = a[sortField].toLowerCase(); - const valB = b[sortField].toLowerCase(); + // Handle numeric sorting for ID + if (sortField === 'id') { + return sortDirection === 'asc' ? a.id - b.id : b.id - a.id; + } + + // String sorting for other fields + const valA = String(a[sortField]).toLowerCase(); + const valB = String(b[sortField]).toLowerCase(); if (valA < valB) return sortDirection === 'asc' ? -1 : 1; if (valA > valB) return sortDirection === 'asc' ? 1 : -1; @@ -302,7 +308,12 @@ export default function AdminPage() { - +
ID handleSort('id')} + > + ID {sortField === 'id' && (sortDirection === 'asc' ? '↑' : '↓')} + handleSort('title')} diff --git a/app/api/songs/route.ts b/app/api/songs/route.ts index 3aae87e..a5ca747 100644 --- a/app/api/songs/route.ts +++ b/app/api/songs/route.ts @@ -103,7 +103,19 @@ export async function POST(request: Request) { if (!title) title = 'Unknown Title'; if (!artist) artist = 'Unknown Artist'; - const filename = `${Date.now()}-${file.name.replace(/[^a-zA-Z0-9.]/g, '_')}`; + // Create URL-safe filename + const originalName = file.name.replace(/\.mp3$/i, ''); + const sanitizedName = originalName + .replace(/[^a-zA-Z0-9]/g, '-') // Replace special chars with dash + .replace(/-+/g, '-') // Replace multiple dashes with single dash + .replace(/^-|-$/g, ''); // Remove leading/trailing dashes + + // Warn if filename was changed + if (originalName !== sanitizedName) { + validationInfo.warnings.push(`Filename sanitized: "${originalName}" → "${sanitizedName}"`); + } + + const filename = `${Date.now()}-${sanitizedName}.mp3`; const uploadDir = path.join(process.cwd(), 'public/uploads'); await writeFile(path.join(uploadDir, filename), buffer); diff --git a/app/globals.css b/app/globals.css index 402347c..1729812 100644 --- a/app/globals.css +++ b/app/globals.css @@ -194,17 +194,30 @@ body { } .skip-button { - background: none; + width: 100%; + padding: 1rem 1.5rem; + margin-top: 1rem; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; border: none; - color: #666; - text-decoration: underline; - font-size: 0.875rem; - margin-top: 0.5rem; + border-radius: 0.5rem; + font-size: 1rem; + font-weight: 600; cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); + text-transform: uppercase; + letter-spacing: 0.5px; } .skip-button:hover { - color: #000; + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); +} + +.skip-button:active { + transform: translateY(0); + box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4); } /* Messages */ diff --git a/components/Game.tsx b/components/Game.tsx index 66fb594..ae0059e 100644 --- a/components/Game.tsx +++ b/components/Game.tsx @@ -136,12 +136,28 @@ export default function Game({ dailyPuzzle }: GameProps) { {!hasWon && !hasLost && ( <> - + {gameState.guesses.length < 6 ? ( + + ) : ( + + )} )}