mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-18 22:30:40 -07:00
Refactor App layout and enhance UI components for better usability
- Changed the default active tab in the App component from 'profiles' to 'main'. - Improved the layout of the main content area to better accommodate different views, including profiles, generation forms, and history. - Updated Sidebar component to reflect the new tab structure with a 'main' tab. - Enhanced the GenerationForm to utilize the selected profile from the UI store, improving user feedback when no profile is selected. - Added a new CircleButton component for better icon button interactions. - Adjusted styles in various components for improved responsiveness and visual consistency.
This commit is contained in:
@@ -10,7 +10,6 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { apiClient } from '@/lib/api/client';
|
||||
import { useDeleteGeneration, useHistory } from '@/lib/hooks/useHistory';
|
||||
import { formatDate, formatDuration } from '@/lib/utils/format';
|
||||
@@ -19,7 +18,6 @@ import { usePlayerStore } from '@/stores/playerStore';
|
||||
export function HistoryTable() {
|
||||
const [page, setPage] = useState(0);
|
||||
const limit = 20;
|
||||
const { toast } = useToast();
|
||||
|
||||
const { data: historyData, isLoading } = useHistory({
|
||||
limit,
|
||||
@@ -61,74 +59,76 @@ export function HistoryTable() {
|
||||
const hasMore = history.length === limit && (page + 1) * limit < total;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-2xl font-bold">Generation History</h2>
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
<h2 className="text-2xl font-bold mb-4 shrink-0">Generation History</h2>
|
||||
|
||||
{history.length === 0 ? (
|
||||
<div className="text-center py-12 text-muted-foreground">
|
||||
<div className="text-center py-12 text-muted-foreground flex-1 flex items-center justify-center">
|
||||
No generation history yet. Generate your first audio to see it here.
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>Text</TableHead>
|
||||
<TableHead>Profile</TableHead>
|
||||
<TableHead>Language</TableHead>
|
||||
<TableHead>Duration</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{history.map((gen) => (
|
||||
<TableRow key={gen.id}>
|
||||
<TableCell className="max-w-[300px] truncate">{gen.text}</TableCell>
|
||||
<TableCell>{gen.profile_name}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{gen.language}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>{formatDuration(gen.duration)}</TableCell>
|
||||
<TableCell>{formatDate(gen.created_at)}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handlePlay(gen.id, gen.text)}
|
||||
aria-label="Play audio"
|
||||
className={
|
||||
currentAudioId === gen.id && isPlaying ? 'text-primary' : ''
|
||||
}
|
||||
>
|
||||
<Play className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleDownload(gen.id, gen.text)}
|
||||
aria-label="Download audio"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => deleteGeneration.mutate(gen.id)}
|
||||
disabled={deleteGeneration.isPending}
|
||||
aria-label="Delete generation"
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div className="flex-1 min-h-0 overflow-y-auto border rounded-md max-h-[calc(100vh-280px)]">
|
||||
<Table>
|
||||
<TableHeader className="sticky top-0 bg-background z-10">
|
||||
<TableRow>
|
||||
<TableHead>Text</TableHead>
|
||||
<TableHead>Profile</TableHead>
|
||||
<TableHead>Language</TableHead>
|
||||
<TableHead>Duration</TableHead>
|
||||
<TableHead>Created</TableHead>
|
||||
<TableHead className="text-right">Actions</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{history.map((gen) => (
|
||||
<TableRow key={gen.id}>
|
||||
<TableCell className="max-w-[200px] truncate">{gen.text}</TableCell>
|
||||
<TableCell>{gen.profile_name}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{gen.language}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>{formatDuration(gen.duration)}</TableCell>
|
||||
<TableCell className="text-sm">{formatDate(gen.created_at)}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handlePlay(gen.id, gen.text)}
|
||||
aria-label="Play audio"
|
||||
className={
|
||||
currentAudioId === gen.id && isPlaying ? 'text-primary' : ''
|
||||
}
|
||||
>
|
||||
<Play className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleDownload(gen.id, gen.text)}
|
||||
aria-label="Download audio"
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => deleteGeneration.mutate(gen.id)}
|
||||
disabled={deleteGeneration.isPending}
|
||||
aria-label="Delete generation"
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between">
|
||||
<div className="flex justify-between items-center mt-4 shrink-0">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setPage((p) => Math.max(0, p - 1))}
|
||||
@@ -136,7 +136,7 @@ export function HistoryTable() {
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<div className="text-sm text-muted-foreground flex items-center">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Page {page + 1} • {total} total
|
||||
</div>
|
||||
<Button variant="outline" onClick={() => setPage((p) => p + 1)} disabled={!hasMore}>
|
||||
|
||||
Reference in New Issue
Block a user