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:
2026-10-15 15:46:22 +02:00
parent e598f76e1c
commit 4acf629106
42 changed files with 1057 additions and 14 deletions

View File

@@ -128,6 +128,13 @@ export class ProjectDto {
@ApiProperty({ type: ProjectSettingsDto })
settings!: ProjectSettingsDto;
@ApiProperty({ format: 'date-time' }) createdAt!: string;
@ApiProperty({
type: String,
nullable: true,
format: 'date-time',
description: 'Archived since then (read-only); null while active',
})
archivedAt!: string | null;
@ApiProperty({ format: 'date-time' }) updatedAt!: string;
@ApiProperty({ description: 'Version of the aggregate (event count)' })
version!: number;

View File

@@ -5,6 +5,7 @@ import {
Get,
Param,
Patch,
HttpCode,
Post,
Put,
Query,
@@ -148,6 +149,38 @@ export class ProjectsController {
);
}
@Post(':project/archive')
@HttpCode(200)
@PROJECT
@ApiOperation({
summary: 'Archive a project (owner only)',
description:
'It stays readable (board, tickets, history, statistics); nothing can change until it is unarchived.',
})
@ApiOkResponse({ type: CommandResultDto })
@ApiCommandErrors()
async archive(@CurrentUser() me: User, @Param('project') project: string) {
return this.projects.setArchived(
me.id,
await this.refs.projectId(me.id, project),
true,
);
}
@Post(':project/unarchive')
@HttpCode(200)
@PROJECT
@ApiOperation({ summary: 'Bring an archived project back (owner only)' })
@ApiOkResponse({ type: CommandResultDto })
@ApiCommandErrors()
async unarchive(@CurrentUser() me: User, @Param('project') project: string) {
return this.projects.setArchived(
me.id,
await this.refs.projectId(me.id, project),
false,
);
}
@Put(':project/settings')
@PROJECT
@ApiOperation({