From 79f8035d18d8eaa6c1037f09e90fd2114bc4f3ff Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Sun, 14 Sep 2025 08:23:04 +0200 Subject: [PATCH] feat: add clsx and tailwind-merge dependencies, enhance Kanban components - Added `clsx` and `tailwind-merge` to `package.json` and `package-lock.json` for improved class management and utility merging. - Updated `Column` and `TaskCard` components to utilize new UI components (`Card`, `Badge`) for a more cohesive design. - Refactored styles in `Header` and Kanban components to align with the new design system. - Marked several tasks as completed in `TODO.md` reflecting progress on UI enhancements. --- TODO.md | 38 +++++++++++----- components/kanban/Column.tsx | 72 +++++++++++++++--------------- components/kanban/TaskCard.tsx | 33 +++++--------- components/ui/Badge.tsx | 44 +++++++++++++++++++ components/ui/Button.tsx | 43 ++++++++++++++++++ components/ui/Card.tsx | 80 ++++++++++++++++++++++++++++++++++ components/ui/Header.tsx | 60 ++++++++----------------- components/ui/Input.tsx | 44 +++++++++++++++++++ components/ui/Modal.tsx | 78 +++++++++++++++++++++++++++++++++ lib/utils.ts | 9 ++++ package-lock.json | 23 +++++++++- package.json | 4 +- tsconfig.json | 3 +- 13 files changed, 421 insertions(+), 110 deletions(-) create mode 100644 components/ui/Badge.tsx create mode 100644 components/ui/Button.tsx create mode 100644 components/ui/Card.tsx create mode 100644 components/ui/Input.tsx create mode 100644 components/ui/Modal.tsx create mode 100644 lib/utils.ts diff --git a/TODO.md b/TODO.md index 5e2a4d5..6618240 100644 --- a/TODO.md +++ b/TODO.md @@ -25,31 +25,47 @@ ## 🎯 Phase 2: Interface utilisateur moderne (EN COURS) ### 2.1 Système de design et composants UI -- [ ] Créer les composants UI de base (Button, Input, Card, Modal, etc.) -- [ ] Implémenter le système de design (couleurs, typographie, spacing) -- [ ] Setup Tailwind CSS avec design tokens personnalisés -- [ ] Créer une palette de couleurs moderne et accessible +- [x] Créer les composants UI de base (Button, Input, Card, Modal, Badge) +- [x] Implémenter le système de design tech dark (couleurs, typographie, spacing) +- [x] Setup Tailwind CSS avec classes utilitaires personnalisées +- [x] Créer une palette de couleurs tech/cyberpunk ### 2.2 Composants Kanban existants (à améliorer) - [x] `components/kanban/Board.tsx` - Tableau Kanban principal - [x] `components/kanban/Column.tsx` - Colonnes du Kanban - [x] `components/kanban/TaskCard.tsx` - Cartes de tâches - [x] `components/ui/Header.tsx` - Header avec statistiques -- [ ] Améliorer le design et l'UX des composants existants +- [x] Refactoriser les composants pour utiliser le nouveau système UI -### 2.3 Clients HTTP et hooks -- [ ] `clients/tasks-client.ts` - Client pour les tâches +### 2.3 Gestion des tâches (CRUD) +- [ ] Formulaire de création de tâche (Modal + Form) +- [ ] Formulaire d'édition de tâche (Modal + Form avec pré-remplissage) +- [ ] Suppression de tâche (confirmation + API call) +- [ ] Changement de statut par drag & drop ou boutons +- [ ] Validation des formulaires et gestion d'erreurs + +### 2.4 Gestion des tags +- [ ] Créer/éditer des tags avec sélecteur de couleur +- [ ] Autocomplete pour les tags existants +- [ ] Suppression de tags (avec vérification des dépendances) +- [ ] Affichage des tags avec couleurs personnalisées +- [ ] Filtrage par tags + +### 2.5 Clients HTTP et hooks +- [ ] `clients/tasks-client.ts` - Client pour les tâches (CRUD) +- [ ] `clients/tags-client.ts` - Client pour les tags - [ ] `clients/base/http-client.ts` - Client HTTP de base - [ ] `hooks/useTasks.ts` - Hook pour la gestion des tâches +- [ ] `hooks/useTags.ts` - Hook pour la gestion des tags - [ ] `hooks/useKanban.ts` - Hook pour drag & drop - [ ] Gestion des erreurs et loading states -### 2.4 Fonctionnalités Kanban avancées +### 2.6 Fonctionnalités Kanban avancées - [ ] Drag & drop entre colonnes (react-beautiful-dnd) -- [ ] Formulaires de création/édition de tâches -- [ ] Filtrage par tags/statut/priorité +- [ ] Filtrage par statut/priorité/assigné - [ ] Recherche en temps réel dans les tâches -- [ ] Gestion des tags avec couleurs +- [ ] Tri des tâches (date, priorité, alphabétique) +- [ ] Actions en lot (sélection multiple) ## 📊 Phase 3: Dashboard et analytics (Priorité 3) diff --git a/components/kanban/Column.tsx b/components/kanban/Column.tsx index b1803fb..92145ab 100644 --- a/components/kanban/Column.tsx +++ b/components/kanban/Column.tsx @@ -1,5 +1,7 @@ import { Task, TaskStatus } from '@/lib/types'; import { TaskCard } from './TaskCard'; +import { Card, CardHeader, CardContent } from '@/components/ui/Card'; +import { Badge } from '@/components/ui/Badge'; interface KanbanColumnProps { id: TaskStatus; @@ -47,43 +49,45 @@ export function KanbanColumn({ id, title, color, tasks }: KanbanColumnProps) { cancelled: '✕' }; + const badgeVariant = color === 'green' ? 'success' : color === 'blue' ? 'primary' : color === 'red' ? 'danger' : 'default'; + return (
- {/* Header tech avec glow */} -
-
-
-
-

