Skip to content

Commit

Permalink
Set project purpose on project page (#969)
Browse files Browse the repository at this point in the history
If a project does not have a purpose / retention policy, then a button
will be shown on the project page that allows setting its purpose.
  • Loading branch information
psh0078 authored Jul 24, 2024
1 parent 2105dbb commit 1930cfd
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 9 deletions.
21 changes: 21 additions & 0 deletions backend/LexBoxApi/GraphQL/ProjectMutations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,27 @@ public async Task<IQueryable<Project>> SetProjectConfidentiality(SetProjectConfi
return dbContext.Projects.Where(p => p.Id == input.ProjectId);
}

[Error<NotFoundException>]
[Error<DbError>]
[UseMutationConvention]
[UseFirstOrDefault]
[UseProjection]
public async Task<IQueryable<Project>> 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<NotFoundException>]
[Error<LastMemberCantLeaveException>]
[UseMutationConvention]
Expand Down
4 changes: 4 additions & 0 deletions backend/LexBoxApi/Models/Project/ChangeProjectInputs.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using LexCore.Entities;

namespace LexBoxApi.Models.Project;

public record ChangeProjectNameInput(Guid ProjectId, string Name);
Expand All @@ -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);
13 changes: 13 additions & 0 deletions frontend/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -442,6 +443,11 @@ type SetProjectConfidentialityPayload {
errors: [SetProjectConfidentialityError!]
}

type SetRetentionPolicyPayload {
project: Project
errors: [SetRetentionPolicyError!]
}

type SetUserLockedPayload {
user: User
errors: [SetUserLockedError!]
Expand Down Expand Up @@ -538,6 +544,8 @@ union SetOrgMemberRoleError = DbError | NotFoundError | OrgMemberInvitedByEmail

union SetProjectConfidentialityError = NotFoundError | DbError

union SetRetentionPolicyError = NotFoundError | DbError

union SetUserLockedError = NotFoundError

union SoftDeleteProjectError = NotFoundError | DbError
Expand Down Expand Up @@ -943,6 +951,11 @@ input SetProjectConfidentialityInput {
isConfidential: Boolean!
}

input SetRetentionPolicyInput {
projectId: UUID!
retentionPolicy: RetentionPolicy!
}

input SetUserLockedInput {
userId: UUID!
locked: Boolean!
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/modals/FormModal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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<FormType> {
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/lib/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -306,9 +307,15 @@
<BadgeList>
<ProjectConfidentialityBadge on:click={projectConfidentialityModal.openModal} {canManage} isConfidential={project.isConfidential ?? undefined} />
<ProjectTypeBadge type={project.type} />
<Badge>
<FormatRetentionPolicy policy={project.retentionPolicy} />
</Badge>
{#if project.retentionPolicy === RetentionPolicy.Unknown}
{#if canManage}
<AddPurpose projectId={project.id} />
{/if}
{:else}
<Badge>
<FormatRetentionPolicy policy={project.retentionPolicy} />
</Badge>
{/if}
{#if project.resetStatus === ResetStatus.InProgress}
<button
class:tooltip={user.isAdmin}
Expand Down Expand Up @@ -384,9 +391,7 @@
</svelte:fragment>

<div class="space-y-4">
<OrgList
organizations={project.organizations}
>
<OrgList organizations={project.organizations} >
<svelte:fragment slot="extraButtons">
{#if canManage}
<AddOrganization projectId={project.id} userIsAdmin={user.isAdmin} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type {
ProjectPageQuery,
SetProjectConfidentialityInput,
SetProjectConfidentialityMutation,
SetRetentionPolicyInput,
SetRetentionPolicyMutation,
} from '$lib/gql/types';
import { getClient, graphql } from '$lib/gql';

Expand Down Expand Up @@ -368,6 +370,30 @@ export async function _setProjectConfidentiality(input: SetProjectConfidentialit
return result;
}

export async function _setRetentionPolicy(input: SetRetentionPolicyInput): $OpResult<SetRetentionPolicyMutation> {
//language=GraphQL
const result = await getClient()
.mutation(
graphql(`
mutation SetRetentionPolicy($input: SetRetentionPolicyInput!) {
setRetentionPolicy(input: $input) {
project {
id
retentionPolicy
}
errors {
... on Error {
message
}
}
}
}
`),
{ input: input }
);
return result;
}

export async function _deleteProjectUser(projectId: string, userId: string): $OpResult<DeleteProjectUserMutation> {
const result = await getClient()
.mutation(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts">
import { z } from 'zod';
import t from '$lib/i18n';
import { useNotifications } from '$lib/notify';
import { BadgeButton } from '$lib/components/Badges';
import { Select } from '$lib/forms';
import { AdminContent } from '$lib/layout';
import { RetentionPolicy } from '$lib/gql/types';
import { _setRetentionPolicy } from './+page';
import { DialogResponse, FormModal } from '$lib/components/modals';
export let projectId: string;
const schema = z.object({
retentionPolicy: z.nativeEnum(RetentionPolicy).default(RetentionPolicy.Training)
});
type Schema = typeof schema;
let formModal: FormModal<Schema>;
$: form = formModal?.form();
const { notifySuccess } = useNotifications();
async function openModal(): Promise<void> {
const { response, formState } = await formModal.open(async () => {
const { error } = await _setRetentionPolicy({
projectId,
retentionPolicy: $form.retentionPolicy,
})
if (error?.byType('NotFoundError')) {
if (error.message === 'Project not found') return $t('project_page.add_org.project_not_found');
}
});
if (response === DialogResponse.Submit && formState.retentionPolicy.currentValue) {
notifySuccess($t('project_page.add_purpose.notify_success'));
}
}
</script>

<BadgeButton variant="badge-success" icon="i-mdi-plus" on:click={openModal}>
{$t('project_page.add_purpose.add_button')}
</BadgeButton>

<FormModal bind:this={formModal} {schema} let:errors>
<span slot="title">{$t('project_page.add_purpose.modal_title')}</span>
<Select
id="policy"
label={$t('project.create.retention_policy')}
bind:value={$form.retentionPolicy}
error={errors.retentionPolicy}
>
<option value={RetentionPolicy.Verified}>{$t('retention_policy.language_project')}</option>
<option value={RetentionPolicy.Training}>{$t('retention_policy.training')}</option>
<option value={RetentionPolicy.Test}>{$t('retention_policy.test')}</option>
<AdminContent>
<option value={RetentionPolicy.Dev}>{$t('retention_policy.dev')}</option>
</AdminContent>
</Select>
<span slot="submitText">{'Add Purpose'}</span>
</FormModal>

0 comments on commit 1930cfd

Please sign in to comment.