feat: add sorting functionality to KanbanFilters
- Enhanced `KanbanFilters` to include sorting options, allowing users to sort tasks dynamically. - Implemented dropdown for sorting with options rendered via portal to manage z-index issues. - Updated `TasksContext` to handle sorting preferences and apply sorting logic to both pinned and regular tasks. - Added `sortBy` property to `KanbanFilters` and user preferences for persistent sorting settings.
This commit is contained in:
200
lib/sort-config.ts
Normal file
200
lib/sort-config.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import { Task, TaskPriority } from './types';
|
||||
import { getPriorityConfig } from './status-config';
|
||||
|
||||
export type SortField = 'priority' | 'tags' | 'createdAt' | 'updatedAt' | 'dueDate' | 'title';
|
||||
export type SortDirection = 'asc' | 'desc';
|
||||
|
||||
export interface SortConfig {
|
||||
field: SortField;
|
||||
direction: SortDirection;
|
||||
}
|
||||
|
||||
export interface SortOption {
|
||||
key: string;
|
||||
label: string;
|
||||
field: SortField;
|
||||
direction: SortDirection;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
// Configuration des options de tri disponibles
|
||||
export const SORT_OPTIONS: SortOption[] = [
|
||||
{
|
||||
key: 'priority-desc',
|
||||
label: 'Priorité (Urgente → Faible)',
|
||||
field: 'priority',
|
||||
direction: 'desc',
|
||||
icon: '🔥'
|
||||
},
|
||||
{
|
||||
key: 'priority-asc',
|
||||
label: 'Priorité (Faible → Urgente)',
|
||||
field: 'priority',
|
||||
direction: 'asc',
|
||||
icon: '🔵'
|
||||
},
|
||||
{
|
||||
key: 'tags-asc',
|
||||
label: 'Tags (A → Z)',
|
||||
field: 'tags',
|
||||
direction: 'asc',
|
||||
icon: '🏷️'
|
||||
},
|
||||
{
|
||||
key: 'title-asc',
|
||||
label: 'Titre (A → Z)',
|
||||
field: 'title',
|
||||
direction: 'asc',
|
||||
icon: '📝'
|
||||
},
|
||||
{
|
||||
key: 'title-desc',
|
||||
label: 'Titre (Z → A)',
|
||||
field: 'title',
|
||||
direction: 'desc',
|
||||
icon: '📝'
|
||||
},
|
||||
{
|
||||
key: 'createdAt-desc',
|
||||
label: 'Date création (Récent → Ancien)',
|
||||
field: 'createdAt',
|
||||
direction: 'desc',
|
||||
icon: '📅'
|
||||
},
|
||||
{
|
||||
key: 'createdAt-asc',
|
||||
label: 'Date création (Ancien → Récent)',
|
||||
field: 'createdAt',
|
||||
direction: 'asc',
|
||||
icon: '📅'
|
||||
},
|
||||
{
|
||||
key: 'dueDate-asc',
|
||||
label: 'Échéance (Proche → Lointaine)',
|
||||
field: 'dueDate',
|
||||
direction: 'asc',
|
||||
icon: '⏰'
|
||||
},
|
||||
{
|
||||
key: 'dueDate-desc',
|
||||
label: 'Échéance (Lointaine → Proche)',
|
||||
field: 'dueDate',
|
||||
direction: 'desc',
|
||||
icon: '⏰'
|
||||
}
|
||||
];
|
||||
|
||||
// Tri par défaut : Priorité (desc) puis Tags (asc)
|
||||
export const DEFAULT_SORT: SortConfig[] = [
|
||||
{ field: 'priority', direction: 'desc' },
|
||||
{ field: 'tags', direction: 'asc' }
|
||||
];
|
||||
|
||||
/**
|
||||
* Compare deux valeurs selon la direction de tri
|
||||
*/
|
||||
function compareValues<T>(a: T, b: T, direction: SortDirection): number {
|
||||
if (a === b) return 0;
|
||||
if (a == null) return 1;
|
||||
if (b == null) return -1;
|
||||
|
||||
const result = a < b ? -1 : 1;
|
||||
return direction === 'asc' ? result : -result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient la valeur de priorité numérique pour le tri
|
||||
*/
|
||||
function getPriorityValue(priority: TaskPriority): number {
|
||||
return getPriorityConfig(priority).order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtient le premier tag pour le tri (ou chaîne vide si pas de tags)
|
||||
*/
|
||||
function getFirstTag(task: Task): string {
|
||||
return task.tags?.[0]?.toLowerCase() || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare deux tâches selon un critère de tri
|
||||
*/
|
||||
function compareTasksByField(a: Task, b: Task, sortConfig: SortConfig): number {
|
||||
const { field, direction } = sortConfig;
|
||||
|
||||
switch (field) {
|
||||
case 'priority':
|
||||
return compareValues(
|
||||
getPriorityValue(a.priority),
|
||||
getPriorityValue(b.priority),
|
||||
direction
|
||||
);
|
||||
|
||||
case 'tags':
|
||||
return compareValues(
|
||||
getFirstTag(a),
|
||||
getFirstTag(b),
|
||||
direction
|
||||
);
|
||||
|
||||
case 'title':
|
||||
return compareValues(
|
||||
a.title.toLowerCase(),
|
||||
b.title.toLowerCase(),
|
||||
direction
|
||||
);
|
||||
|
||||
case 'createdAt':
|
||||
return compareValues(
|
||||
new Date(a.createdAt).getTime(),
|
||||
new Date(b.createdAt).getTime(),
|
||||
direction
|
||||
);
|
||||
|
||||
case 'updatedAt':
|
||||
return compareValues(
|
||||
new Date(a.updatedAt).getTime(),
|
||||
new Date(b.updatedAt).getTime(),
|
||||
direction
|
||||
);
|
||||
|
||||
case 'dueDate':
|
||||
return compareValues(
|
||||
a.dueDate ? new Date(a.dueDate).getTime() : null,
|
||||
b.dueDate ? new Date(b.dueDate).getTime() : null,
|
||||
direction
|
||||
);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trie un tableau de tâches selon une configuration de tri multiple
|
||||
*/
|
||||
export function sortTasks(tasks: Task[], sortConfigs: SortConfig[] = DEFAULT_SORT): Task[] {
|
||||
return [...tasks].sort((a, b) => {
|
||||
for (const sortConfig of sortConfigs) {
|
||||
const result = compareTasksByField(a, b, sortConfig);
|
||||
if (result !== 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilitaire pour obtenir une option de tri par sa clé
|
||||
*/
|
||||
export function getSortOption(key: string): SortOption | undefined {
|
||||
return SORT_OPTIONS.find(option => option.key === key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utilitaire pour créer une clé de tri
|
||||
*/
|
||||
export function createSortKey(field: SortField, direction: SortDirection): string {
|
||||
return `${field}-${direction}`;
|
||||
}
|
||||
Reference in New Issue
Block a user