feat: Add batch genre assignment functionality to song uploads, including UI for selection and post-upload API calls.

This commit is contained in:
Hördle Bot
2025-11-25 09:34:55 +01:00
parent eb3d2c86d7
commit 5102ca86cb

View File

@@ -106,6 +106,9 @@ export default function AdminPage() {
const [uploadGenreIds, setUploadGenreIds] = useState<number[]>([]); const [uploadGenreIds, setUploadGenreIds] = useState<number[]>([]);
const [uploadExcludeFromGlobal, setUploadExcludeFromGlobal] = useState(false); const [uploadExcludeFromGlobal, setUploadExcludeFromGlobal] = useState(false);
// Batch upload genre selection
const [batchUploadGenreIds, setBatchUploadGenreIds] = useState<number[]>([]);
// AI Categorization state // AI Categorization state
const [isCategorizing, setIsCategorizing] = useState(false); const [isCategorizing, setIsCategorizing] = useState(false);
const [categorizationResults, setCategorizationResults] = useState<any>(null); const [categorizationResults, setCategorizationResults] = useState<any>(null);
@@ -548,6 +551,28 @@ export default function AdminPage() {
setUploadResults(results); setUploadResults(results);
setFiles([]); setFiles([]);
setIsUploading(false); setIsUploading(false);
// Assign genres to successfully uploaded songs
if (batchUploadGenreIds.length > 0) {
const successfulUploads = results.filter(r => r.success && r.song);
for (const result of successfulUploads) {
try {
await fetch('/api/songs', {
method: 'PUT',
headers: getAuthHeaders(),
body: JSON.stringify({
id: result.song.id,
title: result.song.title,
artist: result.song.artist,
genreIds: batchUploadGenreIds
}),
});
} catch (error) {
console.error(`Failed to assign genres to ${result.song.title}:`, error);
}
}
}
fetchSongs(); fetchSongs();
fetchGenres(); fetchGenres();
fetchSpecials(); // Update special counts fetchSpecials(); // Update special counts
@@ -564,6 +589,13 @@ export default function AdminPage() {
if (failedCount > 0) { if (failedCount > 0) {
msg += `\n❌ ${failedCount} failed`; msg += `\n❌ ${failedCount} failed`;
} }
if (batchUploadGenreIds.length > 0) {
const selectedGenreNames = genres
.filter(g => batchUploadGenreIds.includes(g.id))
.map(g => g.name)
.join(', ');
msg += `\n🏷 Assigned genres: ${selectedGenreNames}`;
}
msg += '\n\n🤖 Starting auto-categorization...'; msg += '\n\n🤖 Starting auto-categorization...';
setMessage(msg); setMessage(msg);
// Small delay to let user see the message // Small delay to let user see the message
@@ -1219,6 +1251,48 @@ export default function AdminPage() {
</div> </div>
)} )}
<div style={{ marginBottom: '1rem' }}>
<label style={{ fontWeight: '500', display: 'block', marginBottom: '0.5rem' }}>
Assign Genres (optional)
</label>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem' }}>
{genres.map(genre => (
<label
key={genre.id}
style={{
display: 'flex',
alignItems: 'center',
gap: '0.25rem',
padding: '0.25rem 0.5rem',
background: batchUploadGenreIds.includes(genre.id) ? '#dbeafe' : '#f3f4f6',
border: batchUploadGenreIds.includes(genre.id) ? '2px solid #3b82f6' : '2px solid transparent',
borderRadius: '0.25rem',
cursor: 'pointer',
fontSize: '0.875rem',
transition: 'all 0.2s'
}}
>
<input
type="checkbox"
checked={batchUploadGenreIds.includes(genre.id)}
onChange={e => {
if (e.target.checked) {
setBatchUploadGenreIds([...batchUploadGenreIds, genre.id]);
} else {
setBatchUploadGenreIds(batchUploadGenreIds.filter(id => id !== genre.id));
}
}}
style={{ margin: 0 }}
/>
{genre.name}
</label>
))}
</div>
<p style={{ fontSize: '0.875rem', color: '#666', marginTop: '0.25rem' }}>
Selected genres will be assigned to all uploaded songs.
</p>
</div>
<div style={{ marginBottom: '1rem' }}> <div style={{ marginBottom: '1rem' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}> <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer' }}>
<input <input