auth
This commit is contained in:
+44
-119
@@ -1,6 +1,6 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
const ISSUES_STORAGE_KEY = 'bonsai.issues';
|
||||
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 = 'Basse' | 'Moyenne' | 'Haute';
|
||||
@@ -29,108 +29,60 @@ export type IssueEntity = {
|
||||
progress: number;
|
||||
};
|
||||
|
||||
const DEFAULT_ISSUES: IssueEntity[] = [
|
||||
{
|
||||
id: 1,
|
||||
type: 'Bug',
|
||||
assignee: 'Marie',
|
||||
epic: 'EPIC-UI',
|
||||
name: 'Bug affichage menu mobile',
|
||||
dueDate: '2026-06-10',
|
||||
description: 'Corriger le comportement du menu sur petits ecrans.',
|
||||
estimatedTime: 8,
|
||||
dependsOnIds: [],
|
||||
comments: [],
|
||||
priority: 'Haute',
|
||||
status: 'in-progress',
|
||||
progress: 35,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
type: 'Study',
|
||||
assignee: 'Nabil',
|
||||
epic: 'EPIC-FORM',
|
||||
name: 'Erreur validation formulaire projet',
|
||||
dueDate: '2026-06-12',
|
||||
description: 'Fiabiliser les regles de validation du formulaire projet.',
|
||||
estimatedTime: 16,
|
||||
dependsOnIds: [],
|
||||
comments: [],
|
||||
priority: 'Moyenne',
|
||||
status: 'todo',
|
||||
progress: 20,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'Story',
|
||||
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.',
|
||||
estimatedTime: 4,
|
||||
dependsOnIds: [],
|
||||
comments: [],
|
||||
priority: 'Basse',
|
||||
status: 'done',
|
||||
progress: 100,
|
||||
},
|
||||
];
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IssuesStore {
|
||||
private readonly data = signal<IssueEntity[]>(DEFAULT_ISSUES);
|
||||
|
||||
constructor() {
|
||||
const cachedIssues = this.readFromStorage();
|
||||
if (cachedIssues) {
|
||||
this.data.set(cachedIssues.map((issue) => this.normalizeIssue(issue)));
|
||||
}
|
||||
}
|
||||
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((issue) => issue.id === id);
|
||||
return this.data().find((i) => i.id === id);
|
||||
}
|
||||
|
||||
getNextId(): number {
|
||||
const ids = this.data().map((issue) => issue.id);
|
||||
return ids.length > 0 ? Math.max(...ids) + 1 : 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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, normalizedIssue];
|
||||
this.persistToStorage(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
const updated = [...issues];
|
||||
updated[existingIndex] = normalizedIssue;
|
||||
this.persistToStorage(updated);
|
||||
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 updated = this.normalizeIssue(
|
||||
await firstValueFrom(this.api.update(normalized.id, normalized)),
|
||||
);
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
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(
|
||||
@@ -149,31 +101,4 @@ export class IssuesStore {
|
||||
comments: Array.isArray(issue.comments) ? issue.comments : [],
|
||||
} as IssueEntity;
|
||||
}
|
||||
|
||||
private readFromStorage(): IssueEntity[] | null {
|
||||
if (typeof window === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const rawIssues = window.localStorage.getItem(ISSUES_STORAGE_KEY);
|
||||
if (!rawIssues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(rawIssues);
|
||||
return Array.isArray(parsed) ? (parsed as IssueEntity[]) : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private persistToStorage(issues: IssueEntity[]): void {
|
||||
if (typeof window === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
window.localStorage.setItem(ISSUES_STORAGE_KEY, JSON.stringify(issues));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user