cd93533b7c
Signed-off-by: Gato <cedric@goutailler-olivier.fr>
124 lines
4.0 KiB
TypeScript
124 lines
4.0 KiB
TypeScript
import { Injectable, inject, signal } from '@angular/core';
|
|
import { firstValueFrom } from 'rxjs';
|
|
import { IssuesApiService } from './issues-api.service';
|
|
|
|
export type IssueStatus = 'draft' | 'todo' | 'done' | 'in-progress';
|
|
export type IssuePriority = 'TRES_FAIBLE' | 'BASSE' | 'MOYENNE' | 'HAUTE' | 'TRES_HAUTE';
|
|
export type IssueType = 'Epic' | 'Bug' | 'Study' | 'Story' | 'Task' | 'Technical Story';
|
|
|
|
export type IssueComment = {
|
|
id: number;
|
|
text: string;
|
|
createdAt: string;
|
|
updatedAt: string | null;
|
|
linkedIssueIds: number[];
|
|
};
|
|
|
|
export type IssueEntity = {
|
|
id: number;
|
|
type: IssueType;
|
|
assignee: string;
|
|
epic: string;
|
|
name: string;
|
|
dueDate: string;
|
|
description: string;
|
|
estimatedTime: number | null;
|
|
dependsOnIds: number[];
|
|
comments: IssueComment[];
|
|
priority: IssuePriority;
|
|
status: IssueStatus;
|
|
progress: number;
|
|
};
|
|
|
|
@Injectable({ providedIn: 'root' })
|
|
export class IssuesStore {
|
|
private readonly api = inject(IssuesApiService);
|
|
private readonly data = signal<IssueEntity[]>([]);
|
|
|
|
readonly loading = signal(false);
|
|
readonly loaded = signal(false);
|
|
readonly issues = this.data.asReadonly();
|
|
|
|
getById(id: number): IssueEntity | undefined {
|
|
return this.data().find((i) => i.id === id);
|
|
}
|
|
|
|
getNextId(): number {
|
|
const ids = this.data().map((i) => i.id);
|
|
return ids.length === 0 ? 1 : Math.max(...ids) + 1;
|
|
}
|
|
|
|
async load(): Promise<void> {
|
|
if (this.loaded()) return;
|
|
this.loading.set(true);
|
|
try {
|
|
const issues = await firstValueFrom(this.api.getAll());
|
|
this.data.set(issues.map((i) => this.normalizeIssue(i)));
|
|
this.loaded.set(true);
|
|
} finally {
|
|
this.loading.set(false);
|
|
}
|
|
}
|
|
|
|
async upsert(issue: IssueEntity): Promise<IssueEntity> {
|
|
const normalized = this.normalizeIssue(issue);
|
|
if (!normalized.id) {
|
|
const { id: _id, ...body } = normalized;
|
|
const created = this.normalizeIssue(await firstValueFrom(this.api.create(body)));
|
|
this.data.update((issues) => [...issues, created]);
|
|
return created;
|
|
} else {
|
|
const apiResult = await firstValueFrom(this.api.update(normalized.id, normalized));
|
|
// L'API ne retourne pas linkedIssueIds dans les commentaires : on le restaure
|
|
// depuis les données envoyées pour ne pas perdre les liens.
|
|
if (Array.isArray(apiResult.comments) && Array.isArray(normalized.comments)) {
|
|
apiResult.comments = apiResult.comments.map((c: IssueComment) => {
|
|
if (Array.isArray(c.linkedIssueIds)) return c;
|
|
const sent = normalized.comments.find((nc) => nc.id === c.id);
|
|
return { ...c, linkedIssueIds: sent?.linkedIssueIds ?? [] };
|
|
});
|
|
}
|
|
const updated = this.normalizeIssue(apiResult);
|
|
this.data.update((issues) => {
|
|
const idx = issues.findIndex((i) => i.id === normalized.id);
|
|
if (idx === -1) return issues;
|
|
const copy = [...issues];
|
|
copy[idx] = updated;
|
|
return copy;
|
|
});
|
|
return updated;
|
|
}
|
|
}
|
|
|
|
async deleteById(id: number): Promise<void> {
|
|
await firstValueFrom(this.api.remove(id));
|
|
this.data.update((issues) =>
|
|
issues
|
|
.filter((i) => i.id !== id)
|
|
.map((i) => ({ ...i, dependsOnIds: i.dependsOnIds.filter((d) => d !== id) })),
|
|
);
|
|
}
|
|
|
|
private normalizeIssue(
|
|
issue: Partial<IssueEntity> & { 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,
|
|
type: issue.type ?? 'Story',
|
|
estimatedTime: issue.estimatedTime ?? null,
|
|
dependsOnIds: normalizedDependencies,
|
|
comments: Array.isArray(issue.comments)
|
|
? issue.comments.map((c) => ({
|
|
...c,
|
|
linkedIssueIds: Array.isArray(c.linkedIssueIds) ? c.linkedIssueIds : [],
|
|
}))
|
|
: [],
|
|
} as IssueEntity;
|
|
}
|
|
}
|