From 105cafe17f210973d4872c4056feab26f4e3c487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20OLIVIER?= Date: Fri, 22 May 2026 18:10:23 +0200 Subject: [PATCH] Edition des issues --- src/app/issues/issue-detail/issue-detail.css | 54 +++++++++ src/app/issues/issue-detail/issue-detail.html | 87 +++++++++++++-- src/app/issues/issue-detail/issue-detail.ts | 104 ++++++++---------- src/app/issues/issues.html | 2 +- src/app/issues/issues.store.ts | 81 ++++++++++++++ src/app/issues/issues.ts | 44 ++------ 6 files changed, 262 insertions(+), 110 deletions(-) create mode 100644 src/app/issues/issues.store.ts diff --git a/src/app/issues/issue-detail/issue-detail.css b/src/app/issues/issue-detail/issue-detail.css index 7b80023..c28b547 100644 --- a/src/app/issues/issue-detail/issue-detail.css +++ b/src/app/issues/issue-detail/issue-detail.css @@ -2,6 +2,13 @@ display: block; } +.page-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1rem; +} + .page-header h1 { margin: 0; font-size: 2rem; @@ -12,6 +19,34 @@ color: #4b5563; } +.edit-button { + border: none; + border-radius: 0.5rem; + background-color: #2563eb; + color: #ffffff; + padding: 0.65rem 1rem; + font-weight: 600; + cursor: pointer; +} + +.edit-button:hover { + background-color: #1d4ed8; +} + +.save-button { + border: none; + border-radius: 0.5rem; + background-color: #059669; + color: #ffffff; + padding: 0.65rem 1rem; + font-weight: 600; + cursor: pointer; +} + +.save-button:hover { + background-color: #047857; +} + .detail-card { background-color: #ffffff; border: 1px solid #e5e7eb; @@ -32,6 +67,21 @@ td { vertical-align: top; } +input, +select, +textarea { + width: 100%; + padding: 0.5rem 0.65rem; + border: 1px solid #d1d5db; + border-radius: 0.5rem; + font: inherit; + box-sizing: border-box; +} + +textarea { + resize: vertical; +} + th { width: 220px; background-color: #f9fafb; @@ -44,6 +94,10 @@ tr:last-child td { } @media (max-width: 768px) { + .page-header { + flex-direction: column; + } + th { width: 40%; } diff --git a/src/app/issues/issue-detail/issue-detail.html b/src/app/issues/issue-detail/issue-detail.html index 34f4b5d..b6fc2f7 100644 --- a/src/app/issues/issue-detail/issue-detail.html +++ b/src/app/issues/issue-detail/issue-detail.html @@ -1,6 +1,14 @@
@@ -8,39 +16,96 @@ ID - {{ issue().id }} + {{ issue.id }} Nom - {{ issue().name }} + + @if (isEditing) { + + } @else { + {{ issue.name || '-' }} + } + Epic - {{ issue().epic }} + + @if (isEditing) { + + } @else { + {{ issue.epic || '-' }} + } + Assignee - {{ issue().assignee }} + + @if (isEditing) { + + } @else { + {{ issue.assignee || '-' }} + } + Date d'echeance - {{ issue().dueDate }} + + @if (isEditing) { + + } @else { + {{ issue.dueDate || '-' }} + } + Description - {{ issue().description }} + + @if (isEditing) { + + } @else { + {{ issue.description || '-' }} + } + Priorite - {{ issue().priority }} + + @if (isEditing) { + + } @else { + {{ issue.priority }} + } + Status - {{ issue().status }} + + @if (isEditing) { + + } @else { + {{ issue.status }} + } + Progression - {{ issue().progress }}% + + @if (isEditing) { + + } @else { + {{ issue.progress }}% + } + diff --git a/src/app/issues/issue-detail/issue-detail.ts b/src/app/issues/issue-detail/issue-detail.ts index 60fa9de..755ca40 100644 --- a/src/app/issues/issue-detail/issue-detail.ts +++ b/src/app/issues/issue-detail/issue-detail.ts @@ -1,82 +1,64 @@ -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; -}; +import { Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { IssueEntity, IssuesStore } from '../issues.store'; @Component({ selector: 'app-issue-detail', - imports: [], + imports: [FormsModule], templateUrl: './issue-detail.html', styleUrl: './issue-detail.css', }) export class IssueDetail { private readonly route = inject(ActivatedRoute); + private readonly router = inject(Router); + private readonly issuesStore = inject(IssuesStore); - protected readonly issue = signal(this.buildIssue()); + protected issue: IssueEntity = this.buildIssue(); + protected isEditing = this.route.snapshot.queryParamMap.get('mode') === 'edit'; - private buildIssue(): IssueDetailModel { + protected toggleEdit(): void { + this.isEditing = !this.isEditing; + } + + protected saveIssue(): void { + this.issuesStore.upsert(this.issue); + this.isEditing = false; + this.router.navigate(['/issues']); + } + + private buildIssue(): IssueEntity { const idParam = this.route.snapshot.paramMap.get('id'); const draftId = this.route.snapshot.queryParamMap.get('draftId'); + const isNewIssueRoute = this.route.snapshot.routeConfig?.path === 'issues/new'; - const resolvedId = Number(idParam ?? draftId ?? 1); - const safeId = Number.isNaN(resolvedId) ? 1 : resolvedId; + const resolvedId = Number(idParam ?? draftId ?? 0); + const safeId = Number.isNaN(resolvedId) ? 0 : 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.', + if (isNewIssueRoute) { + return { + id: safeId, + assignee: '', + epic: '', + name: '', + dueDate: '', + description: '', 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, - }, - }; + status: 'draft', + progress: 0, + }; + } + + const existingIssue = this.issuesStore.getById(safeId); return ( - existingIssues[safeId] ?? { + existingIssue ?? { 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.', + assignee: '', + epic: '', + name: '', + dueDate: '', + description: '', priority: 'Moyenne', status: 'draft', progress: 0, diff --git a/src/app/issues/issues.html b/src/app/issues/issues.html index 95ecbac..56bf6ff 100644 --- a/src/app/issues/issues.html +++ b/src/app/issues/issues.html @@ -26,7 +26,7 @@ (click)="openIssue(issue.id)" (keydown.enter)="openIssue(issue.id)" > - {{ issue.title }} + {{ issue.name }} {{ issue.priority }} {{ issue.status }} {{ issue.assignee }} diff --git a/src/app/issues/issues.store.ts b/src/app/issues/issues.store.ts new file mode 100644 index 0000000..dac3bad --- /dev/null +++ b/src/app/issues/issues.store.ts @@ -0,0 +1,81 @@ +import { Injectable, signal } from '@angular/core'; + +export type IssueStatus = 'draft' | 'todo' | 'done' | 'in-progress'; +export type IssuePriority = 'Basse' | 'Moyenne' | 'Haute'; + +export type IssueEntity = { + id: number; + assignee: string; + epic: string; + name: string; + dueDate: string; + description: string; + priority: IssuePriority; + status: IssueStatus; + progress: number; +}; + +@Injectable({ providedIn: 'root' }) +export class IssuesStore { + private readonly data = signal([ + { + 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, + }, + { + 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, + }, + { + 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, + }, + ]); + + readonly issues = this.data.asReadonly(); + + getById(id: number): IssueEntity | undefined { + return this.data().find((issue) => issue.id === id); + } + + getNextId(): number { + const ids = this.data().map((issue) => issue.id); + return ids.length > 0 ? Math.max(...ids) + 1 : 1; + } + + upsert(issue: IssueEntity): void { + this.data.update((issues) => { + const existingIndex = issues.findIndex((current) => current.id === issue.id); + + if (existingIndex === -1) { + return [...issues, issue]; + } + + const updated = [...issues]; + updated[existingIndex] = issue; + return updated; + }); + } +} + diff --git a/src/app/issues/issues.ts b/src/app/issues/issues.ts index 3740dd3..2a842ea 100644 --- a/src/app/issues/issues.ts +++ b/src/app/issues/issues.ts @@ -1,13 +1,6 @@ -import { Component, signal } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { Router } from '@angular/router'; - -type Issue = { - id: number; - title: string; - priority: 'Basse' | 'Moyenne' | 'Haute'; - status: 'draft' | 'todo' | 'done' | 'in-progress'; - assignee: string; -}; +import { IssuesStore } from './issues.store'; @Component({ selector: 'app-issues', @@ -16,39 +9,16 @@ type Issue = { styleUrl: './issues.css', }) export class Issues { - constructor(private readonly router: Router) {} + private readonly router = inject(Router); + private readonly issuesStore = inject(IssuesStore); - protected readonly issues = signal([ - { - id: 1, - title: 'Bug affichage menu mobile', - priority: 'Haute', - status: 'in-progress', - assignee: 'Marie', - }, - { - id: 2, - title: 'Erreur validation formulaire projet', - priority: 'Moyenne', - status: 'todo', - assignee: 'Nabil', - }, - { - id: 3, - title: 'Mise a jour message de bienvenue', - priority: 'Basse', - status: 'done', - assignee: 'Sonia', - }, - ]); - - private nextId = 4; + protected readonly issues = this.issuesStore.issues; protected createIssue(): void { + const nextId = this.issuesStore.getNextId(); this.router.navigate(['/issues/new'], { - queryParams: { draftId: this.nextId }, + queryParams: { draftId: nextId, mode: 'edit' }, }); - this.nextId += 1; } protected openIssue(issueId: number): void {