diff --git a/src/actions/preferences.ts b/src/actions/preferences.ts index 79c33ec..fc1e51e 100644 --- a/src/actions/preferences.ts +++ b/src/actions/preferences.ts @@ -1,7 +1,8 @@ 'use server'; import { userPreferencesService } from '@/services/core/user-preferences'; -import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus, Theme } from '@/lib/types'; +import { KanbanFilters, ViewPreferences, ColumnVisibility, TaskStatus } from '@/lib/types'; +import { Theme } from '@/lib/theme-config'; import { revalidatePath } from 'next/cache'; /** diff --git a/src/app/globals.css b/src/app/globals.css index 5a0d450..910f6f9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -22,6 +22,14 @@ --blue: #2563eb; /* blue-600 */ --gray: #6b7280; /* gray-500 */ --gray-light: #e5e7eb; /* gray-200 */ + + /* Cartes spéciales */ + --jira-card: #dbeafe; /* blue-100 - clair */ + --tfs-card: #fed7aa; /* orange-200 - clair */ + --jira-border: #3b82f6; /* blue-500 */ + --tfs-border: #f59e0b; /* amber-500 */ + --jira-text: #1e40af; /* blue-800 - foncé pour contraste */ + --tfs-text: #92400e; /* amber-800 - foncé pour contraste */ } .light { @@ -46,13 +54,21 @@ --blue: #2563eb; /* blue-600 */ --gray: #6b7280; /* gray-500 */ --gray-light: #e5e7eb; /* gray-200 */ + + /* Cartes spéciales */ + --jira-card: #dbeafe; /* blue-100 - clair */ + --tfs-card: #fed7aa; /* orange-200 - clair */ + --jira-border: #3b82f6; /* blue-500 */ + --tfs-border: #f59e0b; /* amber-500 */ + --jira-text: #1e40af; /* blue-800 - foncé pour contraste */ + --tfs-text: #92400e; /* amber-800 - foncé pour contraste */ } .dark { /* Dark theme override */ --background: #1e293b; /* slate-800 - encore plus clair */ --foreground: #f1f5f9; /* slate-100 */ - --card: #334155; /* slate-700 - beaucoup plus clair pour contraste fort */ + --card: #1e293b; /* slate-800 - même couleur que le background */ --card-hover: #475569; /* slate-600 */ --card-column: #0f172a; /* slate-900 - plus foncé que les cartes */ --border: #64748b; /* slate-500 - encore plus clair */ @@ -70,6 +86,14 @@ --blue: #3b82f6; /* blue-500 */ --gray: #9ca3af; /* gray-400 */ --gray-light: #374151; /* gray-700 */ + + /* Cartes spéciales */ + --jira-card: #1e3a8a; /* blue-900 - très foncé */ + --tfs-card: #9a3412; /* orange-900 - très foncé */ + --jira-border: #60a5fa; /* blue-400 - plus clair pour contraste */ + --tfs-border: #fb923c; /* orange-400 - plus clair pour contraste */ + --jira-text: #93c5fd; /* blue-300 - clair pour contraste */ + --tfs-text: #fdba74; /* orange-300 - clair pour contraste */ } .dracula { @@ -94,6 +118,14 @@ --blue: #8be9fd; /* dracula cyan */ --gray: #6272a4; /* dracula comment */ --gray-light: #44475a; /* dracula current line */ + + /* Cartes spéciales */ + --jira-card: #44475a; /* dracula current line - fond neutre */ + --tfs-card: #44475a; /* dracula current line - fond neutre */ + --jira-border: #8be9fd; /* dracula cyan */ + --tfs-border: #ffb86c; /* dracula orange */ + --jira-text: #f8f8f2; /* dracula foreground - texte principal */ + --tfs-text: #f8f8f2; /* dracula foreground - texte principal */ } .monokai { @@ -118,6 +150,14 @@ --blue: #66d9ef; /* monokai cyan */ --gray: #75715e; /* monokai comment */ --gray-light: #3e3d32; /* monokai selection */ + + /* Cartes spéciales */ + --jira-card: #3e3d32; /* monokai selection - fond neutre */ + --tfs-card: #3e3d32; /* monokai selection - fond neutre */ + --jira-border: #66d9ef; /* monokai cyan */ + --tfs-border: #fd971f; /* monokai orange */ + --jira-text: #f8f8f2; /* monokai foreground */ + --tfs-text: #f8f8f2; /* monokai foreground */ } .nord { @@ -142,6 +182,14 @@ --blue: #5e81ac; /* nord10 */ --gray: #4c566a; /* nord3 */ --gray-light: #3b4252; /* nord1 */ + + /* Cartes spéciales */ + --jira-card: #3b4252; /* nord1 - fond neutre */ + --tfs-card: #3b4252; /* nord1 - fond neutre */ + --jira-border: #5e81ac; /* nord10 - bleu */ + --tfs-border: #d08770; /* nord12 - orange */ + --jira-text: #d8dee9; /* nord4 - texte principal */ + --tfs-text: #d8dee9; /* nord4 - texte principal */ } .gruvbox { @@ -166,6 +214,14 @@ --blue: #83a598; /* gruvbox blue */ --gray: #a89984; /* gruvbox gray */ --gray-light: #3c3836; /* gruvbox bg1 */ + + /* Cartes spéciales */ + --jira-card: #3c3836; /* gruvbox bg1 - fond neutre */ + --tfs-card: #3c3836; /* gruvbox bg1 - fond neutre */ + --jira-border: #83a598; /* gruvbox blue */ + --tfs-border: #fe8019; /* gruvbox orange */ + --jira-text: #ebdbb2; /* gruvbox fg */ + --tfs-text: #ebdbb2; /* gruvbox fg */ } .tokyo_night { @@ -190,6 +246,14 @@ --blue: #7aa2f7; /* tokyo-night blue */ --gray: #565f89; /* tokyo-night comment */ --gray-light: #24283b; /* tokyo-night bg_highlight */ + + /* Cartes spéciales */ + --jira-card: #24283b; /* tokyo-night bg_highlight - fond neutre */ + --tfs-card: #24283b; /* tokyo-night bg_highlight - fond neutre */ + --jira-border: #7aa2f7; /* tokyo-night blue */ + --tfs-border: #ff9e64; /* tokyo-night orange */ + --jira-text: #a9b1d6; /* tokyo-night fg */ + --tfs-text: #a9b1d6; /* tokyo-night fg */ } .catppuccin { @@ -214,6 +278,14 @@ --blue: #89b4fa; /* catppuccin blue */ --gray: #6c7086; /* catppuccin overlay0 */ --gray-light: #313244; /* catppuccin surface0 */ + + /* Cartes spéciales */ + --jira-card: #313244; /* catppuccin surface0 - fond neutre */ + --tfs-card: #313244; /* catppuccin surface0 - fond neutre */ + --jira-border: #89b4fa; /* catppuccin blue */ + --tfs-border: #fab387; /* catppuccin peach */ + --jira-text: #cdd6f4; /* catppuccin text */ + --tfs-text: #cdd6f4; /* catppuccin text */ } .rose_pine { @@ -238,6 +310,14 @@ --blue: #3e8fb0; /* rose-pine pine */ --gray: #6e6a86; /* rose-pine muted */ --gray-light: #26233a; /* rose-pine surface */ + + /* Cartes spéciales */ + --jira-card: #26233a; /* rose-pine surface - fond neutre */ + --tfs-card: #26233a; /* rose-pine surface - fond neutre */ + --jira-border: #3e8fb0; /* rose-pine pine - bleu */ + --tfs-border: #f6c177; /* rose-pine gold - orange/jaune */ + --jira-text: #e0def4; /* rose-pine text */ + --tfs-text: #e0def4; /* rose-pine text */ } .one_dark { @@ -262,6 +342,14 @@ --blue: #61afef; /* one-dark blue */ --gray: #5c6370; /* one-dark bg3 */ --gray-light: #3e4451; /* one-dark bg1 */ + + /* Cartes spéciales */ + --jira-card: #3e4451; /* one-dark bg1 - fond neutre */ + --tfs-card: #3e4451; /* one-dark bg1 - fond neutre */ + --jira-border: #61afef; /* one-dark blue */ + --tfs-border: #e5c07b; /* one-dark yellow */ + --jira-text: #abb2bf; /* one-dark fg */ + --tfs-text: #abb2bf; /* one-dark fg */ } .material { @@ -286,6 +374,14 @@ --blue: #2196f3; /* material info */ --gray: #3c3c3c; /* material outline */ --gray-light: #1e1e1e; /* material surface */ + + /* Cartes spéciales */ + --jira-card: #1e1e1e; /* material surface - fond neutre */ + --tfs-card: #1e1e1e; /* material surface - fond neutre */ + --jira-border: #2196f3; /* material info - bleu */ + --tfs-border: #ffab40; /* material secondary - orange */ + --jira-text: #ffffff; /* material on-bg */ + --tfs-text: #ffffff; /* material on-bg */ } .solarized { @@ -310,6 +406,14 @@ --blue: #268bd2; /* solarized blue */ --gray: #586e75; /* solarized base01 */ --gray-light: #073642; /* solarized base02 */ + + /* Cartes spéciales */ + --jira-card: #073642; /* solarized base02 - fond neutre */ + --tfs-card: #073642; /* solarized base02 - fond neutre */ + --jira-border: #268bd2; /* solarized blue */ + --tfs-border: #b58900; /* solarized yellow */ + --jira-text: #93a1a1; /* solarized base1 */ + --tfs-text: #93a1a1; /* solarized base1 */ } @theme inline { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e31ec0a..8fbef51 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,6 +5,7 @@ import { ThemeProvider } from "@/contexts/ThemeContext"; import { JiraConfigProvider } from "@/contexts/JiraConfigContext"; import { UserPreferencesProvider } from "@/contexts/UserPreferencesContext"; import { userPreferencesService } from "@/services/core/user-preferences"; +import { KeyboardShortcuts } from "@/components/KeyboardShortcuts"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -38,6 +39,7 @@ export default async function RootLayout({ initialTheme={initialPreferences.viewPreferences.theme} userPreferredTheme={initialPreferences.viewPreferences.theme === 'light' ? 'dark' : initialPreferences.viewPreferences.theme} > + {children} diff --git a/src/components/KeyboardShortcuts.tsx b/src/components/KeyboardShortcuts.tsx new file mode 100644 index 0000000..ab72f3f --- /dev/null +++ b/src/components/KeyboardShortcuts.tsx @@ -0,0 +1,8 @@ +'use client'; + +import { useKeyboardShortcuts } from '@/hooks/useKeyboardShortcuts'; + +export function KeyboardShortcuts() { + useKeyboardShortcuts(); + return null; // Ce composant ne rend rien, il gère juste les raccourcis +} diff --git a/src/components/ThemeSelector.tsx b/src/components/ThemeSelector.tsx index 39eeb03..e312325 100644 --- a/src/components/ThemeSelector.tsx +++ b/src/components/ThemeSelector.tsx @@ -1,23 +1,20 @@ 'use client'; import { useTheme } from '@/contexts/ThemeContext'; -import { Theme } from '@/lib/types'; +import { Theme } from '@/lib/theme-config'; import { Button } from '@/components/ui/Button'; +import { THEME_CONFIG, getThemeMetadata } from '@/lib/theme-config'; -const themes: { id: Theme; name: string; description: string }[] = [ - { id: 'dark', name: 'Dark', description: 'Thème sombre par défaut' }, - { id: 'dracula', name: 'Dracula', description: 'Thème Dracula coloré' }, - { id: 'monokai', name: 'Monokai', description: 'Thème Monokai vibrant' }, - { id: 'nord', name: 'Nord', description: 'Thème Nord minimaliste' }, - { id: 'gruvbox', name: 'Gruvbox', description: 'Thème Gruvbox chaleureux' }, - { id: 'tokyo_night', name: 'Tokyo Night', description: 'Thème Tokyo Night moderne' }, - { id: 'catppuccin', name: 'Catppuccin', description: 'Thème Catppuccin pastel' }, - { id: 'rose_pine', name: 'Rose Pine', description: 'Thème Rose Pine élégant' }, - { id: 'one_dark', name: 'One Dark', description: 'Thème One Dark populaire' }, - { id: 'material', name: 'Material', description: 'Thème Material Design' }, - { id: 'solarized', name: 'Solarized', description: 'Thème Solarized scientifique' }, -]; +// Génération des thèmes à partir de la configuration centralisée +const themes: { id: Theme; name: string; description: string }[] = THEME_CONFIG.allThemes.map(themeId => { + const metadata = getThemeMetadata(themeId); + return { + id: themeId, + name: metadata.name, + description: metadata.description + }; +}); // Composant pour l'aperçu du thème function ThemePreview({ themeId, isSelected }: { themeId: Theme; isSelected: boolean }) { diff --git a/src/components/dashboard/RecentTasks.tsx b/src/components/dashboard/RecentTasks.tsx index a6a8790..f361b4c 100644 --- a/src/components/dashboard/RecentTasks.tsx +++ b/src/components/dashboard/RecentTasks.tsx @@ -2,12 +2,8 @@ import { Task } from '@/lib/types'; import { Card } from '@/components/ui/Card'; -import { TagDisplay } from '@/components/ui/TagDisplay'; -import { formatDateShort } from '@/lib/date-utils'; import { TaskCard } from '@/components/ui'; import { useTasksContext } from '@/contexts/TasksContext'; -import { getPriorityConfig, getStatusLabel } from '@/lib/status-config'; -import { TaskPriority } from '@/lib/types'; import Link from 'next/link'; interface RecentTasksProps { @@ -45,46 +41,44 @@ export function RecentTasks({ tasks }: RecentTasksProps) { ) : (
{recentTasks.map((task) => ( - { - try { - return getPriorityConfig(task.priority as TaskPriority).label; - } catch { - return task.priority; - } - })() : undefined} - tags={task.tags && task.tags.length > 0 ? [ - , - ...(task.tags.length > 2 ? [ - - +{task.tags.length - 2} - - ] : []) - ] : undefined} - metadata={formatDateShort(task.updatedAt)} - actions={ - - +
+ { + // Navigation vers le kanban avec la tâche sélectionnée + window.location.href = `/kanban?taskId=${task.id}`; + }} + /> + + {/* Overlay avec lien vers le kanban */} + +
+ - - } - /> +
+ +
))}
)} diff --git a/src/components/ui-showcase/UIShowcaseClient.tsx b/src/components/ui-showcase/UIShowcaseClient.tsx index db206f2..2c9dcbb 100644 --- a/src/components/ui-showcase/UIShowcaseClient.tsx +++ b/src/components/ui-showcase/UIShowcaseClient.tsx @@ -9,41 +9,21 @@ import { Input } from '@/components/ui/Input'; import { StyledCard } from '@/components/ui/StyledCard'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'; import { StatCard, ProgressBar, ActionCard, TaskCard, MetricCard, ToggleButton, SearchInput, ControlPanel, ControlSection, ControlGroup, FilterSummary, FilterChip, ColumnHeader, EmptyState, DropZone } from '@/components/ui'; -import { ThemeSelector } from '@/components/ThemeSelector'; +import { Header } from '@/components/ui/Header'; export function UIShowcaseClient() { const [inputValue, setInputValue] = useState(''); return ( -
-
- {/* Header */} -
-

