feat: archive a project instead of deleting it
The owner archives a finished project (archiveProject, POST
/projects/{project}/archive) and brings it back (unarchive). While
archived it stays readable, managers still read its rules and webhooks,
but no command changes it: contributing and managing checks refuse with
422, votes and public proposals too, and automations do not run. The web
shows a read-only notice on every page of it (with «Desarchivar» for the
owner), hides what edits, marks it on the home page and lists it last.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ export const projectPageEvents = eventGroup({
|
||||
analyticsRequested: type<{ projectId: string; days: number }>(),
|
||||
burnupRequested: type<{ releaseId: string }>(),
|
||||
visibilityChangeRequested: type<{ projectId: string; isPublic: boolean }>(),
|
||||
archiveRequested: type<{ projectId: string; archived: boolean }>(),
|
||||
proposalsChangeRequested: type<{
|
||||
projectId: string;
|
||||
acceptsProposals: boolean;
|
||||
|
||||
@@ -162,6 +162,12 @@ export class ProjectFacade {
|
||||
this.dispatch.visibilityChangeRequested({ projectId, isPublic });
|
||||
}
|
||||
|
||||
/** Archives the project (read-only for everyone) or brings it back. Owner only. */
|
||||
setArchived(archived: boolean): void {
|
||||
const projectId = this.projectId();
|
||||
if (projectId) this.dispatch.archiveRequested({ projectId, archived });
|
||||
}
|
||||
|
||||
/** Take bug reports and ideas from anyone (while public). Owners and admins. */
|
||||
setAcceptsProposals(acceptsProposals: boolean): void {
|
||||
const projectId = this.projectId();
|
||||
|
||||
@@ -100,11 +100,17 @@ export const ProjectStore = signalStore(
|
||||
);
|
||||
return {
|
||||
myRole,
|
||||
/** Archived projects are read-only for everyone. */
|
||||
canManage: computed(
|
||||
() => myRole() === ProjectRole.OWNER || myRole() === ProjectRole.ADMIN,
|
||||
() =>
|
||||
!project()?.archivedAt &&
|
||||
(myRole() === ProjectRole.OWNER || myRole() === ProjectRole.ADMIN),
|
||||
),
|
||||
canContribute: computed(
|
||||
() => !!myRole() && myRole() !== ProjectRole.VIEWER,
|
||||
() =>
|
||||
!project()?.archivedAt &&
|
||||
!!myRole() &&
|
||||
myRole() !== ProjectRole.VIEWER,
|
||||
),
|
||||
members: computed(() => project()?.members ?? []),
|
||||
/** Statuses, types and areas of the project, with lookups. */
|
||||
@@ -433,6 +439,20 @@ export const ProjectStore = signalStore(
|
||||
),
|
||||
),
|
||||
),
|
||||
events
|
||||
.on(projectPageEvents.archiveRequested)
|
||||
.pipe(
|
||||
mergeMap(({ payload }) =>
|
||||
runCommand(
|
||||
projects.setArchived(payload.projectId, payload.archived),
|
||||
payload.archived
|
||||
? 'Proyecto archivado: queda en solo lectura'
|
||||
: 'Proyecto desarchivado',
|
||||
projectApiEvents.commandSucceeded,
|
||||
projectApiEvents.commandFailed,
|
||||
),
|
||||
),
|
||||
),
|
||||
events
|
||||
.on(projectPageEvents.proposalsChangeRequested)
|
||||
.pipe(
|
||||
|
||||
@@ -39,8 +39,13 @@ export const ProjectsStore = signalStore(
|
||||
error: null,
|
||||
}),
|
||||
withComputed(({ projects }) => ({
|
||||
/** By name, archived ones last. */
|
||||
sorted: computed(() =>
|
||||
[...projects()].sort((a, b) => a.name.localeCompare(b.name)),
|
||||
[...projects()].sort(
|
||||
(a, b) =>
|
||||
Number(!!a.archivedAt) - Number(!!b.archivedAt) ||
|
||||
a.name.localeCompare(b.name),
|
||||
),
|
||||
),
|
||||
})),
|
||||
withReducer(
|
||||
|
||||
Reference in New Issue
Block a user