| Description |
diff --git a/src/app/issues/issue-detail/issue-detail.ts b/src/app/issues/issue-detail/issue-detail.ts
index f56eaef..8b6f37c 100644
--- a/src/app/issues/issue-detail/issue-detail.ts
+++ b/src/app/issues/issue-detail/issue-detail.ts
@@ -17,6 +17,33 @@ export class IssueDetail {
protected issue: IssueEntity = this.buildIssue();
protected isEditing = this.route.snapshot.queryParamMap.get('mode') === 'edit';
private issueBeforeEdit: IssueEntity | null = null;
+ protected readonly issues = this.issuesStore.issues;
+ protected moreMenuOpen = false;
+
+ protected readonly statusOptions: IssueEntity['status'][] = [
+ 'draft',
+ 'todo',
+ 'in-progress',
+ 'done',
+ ];
+
+ protected get dependencyIds(): number[] {
+ return this.issue.dependsOnIds;
+ }
+
+ protected set dependencyIds(value: number[]) {
+ this.issue.dependsOnIds = Array.isArray(value)
+ ? value.filter((dependencyId): dependencyId is number => typeof dependencyId === 'number')
+ : [];
+ }
+
+ protected get estimatedTimeValue(): number | null {
+ return this.issue.estimatedTime;
+ }
+
+ protected set estimatedTimeValue(value: number | null) {
+ this.issue.estimatedTime = value === null || value === undefined ? null : Number(value);
+ }
constructor() {
if (this.isEditing) {
@@ -43,6 +70,40 @@ export class IssueDetail {
this.router.navigate(['/issues', this.issue.id]);
}
+ protected deleteIssue(): void {
+ this.issuesStore.deleteById(this.issue.id);
+ this.router.navigate(['/issues']);
+ }
+
+ protected updateStatus(status: IssueEntity['status']): void {
+ this.issue.status = status;
+ this.issuesStore.upsert(this.issue);
+ }
+
+ protected toggleMoreMenu(): void {
+ this.moreMenuOpen = !this.moreMenuOpen;
+ }
+
+ protected closeMoreMenu(): void {
+ this.moreMenuOpen = false;
+ }
+
+ protected resolveDependencyLabels(issueIds: number[]): string {
+ if (issueIds.length === 0) {
+ return '-';
+ }
+
+ return issueIds
+ .map((issueId) => this.issues().find((issue) => issue.id === issueId))
+ .filter((issue): issue is IssueEntity => Boolean(issue))
+ .map((issue) => `#${issue.id} - ${issue.name || 'Sans nom'}`)
+ .join(', ');
+ }
+
+ protected get dependencyCandidates(): IssueEntity[] {
+ return this.issues().filter((issue) => issue.id !== this.issue.id);
+ }
+
private cloneIssue(issue: IssueEntity): IssueEntity {
return { ...issue };
}
@@ -63,6 +124,8 @@ export class IssueDetail {
name: '',
dueDate: '',
description: '',
+ estimatedTime: null,
+ dependsOnIds: [],
priority: 'Moyenne',
status: 'draft',
progress: 0,
@@ -79,6 +142,8 @@ export class IssueDetail {
name: '',
dueDate: '',
description: '',
+ estimatedTime: null,
+ dependsOnIds: [],
priority: 'Moyenne',
status: 'draft',
progress: 0,
diff --git a/src/app/issues/issues.store.ts b/src/app/issues/issues.store.ts
index efbab24..adcdbf1 100644
--- a/src/app/issues/issues.store.ts
+++ b/src/app/issues/issues.store.ts
@@ -12,6 +12,8 @@ export type IssueEntity = {
name: string;
dueDate: string;
description: string;
+ estimatedTime: number | null;
+ dependsOnIds: number[];
priority: IssuePriority;
status: IssueStatus;
progress: number;
@@ -25,6 +27,8 @@ const DEFAULT_ISSUES: IssueEntity[] = [
name: 'Bug affichage menu mobile',
dueDate: '2026-06-10',
description: 'Corriger le comportement du menu sur petits ecrans.',
+ estimatedTime: 8,
+ dependsOnIds: [],
priority: 'Haute',
status: 'in-progress',
progress: 35,
@@ -36,6 +40,8 @@ const DEFAULT_ISSUES: IssueEntity[] = [
name: 'Erreur validation formulaire projet',
dueDate: '2026-06-12',
description: 'Fiabiliser les regles de validation du formulaire projet.',
+ estimatedTime: 16,
+ dependsOnIds: [],
priority: 'Moyenne',
status: 'todo',
progress: 20,
@@ -47,6 +53,8 @@ const DEFAULT_ISSUES: IssueEntity[] = [
name: 'Mise a jour message de bienvenue',
dueDate: '2026-06-18',
description: 'Mettre a jour le wording d accueil selon la charte produit.',
+ estimatedTime: 4,
+ dependsOnIds: [],
priority: 'Basse',
status: 'done',
progress: 100,
@@ -60,7 +68,7 @@ export class IssuesStore {
constructor() {
const cachedIssues = this.readFromStorage();
if (cachedIssues) {
- this.data.set(cachedIssues);
+ this.data.set(cachedIssues.map((issue) => this.normalizeIssue(issue)));
}
}
@@ -75,23 +83,73 @@ export class IssuesStore {
return ids.length > 0 ? Math.max(...ids) + 1 : 1;
}
+ createDraftIssue(): IssueEntity {
+ const draftIssue: IssueEntity = this.normalizeIssue({
+ id: this.getNextId(),
+ assignee: '',
+ epic: '',
+ name: '',
+ dueDate: '',
+ description: '',
+ estimatedTime: null,
+ dependsOnIds: [],
+ priority: 'Moyenne',
+ status: 'draft',
+ progress: 0,
+ });
+
+ this.upsert(draftIssue);
+ return draftIssue;
+ }
+
upsert(issue: IssueEntity): void {
+ const normalizedIssue = this.normalizeIssue(issue);
+
this.data.update((issues) => {
const existingIndex = issues.findIndex((current) => current.id === issue.id);
if (existingIndex === -1) {
- const created = [...issues, issue];
+ const created = [...issues, normalizedIssue];
this.persistToStorage(created);
return created;
}
const updated = [...issues];
- updated[existingIndex] = issue;
+ updated[existingIndex] = normalizedIssue;
this.persistToStorage(updated);
return updated;
});
}
+ deleteById(id: number): void {
+ this.data.update((issues) => {
+ const updated = issues
+ .filter((issue) => issue.id !== id)
+ .map((issue) => ({
+ ...issue,
+ dependsOnIds: issue.dependsOnIds.filter((dependencyId) => dependencyId !== id),
+ }));
+
+ this.persistToStorage(updated);
+ return updated;
+ });
+ }
+
+ private normalizeIssue(
+ issue: Partial & { dependsOnId?: number | null },
+ ): IssueEntity {
+ const legacyDependency = typeof issue.dependsOnId === 'number' ? [issue.dependsOnId] : [];
+ const normalizedDependencies = Array.isArray(issue.dependsOnIds)
+ ? issue.dependsOnIds.filter((value): value is number => typeof value === 'number')
+ : legacyDependency;
+
+ return {
+ ...issue,
+ estimatedTime: issue.estimatedTime ?? null,
+ dependsOnIds: normalizedDependencies,
+ } as IssueEntity;
+ }
+
private readFromStorage(): IssueEntity[] | null {
if (typeof window === 'undefined') {
return null;
diff --git a/src/app/menu/menu.html b/src/app/menu/menu.html
index bd2687c..ebc9791 100644
--- a/src/app/menu/menu.html
+++ b/src/app/menu/menu.html
@@ -1,5 +1,4 @@
|