- {title} -

-
- - {String(tasks.length).padStart(2, '0')} - -
-
- - {/* Zone de contenu tech */} -
-
- {tasks.length === 0 ? ( -
-
- {techIcons[id]} -
-

NO DATA

-
-
-
+ + +
+
+
+

+ {title} +

- ) : ( - tasks.map((task) => ( - - )) - )} -
-
+ + {String(tasks.length).padStart(2, '0')} + +
+ + + +
+ {tasks.length === 0 ? ( +
+
+ {techIcons[id]} +
+

NO DATA

+
+
+
+
+ ) : ( + tasks.map((task) => ( + + )) + )} +
+
+
); } diff --git a/components/kanban/TaskCard.tsx b/components/kanban/TaskCard.tsx index fb66aa5..cf8493e 100644 --- a/components/kanban/TaskCard.tsx +++ b/components/kanban/TaskCard.tsx @@ -1,6 +1,8 @@ import { Task } from '@/lib/types'; import { formatDistanceToNow } from 'date-fns'; import { fr } from 'date-fns/locale'; +import { Card } from '@/components/ui/Card'; +import { Badge } from '@/components/ui/Badge'; interface TaskCardProps { task: Task; @@ -12,22 +14,9 @@ export function TaskCard({ task }: TaskCardProps) { const emojis = task.title.match(emojiRegex) || []; const titleWithoutEmojis = task.title.replace(emojiRegex, '').trim(); - // Couleur de priorité - const priorityColors = { - low: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300', - medium: 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-300', - high: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300', - urgent: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300' - }; - - // Couleur de source - const sourceColors = { - reminders: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300', - jira: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-300' - }; return ( -
+ {/* Header tech avec titre et status */}
{emojis.length > 0 && ( @@ -59,21 +48,23 @@ export function TaskCard({ task }: TaskCardProps) {

)} - {/* Tags tech style */} + {/* Tags avec composant Badge */} {task.tags && task.tags.length > 0 && (
{task.tags.slice(0, 3).map((tag, index) => ( - {tag} - + ))} {task.tags.length > 3 && ( - + +{task.tags.length - 3} - + )}
)} @@ -98,6 +89,6 @@ export function TaskCard({ task }: TaskCardProps) { )}
-
+ ); } diff --git a/components/ui/Badge.tsx b/components/ui/Badge.tsx new file mode 100644 index 0000000..ee3cace --- /dev/null +++ b/components/ui/Badge.tsx @@ -0,0 +1,44 @@ +import { HTMLAttributes, forwardRef } from 'react'; +import { cn } from '@/lib/utils'; + +interface BadgeProps extends HTMLAttributes { + variant?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'outline'; + size?: 'sm' | 'md'; +} + +const Badge = forwardRef( + ({ className, variant = 'default', size = 'md', ...props }, ref) => { + const baseStyles = 'inline-flex items-center font-mono font-medium transition-all duration-200'; + + const variants = { + default: 'bg-slate-800/50 text-slate-300 border border-slate-600/50', + primary: 'bg-cyan-950/50 text-cyan-300 border border-cyan-500/30', + success: 'bg-emerald-950/50 text-emerald-300 border border-emerald-500/30', + warning: 'bg-yellow-950/50 text-yellow-300 border border-yellow-500/30', + danger: 'bg-red-950/50 text-red-300 border border-red-500/30', + outline: 'bg-transparent text-slate-400 border border-slate-600 hover:bg-slate-800/30 hover:text-slate-300' + }; + + const sizes = { + sm: 'px-1.5 py-0.5 text-xs rounded', + md: 'px-2 py-1 text-xs rounded-md' + }; + + return ( + + ); + } +); + +Badge.displayName = 'Badge'; + +export { Badge }; diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx new file mode 100644 index 0000000..a0ac7f1 --- /dev/null +++ b/components/ui/Button.tsx @@ -0,0 +1,43 @@ +import { ButtonHTMLAttributes, forwardRef } from 'react'; +import { cn } from '@/lib/utils'; + +interface ButtonProps extends ButtonHTMLAttributes { + variant?: 'primary' | 'secondary' | 'danger' | 'ghost'; + size?: 'sm' | 'md' | 'lg'; +} + +const Button = forwardRef( + ({ className, variant = 'primary', size = 'md', ...props }, ref) => { + const baseStyles = 'inline-flex items-center justify-center font-mono font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-slate-950 disabled:opacity-50 disabled:cursor-not-allowed'; + + const variants = { + primary: 'bg-cyan-600 hover:bg-cyan-500 text-white border border-cyan-500/30 shadow-cyan-500/20 shadow-lg hover:shadow-cyan-500/30 focus:ring-cyan-500', + secondary: 'bg-slate-800 hover:bg-slate-700 text-slate-100 border border-slate-600 shadow-slate-500/20 shadow-lg hover:shadow-slate-500/30 focus:ring-slate-500', + danger: 'bg-red-600 hover:bg-red-500 text-white border border-red-500/30 shadow-red-500/20 shadow-lg hover:shadow-red-500/30 focus:ring-red-500', + ghost: 'bg-transparent hover:bg-slate-800/50 text-slate-300 hover:text-slate-100 border border-slate-700/50 hover:border-slate-600 focus:ring-slate-500' + }; + + const sizes = { + sm: 'px-3 py-1.5 text-xs rounded-md', + md: 'px-4 py-2 text-sm rounded-lg', + lg: 'px-6 py-3 text-base rounded-lg' + }; + + return ( + + + )} + + {/* Content */} +
+ {children} +
+ + , + document.body + ); +} diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..ac02527 --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,9 @@ +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +/** + * Utility function to merge Tailwind CSS classes + */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/package-lock.json b/package-lock.json index dd8cbdc..98d9b1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,12 +9,14 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "^6.16.1", + "clsx": "^2.1.1", "date-fns": "^4.1.0", "next": "15.5.3", "prisma": "^6.16.1", "react": "19.1.0", "react-dom": "19.1.0", - "sqlite3": "^5.1.7" + "sqlite3": "^5.1.7", + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -2717,6 +2719,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -7409,6 +7420,16 @@ "url": "https://opencollective.com/synckit" } }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", diff --git a/package.json b/package.json index 8fe4e74..fc3696e 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ }, "dependencies": { "@prisma/client": "^6.16.1", + "clsx": "^2.1.1", "date-fns": "^4.1.0", "next": "15.5.3", "prisma": "^6.16.1", "react": "19.1.0", "react-dom": "19.1.0", - "sqlite3": "^5.1.7" + "sqlite3": "^5.1.7", + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@eslint/eslintrc": "^3", diff --git a/tsconfig.json b/tsconfig.json index a086fe9..51d95a7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ "paths": { "@/*": ["./src/*"], "@/services/*": ["./services/*"], - "@/lib/*": ["./lib/*"] + "@/lib/*": ["./lib/*"], + "@/components/*": ["./components/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],