mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-17 05:40:42 -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:
@@ -1,7 +1,8 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { Loader2, Mic } from 'lucide-react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import * as z from 'zod';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
@@ -24,10 +25,10 @@ import {
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { useToast } from '@/components/ui/use-toast';
|
||||
import { useGeneration } from '@/lib/hooks/useGeneration';
|
||||
import { useProfiles } from '@/lib/hooks/useProfiles';
|
||||
import { useProfile } from '@/lib/hooks/useProfiles';
|
||||
import { useUIStore } from '@/stores/uiStore';
|
||||
|
||||
const generationSchema = z.object({
|
||||
profileId: z.string().min(1, 'Please select a voice profile'),
|
||||
text: z.string().min(1, 'Text is required').max(5000),
|
||||
language: z.enum(['en', 'zh']),
|
||||
seed: z.number().int().optional(),
|
||||
@@ -37,14 +38,14 @@ const generationSchema = z.object({
|
||||
type GenerationFormValues = z.infer<typeof generationSchema>;
|
||||
|
||||
export function GenerationForm() {
|
||||
const { data: profiles } = useProfiles();
|
||||
const selectedProfileId = useUIStore((state) => state.selectedProfileId);
|
||||
const { data: selectedProfile } = useProfile(selectedProfileId || '');
|
||||
const generation = useGeneration();
|
||||
const { toast } = useToast();
|
||||
|
||||
const form = useForm<GenerationFormValues>({
|
||||
resolver: zodResolver(generationSchema),
|
||||
defaultValues: {
|
||||
profileId: '',
|
||||
text: '',
|
||||
language: 'en',
|
||||
seed: undefined,
|
||||
@@ -53,9 +54,18 @@ export function GenerationForm() {
|
||||
});
|
||||
|
||||
async function onSubmit(data: GenerationFormValues) {
|
||||
if (!selectedProfileId) {
|
||||
toast({
|
||||
title: 'No profile selected',
|
||||
description: 'Please select a voice profile from the cards above.',
|
||||
variant: 'destructive',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await generation.mutateAsync({
|
||||
profile_id: data.profileId,
|
||||
profile_id: selectedProfileId,
|
||||
text: data.text,
|
||||
language: data.language,
|
||||
seed: data.seed,
|
||||
@@ -85,30 +95,20 @@ export function GenerationForm() {
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="profileId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Voice Profile</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a voice" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{profiles?.map((profile) => (
|
||||
<SelectItem key={profile.id} value={profile.id}>
|
||||
{profile.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
<div>
|
||||
<FormLabel>Voice Profile</FormLabel>
|
||||
{selectedProfile ? (
|
||||
<div className="mt-2 p-3 border rounded-md bg-muted/50 flex items-center gap-2">
|
||||
<Mic className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="font-medium">{selectedProfile.name}</span>
|
||||
<Badge variant="outline">{selectedProfile.language}</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-2 p-3 border border-dashed rounded-md text-sm text-muted-foreground">
|
||||
Click on a profile card above to select a voice profile
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -198,7 +198,11 @@ export function GenerationForm() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="w-full" disabled={generation.isPending}>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={generation.isPending || !selectedProfileId}
|
||||
>
|
||||
{generation.isPending ? (
|
||||
<>
|
||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||
|
||||
Reference in New Issue
Block a user