Ajout filtre par priorité des issues

Signed-off-by: Gato <cedric@goutailler-olivier.fr>
This commit is contained in:
2026-05-31 14:53:55 +02:00
parent f680c06208
commit 844090528f
4 changed files with 105 additions and 2 deletions
+23
View File
@@ -32,5 +32,28 @@
font-weight: 700;
}
.sortable-col {
cursor: pointer;
user-select: none;
white-space: nowrap;
}
.sortable-col:hover {
background-color: #e9ecef;
}
.sortable-col.sorted {
color: #2563eb;
}
.sort-icon {
font-size: 0.75rem;
margin-left: 0.25rem;
}
.sort-icon--idle {
color: #adb5bd;
}
+11 -1
View File
@@ -165,7 +165,17 @@
<th>#</th>
<th>Titre</th>
<th>Type</th>
<th>Priorite</th>
<th
class="sortable-col"
(click)="toggleSortPriority()"
[class.sorted]="sortPriority !== null"
title="Trier par priorité"
>
Priorité
@if (sortPriority === 'desc') { <span class="sort-icon"></span> }
@else if (sortPriority === 'asc') { <span class="sort-icon"></span> }
@else { <span class="sort-icon sort-icon--idle"></span> }
</th>
<th>Statut</th>
<th>Milestone</th>
<th>Assignee</th>
+53
View File
@@ -356,6 +356,59 @@ describe('Issues', () => {
});
});
describe('sortPriority — toggleSortPriority', () => {
it('starts with no sort active', () => {
expect((component as any).sortPriority).toBeNull();
});
it('first toggle sets sort to desc (highest first)', () => {
(component as any).toggleSortPriority();
expect((component as any).sortPriority).toBe('desc');
});
it('second toggle sets sort to asc (lowest first)', () => {
(component as any).sortPriority = 'desc';
(component as any).toggleSortPriority();
expect((component as any).sortPriority).toBe('asc');
});
it('third toggle resets sort to null', () => {
(component as any).sortPriority = 'asc';
(component as any).toggleSortPriority();
expect((component as any).sortPriority).toBeNull();
});
});
describe('filteredIssues — priority sort', () => {
beforeEach(() => {
store.upsert(makeIssue({ id: 10, name: 'Très haute', priority: 'TRES_HAUTE' }));
store.upsert(makeIssue({ id: 11, name: 'Basse', priority: 'BASSE' }));
store.upsert(makeIssue({ id: 12, name: 'Haute', priority: 'HAUTE' }));
});
it('does not reorder issues when sortPriority is null', () => {
(component as any).sortPriority = null;
const ids = (component as any).filteredIssues.map((i: IssueEntity) => i.id);
expect(ids).toEqual(store.issues().map((i) => i.id));
});
it('sorts highest priority first when sortPriority is desc', () => {
(component as any).sortPriority = 'desc';
const priorities = (component as any).filteredIssues.map((i: IssueEntity) => i.priority);
const order = ['TRES_HAUTE', 'HAUTE', 'MOYENNE', 'BASSE', 'TRES_FAIBLE'];
const indices = priorities.map((p: string) => order.indexOf(p));
expect(indices).toEqual([...indices].sort((a, b) => a - b));
});
it('sorts lowest priority first when sortPriority is asc', () => {
(component as any).sortPriority = 'asc';
const priorities = (component as any).filteredIssues.map((i: IssueEntity) => i.priority);
const order = ['TRES_HAUTE', 'HAUTE', 'MOYENNE', 'BASSE', 'TRES_FAIBLE'];
const indices = priorities.map((p: string) => order.indexOf(p));
expect(indices).toEqual([...indices].sort((a, b) => b - a));
});
});
describe('createIssue', () => {
it('navigates to /projects/:pid/issues/new', () => {
const spy = vi.spyOn(router, 'navigate').mockResolvedValue(true);
+18 -1
View File
@@ -57,6 +57,12 @@ export class Issues {
'TRES_HAUTE', 'HAUTE', 'MOYENNE', 'BASSE', 'TRES_FAIBLE',
];
private readonly PRIORITY_ORDER: Record<IssuePriority, number> = {
TRES_HAUTE: 0, HAUTE: 1, MOYENNE: 2, BASSE: 3, TRES_FAIBLE: 4,
};
protected sortPriority: 'desc' | 'asc' | null = null;
protected readonly statusOptions = this.statusesStore.statuses;
protected getMilestoneForIssue(issueId: number): MilestoneEntity | undefined {
@@ -66,7 +72,7 @@ export class Issues {
protected get filteredIssues(): IssueEntity[] {
const q = this.searchQuery.trim().toLowerCase();
const milestoneActive = this.selectedMilestoneIds.size > 0 || this.showNoMilestone;
return this.issues().filter((i) => {
const filtered = this.issues().filter((i) => {
if (this.selectedTypes.size > 0 && !this.selectedTypes.has(i.type)) return false;
if (this.selectedStatuses.size > 0 && !this.selectedStatuses.has(i.status)) return false;
if (this.selectedPriorities.size > 0 && !this.selectedPriorities.has(i.priority)) return false;
@@ -79,6 +85,17 @@ export class Issues {
if (q && !i.name.toLowerCase().includes(q)) return false;
return true;
});
if (this.sortPriority === null) return filtered;
const dir = this.sortPriority === 'desc' ? 1 : -1;
return [...filtered].sort(
(a, b) => (this.PRIORITY_ORDER[a.priority] - this.PRIORITY_ORDER[b.priority]) * dir,
);
}
protected toggleSortPriority(): void {
if (this.sortPriority === null) this.sortPriority = 'desc';
else if (this.sortPriority === 'desc') this.sortPriority = 'asc';
else this.sortPriority = null;
}
protected toggleDropdown(name: 'type' | 'status' | 'priority' | 'milestone', event: Event): void {