- 🎨 UI Components Showcase -

-

- Démonstration de tous les composants UI disponibles -

-
+
+ {/* Header avec navigation et dropdown de thèmes */} +
+ +
- {/* Theme Selector */} -
-
-

- 🎨 Sélecteur de Thèmes -

-

- Changez de thème pour voir comment tous les composants s'adaptent -

-
- -
-
- -
-
-
{/* Buttons Section */}
@@ -530,43 +510,145 @@ export function UIShowcaseClient() { {/* Task Cards */}

Task Cards

-
- Frontend, - UI - ]} - metadata="Il y a 2h" - actions={ - - } - /> - - Design - ]} - metadata="Hier" - actions={ - - } - /> +
+ {/* Task Card Compacte */} +
+
+ variant="compact" - Vue compacte +
+ +
+ + {/* Task Card Détaillée */} +
+
+ variant="detailed" - Vue détaillée +
+ +
+ + {/* Task Card Jira */} +
+
+ source="jira" - Tâche Jira avec styles spéciaux +
+ +
+ + {/* Task Card TFS */} +
+
+ source="tfs" - Tâche TFS avec styles spéciaux +
+ +
+ + {/* Task Card avec différents statuts */} +
+
+ Statuts spéciaux - freeze, archived, cancelled +
+
+ + + +
+
diff --git a/src/components/ui/Header.tsx b/src/components/ui/Header.tsx index 6d17aec..e8b79c3 100644 --- a/src/components/ui/Header.tsx +++ b/src/components/ui/Header.tsx @@ -5,6 +5,8 @@ import { useJiraConfig } from '@/contexts/JiraConfigContext'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; import { useState } from 'react'; +import { Theme } from '@/lib/theme-config'; +import { THEME_CONFIG, getThemeMetadata } from '@/lib/theme-config'; interface HeaderProps { title?: string; @@ -13,10 +15,22 @@ interface HeaderProps { } export function Header({ title = "TowerControl", subtitle = "Task Management", syncing = false }: HeaderProps) { - const { theme, toggleTheme, userPreferredTheme } = useTheme(); + const { theme, setTheme } = useTheme(); const { isConfigured: isJiraConfigured, config: jiraConfig } = useJiraConfig(); const pathname = usePathname(); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + const [themeDropdownOpen, setThemeDropdownOpen] = useState(false); + + // Liste des thèmes disponibles avec leurs labels et icônes + const themes: { value: Theme; label: string; icon: string }[] = THEME_CONFIG.allThemes.map(themeValue => { + const metadata = getThemeMetadata(themeValue); + + return { + value: themeValue, + label: metadata.name, + icon: metadata.icon + }; + }); // Fonction pour déterminer si un lien est actif const isActiveLink = (href: string) => { @@ -83,24 +97,53 @@ export function Header({ title = "TowerControl", subtitle = "Task Management", s {/* Controls mobile/tablette */}
- {/* Theme Toggle */} - + + {themeDropdownOpen && ( + <> + {/* Backdrop */} +
setThemeDropdownOpen(false)} + /> + {/* Dropdown */} +
+
+ {themes.map((themeOption) => ( + + ))} +
+
+ )} - +
{/* Menu burger */} + + {themeDropdownOpen && ( + <> + {/* Backdrop */} +
setThemeDropdownOpen(false)} + /> + {/* Dropdown */} +
+
+ {themes.map((themeOption) => ( + + ))} +
+
+ )} - +
diff --git a/src/components/ui/TaskCard.tsx b/src/components/ui/TaskCard.tsx index 47b3c4e..01e2fb5 100644 --- a/src/components/ui/TaskCard.tsx +++ b/src/components/ui/TaskCard.tsx @@ -2,6 +2,7 @@ import { HTMLAttributes, forwardRef, useState, useEffect, useRef } from 'react'; import { cn } from '@/lib/utils'; import { Card } from './Card'; import { Badge } from './Badge'; +import { formatDateForDisplay } from '@/lib/date-utils'; interface TaskCardProps extends HTMLAttributes { // Variants @@ -171,18 +172,16 @@ const TaskCard = forwardRef( const getSourceStyles = () => { if (source === 'jira') { return { - border: '2px solid rgba(0, 130, 201, 0.8)', - borderLeft: '6px solid #0052CC', - background: 'linear-gradient(135deg, rgba(0, 130, 201, 0.3) 0%, rgba(0, 130, 201, 0.2) 100%)', - boxShadow: '0 4px 12px rgba(0, 130, 201, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3)', + backgroundColor: 'var(--jira-card, #dbeafe)', + borderLeft: '3px solid var(--jira-border, #3b82f6)', + color: 'var(--jira-text, #1e40af)', }; } if (source === 'tfs') { return { - border: '2px solid rgba(255, 165, 0, 0.8)', - borderLeft: '6px solid #FF8C00', - background: 'linear-gradient(135deg, rgba(255, 165, 0, 0.3) 0%, rgba(255, 165, 0, 0.2) 100%)', - boxShadow: '0 4px 12px rgba(255, 165, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3)', + backgroundColor: 'var(--tfs-card, #fed7aa)', + borderLeft: '3px solid var(--tfs-border, #f59e0b)', + color: 'var(--tfs-text, #92400e)', }; } return {}; @@ -224,20 +223,17 @@ const TaskCard = forwardRef( return ( -
+
{/* Emojis */} {displayEmojis.length > 0 && ( @@ -318,20 +314,17 @@ const TaskCard = forwardRef( return ( -
+
{/* Header */}
{/* Emojis */} @@ -452,7 +445,7 @@ const TaskCard = forwardRef( {dueDate ? ( - {dueDate.toLocaleDateString()} + {formatDateForDisplay(dueDate, 'DISPLAY_MEDIUM')} ) : (
diff --git a/src/components/ui/index.ts b/src/components/ui/index.ts index ab4b8a6..1d39294 100644 --- a/src/components/ui/index.ts +++ b/src/components/ui/index.ts @@ -24,5 +24,4 @@ export { DropZone } from './DropZone'; // Composants existants export { Card, CardHeader, CardTitle, CardContent, CardFooter } from './Card'; -export { Header } from './Header'; export { FontSizeToggle } from './FontSizeToggle'; diff --git a/src/contexts/ThemeContext.tsx b/src/contexts/ThemeContext.tsx index e0cffc4..58d2994 100644 --- a/src/contexts/ThemeContext.tsx +++ b/src/contexts/ThemeContext.tsx @@ -2,12 +2,14 @@ import { createContext, useContext, useEffect, useState, ReactNode } from 'react'; import { updateViewPreferences } from '@/actions/preferences'; -import { Theme } from '@/lib/types'; +import { Theme } from '@/lib/theme-config'; +import { THEME_CONFIG, getNextDarkTheme } from '@/lib/theme-config'; interface ThemeContextType { theme: Theme; toggleTheme: () => void; setTheme: (theme: Theme) => void; + cycleDarkThemes: () => void; userPreferredTheme: Theme; } @@ -33,7 +35,7 @@ export function ThemeProvider({ children, initialTheme = 'dark', userPreferredTh useEffect(() => { if (mounted) { // Remove all existing theme classes - document.documentElement.classList.remove('light', 'dark', 'dracula', 'monokai', 'nord', 'gruvbox', 'tokyo_night', 'catppuccin', 'rose_pine', 'one_dark', 'material', 'solarized'); + document.documentElement.classList.remove(...THEME_CONFIG.allThemes); // Add the current theme class document.documentElement.classList.add(theme); } @@ -78,8 +80,20 @@ export function ThemeProvider({ children, initialTheme = 'dark', userPreferredTh } }; + const cycleDarkThemes = async () => { + // Si on est sur light, on passe au premier thème dark + if (theme === 'light') { + await setTheme(THEME_CONFIG.darkThemes[0]); + return; + } + + // Sinon, on passe au thème dark suivant + const nextTheme = getNextDarkTheme(theme); + await setTheme(nextTheme); + }; + return ( - +
{children}
diff --git a/src/hooks/useKeyboardShortcuts.ts b/src/hooks/useKeyboardShortcuts.ts new file mode 100644 index 0000000..281a7e3 --- /dev/null +++ b/src/hooks/useKeyboardShortcuts.ts @@ -0,0 +1,30 @@ +'use client'; + +import { useEffect } from 'react'; +import { useTheme } from '@/contexts/ThemeContext'; + +export function useKeyboardShortcuts() { + const { toggleTheme, cycleDarkThemes } = useTheme(); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + // Cmd + T pour basculer entre light et le thème dark préféré + if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'D') { + event.preventDefault(); + toggleTheme(); + } + + // Cmd + Shift + T pour faire tourner les thèmes dark + if ((event.metaKey || event.ctrlKey) && event.shiftKey && event.key === 'T') { + event.preventDefault(); + cycleDarkThemes(); + } + }; + + document.addEventListener('keydown', handleKeyDown); + + return () => { + document.removeEventListener('keydown', handleKeyDown); + }; + }, [toggleTheme, cycleDarkThemes]); +} diff --git a/src/lib/theme-config.ts b/src/lib/theme-config.ts new file mode 100644 index 0000000..1b5892c --- /dev/null +++ b/src/lib/theme-config.ts @@ -0,0 +1,76 @@ +// Types de thèmes +export type Theme = 'light' | 'dark' | 'dracula' | 'monokai' | 'nord' | 'gruvbox' | 'tokyo_night' | 'catppuccin' | 'rose_pine' | 'one_dark' | 'material' | 'solarized'; + +// Configuration des thèmes +export const THEME_CONFIG = { + // Thème par défaut + default: 'dark' as Theme, + + // Thème light + light: 'light' as Theme, + + // Liste de tous les thèmes dark disponibles + darkThemes: [ + 'dark', + 'dracula', + 'monokai', + 'nord', + 'gruvbox', + 'tokyo_night', + 'catppuccin', + 'rose_pine', + 'one_dark', + 'material', + 'solarized' + ] as Theme[], + + // Tous les thèmes disponibles + allThemes: [ + 'light', + 'dark', + 'dracula', + 'monokai', + 'nord', + 'gruvbox', + 'tokyo_night', + 'catppuccin', + 'rose_pine', + 'one_dark', + 'material', + 'solarized' + ] as Theme[], + + // Métadonnées des thèmes (pour l'UI future) + metadata: { + light: { name: 'Light', description: 'Thème clair par défaut', icon: '☀️' }, + dark: { name: 'Dark', description: 'Thème sombre classique', icon: '🌙' }, + dracula: { name: 'Dracula', description: 'Inspiré du thème Dracula', icon: '🧛' }, + monokai: { name: 'Monokai', description: 'Inspiré du thème Monokai', icon: '🎨' }, + nord: { name: 'Nord', description: 'Palette Nord arctique', icon: '❄️' }, + gruvbox: { name: 'Gruvbox', description: 'Palette Gruvbox retro', icon: '🎭' }, + tokyo_night: { name: 'Tokyo Night', description: 'Nuit tokyoïte', icon: '🌃' }, + catppuccin: { name: 'Catppuccin', description: 'Palette pastel douce', icon: '🐱' }, + rose_pine: { name: 'Rose Pine', description: 'Palette rose et pin', icon: '🌹' }, + one_dark: { name: 'One Dark', description: 'Inspiré d\'Atom One Dark', icon: '🌑' }, + material: { name: 'Material', description: 'Inspiré de Material Design', icon: '📱' }, + solarized: { name: 'Solarized', description: 'Palette Solarized', icon: '☀️' } + } +} as const; + +// Fonctions utilitaires +export const getNextDarkTheme = (currentTheme: Theme): Theme => { + const currentIndex = THEME_CONFIG.darkThemes.indexOf(currentTheme); + if (currentIndex === -1) { + return THEME_CONFIG.darkThemes[0]; + } + const nextIndex = (currentIndex + 1) % THEME_CONFIG.darkThemes.length; + return THEME_CONFIG.darkThemes[nextIndex]; +}; + +export const isDarkTheme = (theme: Theme): boolean => { + return THEME_CONFIG.darkThemes.includes(theme); +}; + +export const getThemeMetadata = (theme: Theme) => { + return THEME_CONFIG.metadata[theme] || { name: theme, description: 'Thème personnalisé', icon: '🎨' }; +}; diff --git a/src/lib/types.ts b/src/lib/types.ts index 01ed514..9bc18fb 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,4 +1,5 @@ import { TfsConfig } from '@/services/integrations/tfs'; +import { Theme } from './theme-config'; // Types de base pour les tâches // Note: TaskStatus et TaskPriority sont maintenant gérés par la configuration centralisée dans lib/status-config.ts @@ -13,8 +14,7 @@ export type TaskStatus = export type TaskPriority = 'low' | 'medium' | 'high' | 'urgent'; export type TaskSource = 'reminders' | 'jira' | 'tfs' | 'manual'; -// Types de thèmes partagés -export type Theme = 'light' | 'dark' | 'dracula' | 'monokai' | 'nord' | 'gruvbox' | 'tokyo_night' | 'catppuccin' | 'rose_pine' | 'one_dark' | 'material' | 'solarized'; +// Types de thèmes partagés - maintenant dans theme-config.ts // Interface centralisée pour les statistiques export interface TaskStats {