feat: migrate tag management to server actions
- Completed migration of tag management to server actions by creating `actions/tags.ts` for CRUD operations. - Removed obsolete API routes for tags (`/api/tags`, `/api/tags/[id]`) and updated related components to utilize new server actions. - Updated `TagForm` and `useTags` hook to handle tag creation, updating, and deletion through server actions, improving performance and simplifying client-side logic. - Cleaned up `tags-client.ts` by removing unused types and methods, focusing on validation only. - Marked related TODO items as complete in `TODO.md`.
This commit is contained in:
@@ -1,18 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useTransition } from 'react';
|
||||
import { Tag } from '@/lib/types';
|
||||
import { TagsClient } from '@/clients/tags-client';
|
||||
import { Modal } from '@/components/ui/Modal';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { createTag, updateTag } from '@/actions/tags';
|
||||
|
||||
interface TagFormProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: { name: string; color: string }) => Promise<void>;
|
||||
onSuccess?: () => Promise<void>; // Callback après succès pour refresh
|
||||
tag?: Tag | null; // Si fourni, mode édition
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
const PRESET_COLORS = [
|
||||
@@ -30,7 +30,8 @@ const PRESET_COLORS = [
|
||||
'#F43F5E', // Rose
|
||||
];
|
||||
|
||||
export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: TagFormProps) {
|
||||
export function TagForm({ isOpen, onClose, onSuccess, tag }: TagFormProps) {
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
color: '#3B82F6',
|
||||
@@ -66,12 +67,42 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await onSubmit(formData);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
setErrors([error instanceof Error ? error.message : 'Erreur inconnue']);
|
||||
}
|
||||
startTransition(async () => {
|
||||
try {
|
||||
let result;
|
||||
|
||||
if (tag) {
|
||||
// Mode édition
|
||||
result = await updateTag(tag.id, {
|
||||
name: formData.name,
|
||||
color: formData.color,
|
||||
isPinned: formData.isPinned
|
||||
});
|
||||
} else {
|
||||
// Mode création
|
||||
result = await createTag(formData.name, formData.color);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
onClose();
|
||||
// Reset form
|
||||
setFormData({
|
||||
name: '',
|
||||
color: TagsClient.generateRandomColor(),
|
||||
isPinned: false
|
||||
});
|
||||
setErrors([]);
|
||||
// Refresh la liste des tags
|
||||
if (onSuccess) {
|
||||
await onSuccess();
|
||||
}
|
||||
} else {
|
||||
setErrors([result.error || 'Erreur inconnue']);
|
||||
}
|
||||
} catch (error) {
|
||||
setErrors([error instanceof Error ? error.message : 'Erreur inconnue']);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleColorSelect = (color: string) => {
|
||||
@@ -102,7 +133,7 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
}}
|
||||
placeholder="Nom du tag..."
|
||||
maxLength={50}
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
className="w-full"
|
||||
/>
|
||||
</div>
|
||||
@@ -150,7 +181,7 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
type="color"
|
||||
value={formData.color}
|
||||
onChange={handleCustomColorChange}
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
className="w-12 h-8 rounded border border-slate-600 bg-slate-800 cursor-pointer disabled:cursor-not-allowed"
|
||||
/>
|
||||
<Input
|
||||
@@ -163,7 +194,7 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
}}
|
||||
placeholder="#RRGGBB"
|
||||
maxLength={7}
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
className="w-24 text-xs font-mono"
|
||||
/>
|
||||
</div>
|
||||
@@ -180,7 +211,7 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
type="checkbox"
|
||||
checked={formData.isPinned}
|
||||
onChange={(e) => setFormData(prev => ({ ...prev, isPinned: e.target.checked }))}
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
className="w-4 h-4 rounded border border-slate-600 bg-slate-800 text-purple-600 focus:ring-purple-500 focus:ring-2 disabled:cursor-not-allowed"
|
||||
/>
|
||||
<span className="text-sm text-slate-300">
|
||||
@@ -210,16 +241,16 @@ export function TagForm({ isOpen, onClose, onSubmit, tag, loading = false }: Tag
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={onClose}
|
||||
disabled={loading}
|
||||
disabled={isPending}
|
||||
>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={loading || !formData.name.trim()}
|
||||
disabled={isPending || !formData.name.trim()}
|
||||
>
|
||||
{loading ? 'Enregistrement...' : (tag ? 'Mettre à jour' : 'Créer')}
|
||||
{isPending ? 'Enregistrement...' : (tag ? 'Mettre à jour' : 'Créer')}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user