first commit

This commit is contained in:
2026-10-03 13:40:26 +02:00
parent 732a5b5a16
commit 566f415fa7
555 changed files with 62704 additions and 0 deletions

View File

@@ -0,0 +1,85 @@
import { inject, Injectable, Provider } from '@angular/core';
import { injectDispatch } from '@ngrx/signals/events';
import {
CreateReleaseInput,
ProjectRole,
} from '@task-stream/web/tracker/domain';
import { projectPageEvents } from '../events/project.events';
import { ProjectStore } from '../stores/project.store';
/**
* The open project for presenters: details, members, releases, stats and live
* activity. Commands act on the open project, so pages don't pass its id.
*/
@Injectable()
export class ProjectFacade {
private readonly store = inject(ProjectStore);
private readonly dispatch = injectDispatch(projectPageEvents);
readonly project = this.store.project;
readonly members = this.store.members;
readonly stats = this.store.stats;
readonly releases = this.store.releases;
readonly activity = this.store.activity;
/** Pireon directory results for `searchUsers()`. */
readonly userResults = this.store.userResults;
readonly loading = this.store.loading;
readonly error = this.store.error;
readonly myRole = this.store.myRole;
/** Owner or admin. */
readonly canManage = this.store.canManage;
/** Any role but viewer. */
readonly canContribute = this.store.canContribute;
/** Loads the project (by id or key) and follows its changes. */
open(idOrKey: string): void {
this.dispatch.opened({ idOrKey });
}
/** Searches Pireon's directory (from 2 characters, debounced). */
searchUsers(term: string): void {
const query = term.trim();
if (query.length >= 2) this.dispatch.userSearchRequested(query);
}
addMember(userId: string, role: ProjectRole = ProjectRole.MEMBER): void {
const projectId = this.projectId();
if (projectId)
this.dispatch.memberAddRequested({ projectId, userId, role });
}
changeMemberRole(userId: string, role: ProjectRole): void {
const projectId = this.projectId();
if (projectId)
this.dispatch.memberRoleChangeRequested({ projectId, userId, role });
}
removeMember(userId: string): void {
const projectId = this.projectId();
if (projectId) this.dispatch.memberRemoveRequested({ projectId, userId });
}
createRelease(release: Omit<CreateReleaseInput, 'projectId'>): void {
const projectId = this.projectId();
if (projectId)
this.dispatch.releaseCreateRequested({ projectId, ...release });
}
/** `force` publishes even with unfinished tickets. */
publishRelease(releaseId: string, force = false): void {
this.dispatch.releasePublishRequested({ releaseId, force });
}
archiveRelease(releaseId: string): void {
this.dispatch.releaseArchiveRequested({ releaseId });
}
private projectId(): string | null {
return this.store.project()?.id ?? null;
}
}
/** Store + facade of the open project, for the project route's component. */
export function provideProjectFacade(): Provider[] {
return [ProjectStore, ProjectFacade];
}