190 lines
5.3 KiB
TypeScript
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>
|
|
);
|
|
}
|