diff --git a/backend/LexBoxApi/GraphQL/ProjectMutations.cs b/backend/LexBoxApi/GraphQL/ProjectMutations.cs index f2e11e9b8..e6e01ec5d 100644 --- a/backend/LexBoxApi/GraphQL/ProjectMutations.cs +++ b/backend/LexBoxApi/GraphQL/ProjectMutations.cs @@ -285,6 +285,27 @@ public async Task> SetProjectConfidentiality(SetProjectConfi return dbContext.Projects.Where(p => p.Id == input.ProjectId); } + [Error] + [Error] + [UseMutationConvention] + [UseFirstOrDefault] + [UseProjection] + public async Task> SetRetentionPolicy( + SetRetentionPolicyInput input, + IPermissionService permissionService, + [Service] ProjectService projectService, + LexBoxDbContext dbContext) + { + await permissionService.AssertCanManageProject(input.ProjectId); + var project = await dbContext.Projects.FindAsync(input.ProjectId); + NotFoundException.ThrowIfNull(project); + + project.RetentionPolicy = input.RetentionPolicy; + project.UpdateUpdatedDate(); + await dbContext.SaveChangesAsync(); + return dbContext.Projects.Where(p => p.Id == input.ProjectId); + } + [Error] [Error] [UseMutationConvention] diff --git a/backend/LexBoxApi/Models/Project/ChangeProjectInputs.cs b/backend/LexBoxApi/Models/Project/ChangeProjectInputs.cs index d6fed8c71..f0dd80786 100644 --- a/backend/LexBoxApi/Models/Project/ChangeProjectInputs.cs +++ b/backend/LexBoxApi/Models/Project/ChangeProjectInputs.cs @@ -1,3 +1,5 @@ +using LexCore.Entities; + namespace LexBoxApi.Models.Project; public record ChangeProjectNameInput(Guid ProjectId, string Name); @@ -6,6 +8,8 @@ public record ChangeProjectDescriptionInput(Guid ProjectId, string Description); public record SetProjectConfidentialityInput(Guid ProjectId, bool IsConfidential); +public record SetRetentionPolicyInput(Guid ProjectId, RetentionPolicy RetentionPolicy); + public record DeleteUserByAdminOrSelfInput(Guid UserId); public record ResetProjectByAdminInput(string Code); diff --git a/frontend/schema.graphql b/frontend/schema.graphql index 946d97f73..fd54a402f 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -236,6 +236,7 @@ type Mutation { changeProjectName(input: ChangeProjectNameInput!): ChangeProjectNamePayload! changeProjectDescription(input: ChangeProjectDescriptionInput!): ChangeProjectDescriptionPayload! setProjectConfidentiality(input: SetProjectConfidentialityInput!): SetProjectConfidentialityPayload! + setRetentionPolicy(input: SetRetentionPolicyInput!): SetRetentionPolicyPayload! leaveProject(input: LeaveProjectInput!): LeaveProjectPayload! removeProjectMember(input: RemoveProjectMemberInput!): RemoveProjectMemberPayload! deleteDraftProject(input: DeleteDraftProjectInput!): DeleteDraftProjectPayload! @authorize(policy: "AdminRequiredPolicy") @@ -442,6 +443,11 @@ type SetProjectConfidentialityPayload { errors: [SetProjectConfidentialityError!] } +type SetRetentionPolicyPayload { + project: Project + errors: [SetRetentionPolicyError!] +} + type SetUserLockedPayload { user: User errors: [SetUserLockedError!] @@ -538,6 +544,8 @@ union SetOrgMemberRoleError = DbError | NotFoundError | OrgMemberInvitedByEmail union SetProjectConfidentialityError = NotFoundError | DbError +union SetRetentionPolicyError = NotFoundError | DbError + union SetUserLockedError = NotFoundError union SoftDeleteProjectError = NotFoundError | DbError @@ -943,6 +951,11 @@ input SetProjectConfidentialityInput { isConfidential: Boolean! } +input SetRetentionPolicyInput { + projectId: UUID! + retentionPolicy: RetentionPolicy! +} + input SetUserLockedInput { userId: UUID! locked: Boolean! diff --git a/frontend/src/lib/components/modals/FormModal.svelte b/frontend/src/lib/components/modals/FormModal.svelte index e9e51f7bf..071b986f1 100644 --- a/frontend/src/lib/components/modals/FormModal.svelte +++ b/frontend/src/lib/components/modals/FormModal.svelte @@ -57,12 +57,12 @@ const response = await openModal(onSubmit); const _formState = $formState; // we need to read the form state before the modal closes or it will be reset if (response !== DialogResponse.Submit || !options?.keepOpenOnSubmit) - modal.close(); + modal?.close(); return { response, formState: _formState }; } export function close(): void { - modal.close(); + modal?.close(); } export function form(): Readable { diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index caedf6a73..3778e28fa 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -430,6 +430,11 @@ the [Linguistics Institute at Payap University](https://li.payap.ac.th/) in Chia "author_header": "Author", "log_header": "Message" }, + "add_purpose": { + "add_button": "Add Purpose", + "modal_title": "Choose purpose", + "notify_success": "You have successfully added a project purpose." + }, "get_project": { "instructions_header": "To {isEmpty, select, true { set up } other { get } } this project with {type, select, FL_EX { FLEx } WE_SAY { WeSay } other { (e.g.) FLEx } } {mode, select, manual { manually} other { }}", "instructions_flex": "1. In the \"Send/Receive\" menu click \"Get Project from colleague…\"\n\ diff --git a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte index ecd9ef8de..98699793a 100644 --- a/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte +++ b/frontend/src/routes/(authenticated)/project/[project_code]/+page.svelte @@ -29,7 +29,7 @@ import MoreSettings from '$lib/components/MoreSettings.svelte'; import { AdminContent, PageBreadcrumb } from '$lib/layout'; import Markdown from 'svelte-exmarkdown'; - import { OrgRole, ProjectRole, ProjectType, ResetStatus } from '$lib/gql/generated/graphql'; + import { OrgRole, ProjectRole, ProjectType, ResetStatus, RetentionPolicy } from '$lib/gql/generated/graphql'; import Icon from '$lib/icons/Icon.svelte'; import OpenInFlexModal from './OpenInFlexModal.svelte'; import OpenInFlexButton from './OpenInFlexButton.svelte'; @@ -45,6 +45,7 @@ import DetailsPage from '$lib/layout/DetailsPage.svelte'; import OrgList from './OrgList.svelte'; import AddOrganization from './AddOrganization.svelte'; + import AddPurpose from './AddPurpose.svelte'; import WritingSystemList from '$lib/components/Projects/WritingSystemList.svelte'; export let data: PageData; @@ -306,9 +307,15 @@ - - - + {#if project.retentionPolicy === RetentionPolicy.Unknown} + {#if canManage} + + {/if} + {:else} + + + + {/if} {#if project.resetStatus === ResetStatus.InProgress}