Refactor and enhance UI components across multiple pages
Some checks failed
Frontend CI / lint (push) Failing after 19s
Frontend CI / build (push) Has been skipped

- Improved import organization and formatting in PlaylistsPage, RegisterPage, SoundsPage, SettingsPage, and UsersPage for better readability.
- Added error handling and user feedback with toast notifications in SoundsPage and SettingsPage.
- Enhanced user experience by implementing debounced search functionality in PlaylistsPage and SoundsPage.
- Updated the layout and structure of forms in SettingsPage and UsersPage for better usability.
- Improved accessibility and semantics by ensuring proper labeling and descriptions in forms.
- Fixed minor bugs related to state management and API calls in various components.
This commit is contained in:
JSC
2025-08-14 23:51:47 +02:00
parent 8358aa16aa
commit 4e50e7e79d
53 changed files with 2477 additions and 1520 deletions

View File

@@ -1,41 +1,56 @@
import { useState } from 'react'
import { AppLayout } from '@/components/AppLayout'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Checkbox } from '@/components/ui/checkbox'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { toast } from 'sonner'
import {
Scan,
Volume2,
Settings as SettingsIcon,
Loader2,
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import {
type NormalizationResponse,
type ScanResponse,
adminService,
} from '@/lib/api/services/admin'
import {
AudioWaveform,
FolderSync,
AudioWaveform
Loader2,
Scan,
Settings as SettingsIcon,
Volume2,
} from 'lucide-react'
import { adminService, type ScanResponse, type NormalizationResponse } from '@/lib/api/services/admin'
import { useState } from 'react'
import { toast } from 'sonner'
export function SettingsPage() {
// Sound scanning state
const [scanningInProgress, setScanningInProgress] = useState(false)
const [lastScanResults, setLastScanResults] = useState<ScanResponse | null>(null)
const [lastScanResults, setLastScanResults] = useState<ScanResponse | null>(
null,
)
// Sound normalization state
const [normalizationInProgress, setNormalizationInProgress] = useState(false)
const [normalizationOptions, setNormalizationOptions] = useState({
force: false,
onePass: false,
soundType: 'all' as 'all' | 'SDB' | 'TTS' | 'EXT'
soundType: 'all' as 'all' | 'SDB' | 'TTS' | 'EXT',
})
const [lastNormalizationResults, setLastNormalizationResults] = useState<NormalizationResponse | null>(null)
const [lastNormalizationResults, setLastNormalizationResults] =
useState<NormalizationResponse | null>(null)
const handleScanSounds = async () => {
setScanningInProgress(true)
try {
const response = await adminService.scanSounds()
setLastScanResults(response)
toast.success(`Sound scan completed! Added: ${response.results.added}, Updated: ${response.results.updated}, Deleted: ${response.results.deleted}`)
toast.success(
`Sound scan completed! Added: ${response.results.added}, Updated: ${response.results.updated}, Deleted: ${response.results.deleted}`,
)
} catch (error) {
toast.error('Failed to scan sounds')
console.error('Sound scan error:', error)
@@ -48,22 +63,24 @@ export function SettingsPage() {
setNormalizationInProgress(true)
try {
let response: NormalizationResponse
if (normalizationOptions.soundType === 'all') {
response = await adminService.normalizeAllSounds(
normalizationOptions.force,
normalizationOptions.onePass
normalizationOptions.onePass,
)
} else {
response = await adminService.normalizeSoundsByType(
normalizationOptions.soundType,
normalizationOptions.force,
normalizationOptions.onePass
normalizationOptions.onePass,
)
}
setLastNormalizationResults(response)
toast.success(`Sound normalization completed! Processed: ${response.results.processed}, Normalized: ${response.results.normalized}`)
toast.success(
`Sound normalization completed! Processed: ${response.results.processed}, Normalized: ${response.results.normalized}`,
)
} catch (error) {
toast.error('Failed to normalize sounds')
console.error('Sound normalization error:', error)
@@ -73,13 +90,13 @@ export function SettingsPage() {
}
return (
<AppLayout
<AppLayout
breadcrumb={{
items: [
{ label: 'Dashboard', href: '/' },
{ label: 'Admin' },
{ label: 'Settings' }
]
{ label: 'Settings' },
],
}}
>
<div className="flex-1 rounded-xl bg-muted/50 p-4">
@@ -104,11 +121,12 @@ export function SettingsPage() {
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
Scan the sound directories to synchronize new, updated, and deleted audio files with the database.
Scan the sound directories to synchronize new, updated, and
deleted audio files with the database.
</p>
<Button
onClick={handleScanSounds}
<Button
onClick={handleScanSounds}
disabled={scanningInProgress}
className="w-full"
>
@@ -134,7 +152,9 @@ export function SettingsPage() {
<div>🗑 Deleted: {lastScanResults.results.deleted}</div>
<div> Skipped: {lastScanResults.results.skipped}</div>
{lastScanResults.results.errors.length > 0 && (
<div> Errors: {lastScanResults.results.errors.length}</div>
<div>
Errors: {lastScanResults.results.errors.length}
</div>
)}
</div>
</div>
@@ -152,16 +172,20 @@ export function SettingsPage() {
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">
Normalize audio levels across all sounds using FFmpeg's loudnorm filter for consistent volume.
Normalize audio levels across all sounds using FFmpeg's loudnorm
filter for consistent volume.
</p>
<div className="space-y-3">
<div className="space-y-2">
<Label>Sound Type</Label>
<Select
value={normalizationOptions.soundType}
onValueChange={(value: 'all' | 'SDB' | 'TTS' | 'EXT') =>
setNormalizationOptions(prev => ({ ...prev, soundType: value }))
<Select
value={normalizationOptions.soundType}
onValueChange={(value: 'all' | 'SDB' | 'TTS' | 'EXT') =>
setNormalizationOptions(prev => ({
...prev,
soundType: value,
}))
}
>
<SelectTrigger>
@@ -177,11 +201,14 @@ export function SettingsPage() {
</div>
<div className="flex items-center space-x-2">
<Checkbox
<Checkbox
id="force-normalize"
checked={normalizationOptions.force}
onCheckedChange={(checked) =>
setNormalizationOptions(prev => ({ ...prev, force: !!checked }))
onCheckedChange={checked =>
setNormalizationOptions(prev => ({
...prev,
force: !!checked,
}))
}
/>
<Label htmlFor="force-normalize" className="text-sm">
@@ -190,11 +217,14 @@ export function SettingsPage() {
</div>
<div className="flex items-center space-x-2">
<Checkbox
<Checkbox
id="one-pass"
checked={normalizationOptions.onePass}
onCheckedChange={(checked) =>
setNormalizationOptions(prev => ({ ...prev, onePass: !!checked }))
onCheckedChange={checked =>
setNormalizationOptions(prev => ({
...prev,
onePass: !!checked,
}))
}
/>
<Label htmlFor="one-pass" className="text-sm">
@@ -203,8 +233,8 @@ export function SettingsPage() {
</div>
</div>
<Button
onClick={handleNormalizeSounds}
<Button
onClick={handleNormalizeSounds}
disabled={normalizationInProgress}
className="w-full"
>
@@ -223,19 +253,35 @@ export function SettingsPage() {
{lastNormalizationResults && (
<div className="bg-muted rounded-lg p-3 space-y-2">
<div className="text-sm font-medium">Last Normalization Results:</div>
<div className="text-sm font-medium">
Last Normalization Results:
</div>
<div className="text-xs text-muted-foreground space-y-1">
<div>🔄 Processed: {lastNormalizationResults.results.processed}</div>
<div> Normalized: {lastNormalizationResults.results.normalized}</div>
<div> Skipped: {lastNormalizationResults.results.skipped}</div>
<div> Errors: {lastNormalizationResults.results.errors}</div>
{lastNormalizationResults.results.error_details.length > 0 && (
<div>
🔄 Processed: {lastNormalizationResults.results.processed}
</div>
<div>
✅ Normalized:{' '}
{lastNormalizationResults.results.normalized}
</div>
<div>
Skipped: {lastNormalizationResults.results.skipped}
</div>
<div>
Errors: {lastNormalizationResults.results.errors}
</div>
{lastNormalizationResults.results.error_details.length >
0 && (
<details className="mt-2">
<summary className="cursor-pointer text-red-600">View Error Details</summary>
<summary className="cursor-pointer text-red-600">
View Error Details
</summary>
<div className="mt-1 text-xs text-red-600 space-y-1">
{lastNormalizationResults.results.error_details.map((error, index) => (
<div key={index}> {error}</div>
))}
{lastNormalizationResults.results.error_details.map(
(error, index) => (
<div key={index}> {error}</div>
),
)}
</div>
</details>
)}
@@ -248,4 +294,4 @@ export function SettingsPage() {
</div>
</AppLayout>
)
}
}