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:
2
TODO.md
2
TODO.md
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
42
components/ui/FontSizeToggle.tsx
Normal file
42
components/ui/FontSizeToggle.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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: []
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user