import { Component, inject } from '@angular/core'; import { Router } from '@angular/router'; import { IssueEntity, IssuesStore } from './issues.store'; @Component({ selector: 'app-issues', imports: [], templateUrl: './issues.html', styleUrl: './issues.css', }) export class Issues { private readonly router = inject(Router); private readonly issuesStore = inject(IssuesStore); constructor() { this.issuesStore.load(); } protected readonly issues = this.issuesStore.issues; protected selectedType: IssueEntity['type'] | null = null; protected readonly typeOptions: IssueEntity['type'][] = [ 'Epic', 'Bug', 'Study', 'Story', 'Task', 'Technical Story', ]; protected get filteredIssues(): IssueEntity[] { if (this.selectedType === null) return this.issues(); return this.issues().filter((i) => i.type === this.selectedType); } protected selectType(type: IssueEntity['type'] | null): void { this.selectedType = this.selectedType === type ? null : type; } protected createIssue(): void { this.router.navigate(['/issues/new']); } protected openIssue(issueId: number): void { this.router.navigate(['/issues', issueId]); } protected getProgress(issue: IssueEntity): number { if (issue.type !== 'Epic') { return issue.progress; } const children = this.issues().filter( (i) => i.id !== issue.id && (i.epic === issue.name || i.dependsOnIds.includes(issue.id)), ); if (children.length === 0) return 0; const done = children.filter((i) => i.status === 'done').length; return Math.round((done / children.length) * 100); } protected priorityDisplay(priority: IssueEntity['priority']): { symbol: string; color: string; label: string } { const map: Record = { 'TRES_HAUTE': { symbol: '↑↑', color: '#dc3545', label: 'Très haute' }, 'HAUTE': { symbol: '↑', color: '#fd7e14', label: 'Haute' }, 'MOYENNE': { symbol: '–', color: '#ffc107', label: 'Moyenne' }, 'BASSE': { symbol: '↓', color: '#0d6efd', label: 'Basse' }, 'TRES_FAIBLE':{ symbol: '↓↓', color: '#6c757d', label: 'Très faible'}, }; return map[priority] ?? { symbol: '?', color: '#6c757d', label: priority }; } protected typeIcon(type: IssueEntity['type']): { letter: string; bg: string } { const map: Record = { Epic: { letter: 'E', bg: '#7c3aed' }, Bug: { letter: 'B', bg: '#dc2626' }, Story: { letter: 'S', bg: '#16a34a' }, Task: { letter: 'T', bg: '#2563eb' }, Study: { letter: 'St', bg: '#6b7280' }, 'Technical Story':{ letter: 'TS', bg: '#d97706' }, }; return map[type] ?? { letter: '?', bg: '#6b7280' }; } protected typeBadgeClass(type: IssueEntity['type']): string { const map: Record = { Bug: 'text-bg-danger', Study: 'text-bg-secondary', Story: 'text-bg-success', Task: 'text-bg-primary', 'Technical Story': 'text-bg-warning', Epic: 'text-bg-info', }; return map[type] ?? 'text-bg-secondary'; } protected statusBadge(status: IssueEntity['status']): { label: string; bg: string; color: string } { const map: Record = { draft: { label: 'BROUILLON', bg: '#e2e8f0', color: '#475569' }, todo: { label: 'À FAIRE', bg: '#dbeafe', color: '#1d4ed8' }, 'in-progress': { label: 'EN COURS', bg: '#ffedd5', color: '#9a3412' }, done: { label: 'TERMINÉ', bg: '#dcfce7', color: '#166534' }, }; return map[status] ?? { label: status, bg: '#e2e8f0', color: '#475569' }; } }