Refactor and enhance UI components across multiple pages
- 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:
@@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user