feat: add font size toggle functionality

- Implemented a FontSizeToggle component in HomePageClient for adjusting task font sizes in kanban views.
- Updated TaskCard to apply dynamic font size classes based on user preferences.
- Enhanced user preferences to include font size settings, defaulting to 'medium'.
- Modified TODO.md to mark the font size toggle task as complete.
This commit is contained in:
Julien Froidefond
2025-09-18 09:51:40 +02:00
parent 4a4eb9c8ad
commit 7394b16999
7 changed files with 91 additions and 7 deletions

View File

@@ -139,7 +139,7 @@
- [ ] Export des données en CSV/JSON - [ ] Export des données en CSV/JSON
## Autre Todo ## Autre Todo
- [ ] Avoir un bouton pour réduire/agrandir la font des taches dans les kanban (swimlane et classique) - [x] Avoir un bouton pour réduire/agrandir la font des taches dans les kanban (swimlane et classique)
## 🔧 Phase 4: Server Actions - Migration API Routes (Nouveau) ## 🔧 Phase 4: Server Actions - Migration API Routes (Nouveau)

View File

@@ -10,6 +10,7 @@ import { CreateTaskData } from '@/clients/tasks-client';
import { CreateTaskForm } from '@/components/forms/CreateTaskForm'; import { CreateTaskForm } from '@/components/forms/CreateTaskForm';
import { Button } from '@/components/ui/Button'; import { Button } from '@/components/ui/Button';
import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter'; import { JiraQuickFilter } from '@/components/kanban/JiraQuickFilter';
import { FontSizeToggle } from '@/components/ui/FontSizeToggle';
interface HomePageClientProps { interface HomePageClientProps {
initialTasks: Task[]; initialTasks: Task[];
@@ -141,6 +142,9 @@ function HomePageContent() {
</svg> </svg>
{swimlanesByTags ? 'Standard' : 'Swimlanes'} {swimlanesByTags ? 'Standard' : 'Swimlanes'}
</button> </button>
{/* Font Size Toggle */}
<FontSizeToggle />
</div> </div>
</div> </div>

View File

@@ -25,6 +25,32 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
const timeoutRef = useRef<NodeJS.Timeout | null>(null); const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const { tags: availableTags, refreshTasks } = useTasksContext(); const { tags: availableTags, refreshTasks } = useTasksContext();
const { preferences } = useUserPreferences(); const { preferences } = useUserPreferences();
// Classes CSS pour les différentes tailles de police
const getFontSizeClasses = () => {
switch (preferences.viewPreferences.fontSize) {
case 'small':
return {
title: 'text-xs',
description: 'text-xs',
meta: 'text-xs'
};
case 'large':
return {
title: 'text-base',
description: 'text-sm',
meta: 'text-sm'
};
default: // medium
return {
title: 'text-sm',
description: 'text-xs',
meta: 'text-xs'
};
}
};
const fontClasses = getFontSizeClasses();
// Helper pour construire l'URL Jira // Helper pour construire l'URL Jira
const getJiraTicketUrl = (jiraKey: string): string => { const getJiraTicketUrl = (jiraKey: string): string => {
@@ -159,7 +185,7 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
const TitleWithTooltip = () => ( const TitleWithTooltip = () => (
<div className="relative flex-1"> <div className="relative flex-1">
<h4 <h4
className="font-mono text-sm font-medium text-[var(--foreground)] leading-tight line-clamp-2 cursor-pointer hover:text-[var(--primary)] transition-colors" className={`font-mono ${fontClasses.title} font-medium text-[var(--foreground)] leading-tight line-clamp-2 cursor-pointer hover:text-[var(--primary)] transition-colors`}
onClick={handleTitleClick} onClick={handleTitleClick}
onMouseEnter={handleMouseEnter} onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave} onMouseLeave={handleMouseLeave}
@@ -243,7 +269,7 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
onKeyDown={handleTitleKeyPress} onKeyDown={handleTitleKeyPress}
onBlur={handleTitleSave} onBlur={handleTitleSave}
autoFocus autoFocus
className="flex-1 bg-transparent border-none outline-none text-[var(--foreground)] font-mono text-sm font-medium leading-tight" className={`flex-1 bg-transparent border-none outline-none text-[var(--foreground)] font-mono ${fontClasses.title} font-medium leading-tight`}
/> />
) : ( ) : (
<TitleWithTooltip /> <TitleWithTooltip />
@@ -372,7 +398,7 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
{/* Description tech */} {/* Description tech */}
{task.description && ( {task.description && (
<p className="text-xs text-[var(--muted-foreground)] mb-3 line-clamp-1 font-mono"> <p className={`${fontClasses.description} text-[var(--muted-foreground)] mb-3 line-clamp-1 font-mono`}>
{task.description} {task.description}
</p> </p>
)} )}
@@ -397,7 +423,7 @@ export function TaskCard({ task, onEdit, compactView = false }: TaskCardProps) {
{/* Footer tech avec séparateur néon - seulement si des données à afficher */} {/* Footer tech avec séparateur néon - seulement si des données à afficher */}
{(task.dueDate || (task.source && task.source !== 'manual') || task.completedAt) && ( {(task.dueDate || (task.source && task.source !== 'manual') || task.completedAt) && (
<div className="pt-2 border-t border-[var(--border)]/50"> <div className="pt-2 border-t border-[var(--border)]/50">
<div className="flex items-center justify-between text-xs"> <div className={`flex items-center justify-between ${fontClasses.meta}`}>
{task.dueDate ? ( {task.dueDate ? (
<span className="flex items-center gap-1 text-[var(--muted-foreground)] font-mono"> <span className="flex items-center gap-1 text-[var(--muted-foreground)] font-mono">
<span className="text-[var(--primary)]"></span> <span className="text-[var(--primary)]"></span>

View File

@@ -0,0 +1,42 @@
'use client';
import { useUserPreferences } from '@/contexts/UserPreferencesContext';
export function FontSizeToggle() {
const { preferences, toggleFontSize } = useUserPreferences();
// Icône pour la taille de police
const getFontSizeIcon = () => {
switch (preferences.viewPreferences.fontSize) {
case 'small':
return 'A';
case 'large':
return 'A';
default:
return 'A';
}
};
const getFontSizeScale = () => {
switch (preferences.viewPreferences.fontSize) {
case 'small':
return 'text-xs';
case 'large':
return 'text-lg';
default:
return 'text-sm';
}
};
return (
<button
onClick={toggleFontSize}
className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-mono transition-all bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)] hover:border-[var(--primary)]/50 hover:text-[var(--primary)]"
title={`Font size: ${preferences.viewPreferences.fontSize} (click to cycle)`}
>
<span className={`font-mono font-bold ${getFontSizeScale()}`}>
{getFontSizeIcon()}
</span>
</button>
);
}

View File

@@ -70,7 +70,8 @@ export interface ViewPreferences {
showFilters: boolean; showFilters: boolean;
objectivesCollapsed: boolean; objectivesCollapsed: boolean;
theme: 'light' | 'dark'; theme: 'light' | 'dark';
[key: string]: boolean | 'tags' | 'priority' | 'light' | 'dark' | undefined; fontSize: 'small' | 'medium' | 'large';
[key: string]: boolean | 'tags' | 'priority' | 'light' | 'dark' | 'small' | 'medium' | 'large' | undefined;
} }
export interface ColumnVisibility { export interface ColumnVisibility {

View File

@@ -18,7 +18,8 @@ const DEFAULT_PREFERENCES: UserPreferences = {
showObjectives: true, showObjectives: true,
showFilters: true, showFilters: true,
objectivesCollapsed: false, objectivesCollapsed: false,
theme: 'dark' theme: 'dark',
fontSize: 'medium'
}, },
columnVisibility: { columnVisibility: {
hiddenStatuses: [] hiddenStatuses: []

View File

@@ -16,6 +16,7 @@ interface UserPreferencesContextType {
toggleObjectivesCollapse: () => Promise<void>; toggleObjectivesCollapse: () => Promise<void>;
toggleTheme: () => Promise<void>; toggleTheme: () => Promise<void>;
setTheme: (theme: 'light' | 'dark') => Promise<void>; setTheme: (theme: 'light' | 'dark') => Promise<void>;
toggleFontSize: () => Promise<void>;
// Column Visibility // Column Visibility
updateColumnVisibility: (updates: Partial<ColumnVisibility>) => Promise<void>; updateColumnVisibility: (updates: Partial<ColumnVisibility>) => Promise<void>;
@@ -90,6 +91,14 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
await updateViewPreferences({ theme }); await updateViewPreferences({ theme });
}, [updateViewPreferences]); }, [updateViewPreferences]);
const toggleFontSize = useCallback(async () => {
const fontSizes: ('small' | 'medium' | 'large')[] = ['small', 'medium', 'large'];
const currentIndex = fontSizes.indexOf(preferences.viewPreferences.fontSize);
const nextIndex = (currentIndex + 1) % fontSizes.length;
const newFontSize = fontSizes[nextIndex];
await updateViewPreferences({ fontSize: newFontSize });
}, [preferences.viewPreferences.fontSize, updateViewPreferences]);
// === COLUMN VISIBILITY === // === COLUMN VISIBILITY ===
const updateColumnVisibility = useCallback(async (updates: Partial<ColumnVisibility>) => { const updateColumnVisibility = useCallback(async (updates: Partial<ColumnVisibility>) => {
@@ -129,6 +138,7 @@ export function UserPreferencesProvider({ children, initialPreferences }: UserPr
toggleObjectivesCollapse, toggleObjectivesCollapse,
toggleTheme, toggleTheme,
setTheme, setTheme,
toggleFontSize,
updateColumnVisibility, updateColumnVisibility,
toggleColumnVisibility, toggleColumnVisibility,
isColumnVisible isColumnVisible