From f6acfd0e308dc2e61482d06723ba509ed9c54cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20OLIVIER?= Date: Fri, 22 May 2026 18:03:06 +0200 Subject: [PATCH] detail de l'issue --- src/app/app.routes.ts | 3 + src/app/issues/issue-detail/issue-detail.css | 51 +++++++++++ src/app/issues/issue-detail/issue-detail.html | 47 ++++++++++ .../issues/issue-detail/issue-detail.spec.ts | 22 +++++ src/app/issues/issue-detail/issue-detail.ts | 86 +++++++++++++++++++ src/app/issues/issues.css | 13 +++ src/app/issues/issues.html | 7 +- src/app/issues/issues.ts | 27 +++--- 8 files changed, 242 insertions(+), 14 deletions(-) create mode 100644 src/app/issues/issue-detail/issue-detail.css create mode 100644 src/app/issues/issue-detail/issue-detail.html create mode 100644 src/app/issues/issue-detail/issue-detail.spec.ts create mode 100644 src/app/issues/issue-detail/issue-detail.ts diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 48dc3df..2ff2cd7 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,5 +1,6 @@ import { Routes } from '@angular/router'; import { Home } from './home/home'; +import { IssueDetail } from './issues/issue-detail/issue-detail'; import { Issues } from './issues/issues'; import { Projects } from './projects/projects'; @@ -8,6 +9,8 @@ export const routes: Routes = [ { path: 'home', component: Home }, { path: 'project', component: Projects }, { path: 'projects', redirectTo: 'project' }, + { path: 'issues/new', component: IssueDetail }, + { path: 'issues/:id', component: IssueDetail }, { path: 'issues', component: Issues }, { path: '**', redirectTo: 'home' }, ]; diff --git a/src/app/issues/issue-detail/issue-detail.css b/src/app/issues/issue-detail/issue-detail.css new file mode 100644 index 0000000..7b80023 --- /dev/null +++ b/src/app/issues/issue-detail/issue-detail.css @@ -0,0 +1,51 @@ +:host { + display: block; +} + +.page-header h1 { + margin: 0; + font-size: 2rem; +} + +.page-header p { + margin: 0.5rem 0 1.5rem; + color: #4b5563; +} + +.detail-card { + background-color: #ffffff; + border: 1px solid #e5e7eb; + border-radius: 0.75rem; + overflow: hidden; +} + +table { + width: 100%; + border-collapse: collapse; +} + +th, +td { + padding: 0.9rem 1rem; + border-bottom: 1px solid #e5e7eb; + text-align: left; + vertical-align: top; +} + +th { + width: 220px; + background-color: #f9fafb; + color: #374151; +} + +tr:last-child th, +tr:last-child td { + border-bottom: none; +} + +@media (max-width: 768px) { + th { + width: 40%; + } +} + diff --git a/src/app/issues/issue-detail/issue-detail.html b/src/app/issues/issue-detail/issue-detail.html new file mode 100644 index 0000000..34f4b5d --- /dev/null +++ b/src/app/issues/issue-detail/issue-detail.html @@ -0,0 +1,47 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{ issue().id }}
Nom{{ issue().name }}
Epic{{ issue().epic }}
Assignee{{ issue().assignee }}
Date d'echeance{{ issue().dueDate }}
Description{{ issue().description }}
Priorite{{ issue().priority }}
Status{{ issue().status }}
Progression{{ issue().progress }}%
+
diff --git a/src/app/issues/issue-detail/issue-detail.spec.ts b/src/app/issues/issue-detail/issue-detail.spec.ts new file mode 100644 index 0000000..93f93d3 --- /dev/null +++ b/src/app/issues/issue-detail/issue-detail.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { IssueDetail } from './issue-detail'; + +describe('IssueDetail', () => { + let component: IssueDetail; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [IssueDetail], + }).compileComponents(); + + fixture = TestBed.createComponent(IssueDetail); + component = fixture.componentInstance; + await fixture.whenStable(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/issues/issue-detail/issue-detail.ts b/src/app/issues/issue-detail/issue-detail.ts new file mode 100644 index 0000000..60fa9de --- /dev/null +++ b/src/app/issues/issue-detail/issue-detail.ts @@ -0,0 +1,86 @@ +import { Component, inject, signal } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +type IssueStatus = 'draft' | 'todo' | 'done' | 'in-progress'; + +type IssueDetailModel = { + id: number; + assignee: string; + epic: string; + name: string; + dueDate: string; + description: string; + priority: 'Basse' | 'Moyenne' | 'Haute'; + status: IssueStatus; + progress: number; +}; + +@Component({ + selector: 'app-issue-detail', + imports: [], + templateUrl: './issue-detail.html', + styleUrl: './issue-detail.css', +}) +export class IssueDetail { + private readonly route = inject(ActivatedRoute); + + protected readonly issue = signal(this.buildIssue()); + + private buildIssue(): IssueDetailModel { + const idParam = this.route.snapshot.paramMap.get('id'); + const draftId = this.route.snapshot.queryParamMap.get('draftId'); + + const resolvedId = Number(idParam ?? draftId ?? 1); + const safeId = Number.isNaN(resolvedId) ? 1 : resolvedId; + + const existingIssues: Record = { + 1: { + id: 1, + assignee: 'Marie', + epic: 'EPIC-UI', + name: 'Bug affichage menu mobile', + dueDate: '2026-06-10', + description: 'Corriger le comportement du menu sur petits ecrans.', + priority: 'Haute', + status: 'in-progress', + progress: 35, + }, + 2: { + id: 2, + assignee: 'Nabil', + epic: 'EPIC-FORM', + name: 'Erreur validation formulaire projet', + dueDate: '2026-06-12', + description: 'Fiabiliser les regles de validation du formulaire projet.', + priority: 'Moyenne', + status: 'todo', + progress: 20, + }, + 3: { + id: 3, + assignee: 'Sonia', + epic: 'EPIC-CONTENT', + name: 'Mise a jour message de bienvenue', + dueDate: '2026-06-18', + description: 'Mettre a jour le wording d accueil selon la charte produit.', + priority: 'Basse', + status: 'done', + progress: 100, + }, + }; + + return ( + existingIssues[safeId] ?? { + id: safeId, + assignee: 'A definir', + epic: 'EPIC-WEBAPP', + name: `Nouvelle issue ${safeId}`, + dueDate: '2026-06-15', + description: 'Decrire ici le contexte, les attentes et les criteres d acceptation.', + priority: 'Moyenne', + status: 'draft', + progress: 0, + } + ); + } +} diff --git a/src/app/issues/issues.css b/src/app/issues/issues.css index 2772944..1039660 100644 --- a/src/app/issues/issues.css +++ b/src/app/issues/issues.css @@ -64,6 +64,19 @@ tbody tr:last-child td { border-bottom: none; } +.clickable-row { + cursor: pointer; +} + +.clickable-row:hover { + background-color: #f3f4f6; +} + +.clickable-row:focus-visible { + outline: 2px solid #2563eb; + outline-offset: -2px; +} + @media (max-width: 768px) { .page-header { flex-direction: column; diff --git a/src/app/issues/issues.html b/src/app/issues/issues.html index 0e1f1e9..95ecbac 100644 --- a/src/app/issues/issues.html +++ b/src/app/issues/issues.html @@ -20,7 +20,12 @@ @for (issue of issues(); track issue.id) { - + {{ issue.title }} {{ issue.priority }} {{ issue.status }} diff --git a/src/app/issues/issues.ts b/src/app/issues/issues.ts index 45933bb..3740dd3 100644 --- a/src/app/issues/issues.ts +++ b/src/app/issues/issues.ts @@ -1,10 +1,11 @@ import { Component, signal } from '@angular/core'; +import { Router } from '@angular/router'; type Issue = { id: number; title: string; priority: 'Basse' | 'Moyenne' | 'Haute'; - status: 'Ouverte' | 'En cours' | 'Nouvelle'; + status: 'draft' | 'todo' | 'done' | 'in-progress'; assignee: string; }; @@ -15,26 +16,28 @@ type Issue = { styleUrl: './issues.css', }) export class Issues { + constructor(private readonly router: Router) {} + protected readonly issues = signal([ { id: 1, title: 'Bug affichage menu mobile', priority: 'Haute', - status: 'Ouverte', + status: 'in-progress', assignee: 'Marie', }, { id: 2, title: 'Erreur validation formulaire projet', priority: 'Moyenne', - status: 'En cours', + status: 'todo', assignee: 'Nabil', }, { id: 3, title: 'Mise a jour message de bienvenue', priority: 'Basse', - status: 'Ouverte', + status: 'done', assignee: 'Sonia', }, ]); @@ -42,15 +45,13 @@ export class Issues { private nextId = 4; protected createIssue(): void { - const newIssue: Issue = { - id: this.nextId, - title: `Nouvelle issue ${this.nextId}`, - priority: 'Moyenne', - status: 'Nouvelle', - assignee: 'A definir', - }; - - this.issues.update((currentIssues) => [...currentIssues, newIssue]); + this.router.navigate(['/issues/new'], { + queryParams: { draftId: this.nextId }, + }); this.nextId += 1; } + + protected openIssue(issueId: number): void { + this.router.navigate(['/issues', issueId]); + } }