veza/apps/web/src/features/playlists/components/CreatePlaylistDialog.tsx
2025-12-12 21:34:34 -05:00

190 lines
5.3 KiB
TypeScript

import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
// Ensure PlaylistError is exported from service or assume general Error if not available
import { createPlaylist } from '../services/playlistService';
import { Dialog } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Checkbox } from '@/components/ui/checkbox';
import { useToast } from '@/hooks/useToast';
import { Loader2 } from 'lucide-react';
const createPlaylistSchema = z.object({
title: z
.string()
.min(1, 'Le titre est requis')
.max(200, 'Le titre ne peut pas dépasser 200 caractères'),
description: z
.string()
.max(1000, 'La description ne peut pas dépasser 1000 caractères')
.optional(),
is_public: z.boolean().default(true),
});
type CreatePlaylistFormData = z.infer<typeof createPlaylistSchema>;
interface CreatePlaylistDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
onCreated?: () => void;
}
export function CreatePlaylistDialog({
open,
onOpenChange,
onCreated,
}: CreatePlaylistDialogProps) {
const [isSubmitting, setIsSubmitting] = useState(false);
const { success: showSuccess, error: showError } = useToast();
const {
register,
handleSubmit,
formState: { errors },
reset,
watch,
} = useForm<CreatePlaylistFormData>({
resolver: zodResolver(createPlaylistSchema),
defaultValues: {
title: '',
description: '',
is_public: true,
},
});
const isPublic = watch('is_public');
const onSubmit = async (data: CreatePlaylistFormData) => {
setIsSubmitting(true);
try {
await createPlaylist({
title: data.title,
description: data.description || undefined,
is_public: data.is_public,
});
showSuccess('Playlist créée : Votre playlist a été créée avec succès.');
reset();
onOpenChange(false);
onCreated?.();
} catch (error) {
console.error('Failed to create playlist:', error);
showError(
error instanceof Error
? error.message
: 'Impossible de créer la playlist',
);
} finally {
setIsSubmitting(false);
}
};
const handleCancel = () => {
reset();
onOpenChange(false);
};
return (
<Dialog
open={open}
onClose={handleCancel}
title="Créer une playlist"
variant="default"
aria-label="Dialogue de création de playlist"
>
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="title">
Titre{' '}
<span className="text-destructive" aria-hidden="true">
*
</span>
</Label>
<Input
id="title"
{...register('title')}
placeholder="Ma nouvelle playlist"
disabled={isSubmitting}
aria-invalid={errors.title ? 'true' : 'false'}
aria-describedby={errors.title ? 'create-title-error' : undefined}
aria-required="true"
/>
{errors.title && (
<p
id="create-title-error"
className="text-sm text-destructive"
role="alert"
>
{errors.title.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
{...register('description')}
placeholder="Décrivez votre playlist..."
rows={3}
disabled={isSubmitting}
aria-invalid={errors.description ? 'true' : 'false'}
aria-describedby={
errors.description ? 'create-description-error' : undefined
}
/>
{errors.description && (
<p
id="create-description-error"
className="text-sm text-destructive"
role="alert"
>
{errors.description.message}
</p>
)}
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="is_public"
{...register('is_public')}
checked={isPublic}
disabled={isSubmitting}
aria-checked={isPublic}
/>
<Label htmlFor="is_public" className="cursor-pointer">
Playlist publique
</Label>
</div>
<div className="flex justify-end gap-2 pt-4">
<Button
type="button"
variant="outline"
onClick={handleCancel}
disabled={isSubmitting}
aria-label="Annuler la création de playlist"
>
Annuler
</Button>
<Button
type="submit"
disabled={isSubmitting}
aria-label="Créer la playlist"
>
{isSubmitting && (
<Loader2
className="w-4 h-4 mr-2 animate-spin"
aria-hidden="true"
/>
)}
Créer
</Button>
</div>
</form>
</Dialog>
);
}