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:
@@ -219,6 +219,27 @@ export class ProjectResolver {
|
||||
return this.projects.delete(me.id, projectId, confirmKey);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandResultGql, {
|
||||
description:
|
||||
'Archive the project (owner only): everything stays readable, nothing changes until it is unarchived',
|
||||
})
|
||||
archiveProject(
|
||||
@CurrentUser() me: User,
|
||||
@Args('projectId', { type: () => ID }) projectId: string,
|
||||
) {
|
||||
return this.projects.setArchived(me.id, projectId, true);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandResultGql, {
|
||||
description: 'Bring an archived project back (owner only)',
|
||||
})
|
||||
unarchiveProject(
|
||||
@CurrentUser() me: User,
|
||||
@Args('projectId', { type: () => ID }) projectId: string,
|
||||
) {
|
||||
return this.projects.setArchived(me.id, projectId, false);
|
||||
}
|
||||
|
||||
@Mutation(() => CommandResultGql, {
|
||||
description: 'Add a Pireon user to the project',
|
||||
})
|
||||
|
||||
@@ -137,6 +137,11 @@ export class ProjectGql {
|
||||
@Field(() => UserRefGql) owner!: UserRefGql;
|
||||
@Field(() => [ProjectMemberGql]) members!: ProjectMemberGql[];
|
||||
@Field() createdAt!: string;
|
||||
@Field(() => String, {
|
||||
nullable: true,
|
||||
description: 'Archived since then (read-only); null while active',
|
||||
})
|
||||
archivedAt!: string | null;
|
||||
@Field() updatedAt!: string;
|
||||
@Field(() => Int) version!: number;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user