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 && (
<>
|
|---|