diff --git a/backend/api/GQL/Mutation.cs b/backend/api/GQL/Mutation.cs index 05cb0263..34441767 100644 --- a/backend/api/GQL/Mutation.cs +++ b/backend/api/GQL/Mutation.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using HotChocolate.AspNetCore.Authorization; @@ -397,12 +398,12 @@ string projectCategoryId } [Authorize(Roles = new[] { adminRole })] - public QuestionTemplate RemoveFromProjectCategory( + public QuestionTemplate RemoveFromProjectCategories( string questionTemplateId, - string projectCategoryId + List projectCategoryIds ) { - return _questionTemplateService.RemoveFromProjectCategory(questionTemplateId, projectCategoryId); + return _questionTemplateService.RemoveFromProjectCategories(questionTemplateId, projectCategoryIds); } /* Helpers */ diff --git a/backend/api/Services/QuestionTemplateService.cs b/backend/api/Services/QuestionTemplateService.cs index 8b675cce..4342c4a3 100644 --- a/backend/api/Services/QuestionTemplateService.cs +++ b/backend/api/Services/QuestionTemplateService.cs @@ -181,9 +181,9 @@ string projectCategoryId return template; } - public QuestionTemplate RemoveFromProjectCategory( + public QuestionTemplate RemoveFromProjectCategories( string questionTemplateId, - string projectCategoryId + List projectCategoryIds ) { var template = _context.QuestionTemplates @@ -191,17 +191,21 @@ string projectCategoryId .Single(x => x.Id == questionTemplateId) ; - var projectCategory = _context.ProjectCategories - .Single(x => x.Id == projectCategoryId) - ; - - if (!template.ProjectCategories.Contains(projectCategory)) + foreach (var id in projectCategoryIds) { - string msg = "QuestionTemplate is not in ProjectCategory"; - throw new Exception(msg); + var projectCategory = _context.ProjectCategories + .Single(x => x.Id == id) + ; + + if (!template.ProjectCategories.Contains(projectCategory)) + { + string msg = "QuestionTemplate is not in ProjectCategory"; + throw new Exception(msg); + } + + template.ProjectCategories.Remove(projectCategory); } - template.ProjectCategories.Remove(projectCategory); _context.SaveChanges(); return template; } diff --git a/backend/tests/Services/QuestionTemplateService.cs b/backend/tests/Services/QuestionTemplateService.cs index 5470e63b..6a7fa481 100644 --- a/backend/tests/Services/QuestionTemplateService.cs +++ b/backend/tests/Services/QuestionTemplateService.cs @@ -309,19 +309,51 @@ public void RemoveFromProjectCategory() var nActive = service.ActiveQuestions(projectCategory).Count(); var nTemplates = service.GetAll().Count(); - var updatedQT = service.RemoveFromProjectCategory(template.Id, projectCategory.Id); - var updatedSP = projectCategoryService.Get(projectCategory.Id); + var updatedQT = service.RemoveFromProjectCategories(template.Id, new List {projectCategory.Id}); + var updatedPC = projectCategoryService.Get(projectCategory.Id); - Assert.False(updatedQT.ProjectCategories.Contains(updatedSP)); - Assert.False(updatedSP.QuestionTemplates.Contains(updatedQT)); + Assert.False(updatedQT.ProjectCategories.Contains(updatedPC)); + Assert.False(updatedPC.QuestionTemplates.Contains(updatedQT)); Assert.Equal(nActive - 1, service.ActiveQuestions(projectCategory).Count()); Assert.Equal(nTemplates, service.GetAll().Count()); /* Removing the same QuestionTemplate should fail */ Assert.Throws(() => - service.RemoveFromProjectCategory(template.Id, projectCategory.Id) + service.RemoveFromProjectCategories(template.Id, new List {projectCategory.Id}) ); } + + [Fact] + public void RemoveFromSeveralProjectCategories() + { + var projectCategoryService = new ProjectCategoryService(fixture.context); + var projectCategory1 = projectCategoryService.Create(Randomize.String()); + var projectCategory2 = projectCategoryService.Create(Randomize.String()); + var projectCategoryIds = new List {projectCategory1.Id, projectCategory2.Id}; + + var service = new QuestionTemplateService(fixture.context); + var template = service.GetAll().First(); + + service.AddToProjectCategory(template.Id, projectCategory1.Id); + service.AddToProjectCategory(template.Id, projectCategory2.Id); + + var nActiveFirstCategory = service.ActiveQuestions(projectCategory1).Count(); + var nActiveSecondCategory = service.ActiveQuestions(projectCategory2).Count(); + var nTemplates = service.GetAll().Count(); + + var updatedQT = service.RemoveFromProjectCategories(template.Id, projectCategoryIds); + var updatedPC1 = projectCategoryService.Get(projectCategory1.Id); + var updatedPC2 = projectCategoryService.Get(projectCategory2.Id); + + Assert.False(updatedQT.ProjectCategories.Contains(updatedPC1)); + Assert.False(updatedQT.ProjectCategories.Contains(updatedPC2)); + Assert.False(updatedPC1.QuestionTemplates.Contains(updatedQT)); + Assert.False(updatedPC2.QuestionTemplates.Contains(updatedQT)); + + Assert.Equal(nActiveFirstCategory - 1, service.ActiveQuestions(projectCategory1).Count()); + Assert.Equal(nActiveSecondCategory - 1, service.ActiveQuestions(projectCategory2).Count()); + Assert.Equal(nTemplates, service.GetAll().Count()); + } } } diff --git a/frontend/src/api/models.ts b/frontend/src/api/models.ts index 455ca006..19f9d811 100644 --- a/frontend/src/api/models.ts +++ b/frontend/src/api/models.ts @@ -362,7 +362,7 @@ export type Mutation = { deleteQuestionTemplate?: Maybe; reorderQuestionTemplate?: Maybe; addToProjectCategory?: Maybe; - removeFromProjectCategory?: Maybe; + removeFromProjectCategories?: Maybe; }; @@ -511,9 +511,9 @@ export type MutationAddToProjectCategoryArgs = { }; -export type MutationRemoveFromProjectCategoryArgs = { +export type MutationRemoveFromProjectCategoriesArgs = { questionTemplateId?: Maybe; - projectCategoryId?: Maybe; + projectCategoryIds?: Maybe>>; }; export type Note = { diff --git a/frontend/src/views/Admin/StaticQuestionItem.tsx b/frontend/src/views/Admin/StaticQuestionItem.tsx index dc4ca6d4..8b4067f8 100644 --- a/frontend/src/views/Admin/StaticQuestionItem.tsx +++ b/frontend/src/views/Admin/StaticQuestionItem.tsx @@ -83,10 +83,10 @@ const StaticQuestionItem = ({ } = useAddToProjectCategoryMutation() const { - removeFromProjectCategory, + removeFromProjectCategories, loading: removingFromProjectCategory, error: removingFromProjectCategoryError, - } = useRemoveFromProjectCategoryMutation() + } = useRemoveFromProjectCategoriesMutation() const { deleteQuestionTemplate, @@ -167,21 +167,32 @@ const StaticQuestionItem = ({ const updateProjectCategories = (selection: string[] | undefined) => { if (selection) { - const addedItemInSelection = checkIfAddedCategory(selection) - const removedItemInSelection = checkIfRemovedCategory(selection) - - if (addedItemInSelection) { - addToProjectCategory({ - questionTemplateId: question.id, - projectCategoryId: addedItemInSelection.id, + if (selection.length === 0) { + const ids: string[] = [] + question.projectCategories.forEach(questionProjectCategory => { + ids.push(questionProjectCategory.id) }) - } - - if (removedItemInSelection) { - removeFromProjectCategory({ + removeFromProjectCategories({ questionTemplateId: question.id, - projectCategoryId: removedItemInSelection.id, + projectCategoryIds: ids, }) + } else { + const addedItemInSelection = checkIfAddedCategory(selection) + const removedItemInSelection = checkIfRemovedCategory(selection) + + if (addedItemInSelection) { + addToProjectCategory({ + questionTemplateId: question.id, + projectCategoryId: addedItemInSelection.id, + }) + } + + if (removedItemInSelection) { + removeFromProjectCategories({ + questionTemplateId: question.id, + projectCategoryIds: [removedItemInSelection.id], + }) + } } } } @@ -313,13 +324,13 @@ const StaticQuestionItem = ({ export default StaticQuestionItem -export interface DataToAddToOrRemoveFromProjectCategory { +export interface DataToAddToProjectCategory { questionTemplateId: string projectCategoryId: string } interface AddToProjectCategoryMutationProps { - addToProjectCategory: (data: DataToAddToOrRemoveFromProjectCategory) => void + addToProjectCategory: (data: DataToAddToProjectCategory) => void loading: boolean error: ApolloError | undefined } @@ -354,7 +365,7 @@ const useAddToProjectCategoryMutation = (): AddToProjectCategoryMutationProps => }, }) - const addToProjectCategory = (data: DataToAddToOrRemoveFromProjectCategory) => { + const addToProjectCategory = (data: DataToAddToProjectCategory) => { addToProjectCategoryApolloFunc({ variables: { ...data }, }) @@ -367,16 +378,21 @@ const useAddToProjectCategoryMutation = (): AddToProjectCategoryMutationProps => } } -interface RemoveFromProjectCategoryMutationProps { - removeFromProjectCategory: (data: DataToAddToOrRemoveFromProjectCategory) => void +export interface DataToRemoveFromProjectCategories { + questionTemplateId: string + projectCategoryIds: string[] +} + +interface RemoveFromProjectCategoriesMutationProps { + removeFromProjectCategories: (data: DataToRemoveFromProjectCategories) => void loading: boolean error: ApolloError | undefined } -const useRemoveFromProjectCategoryMutation = (): RemoveFromProjectCategoryMutationProps => { - const REMOVE_FROM_PROJECT_CATEGORY = gql` - mutation RemoveFromProjectCategory($questionTemplateId: String!, $projectCategoryId: String!) { - removeFromProjectCategory(questionTemplateId: $questionTemplateId, projectCategoryId: $projectCategoryId) { +const useRemoveFromProjectCategoriesMutation = (): RemoveFromProjectCategoriesMutationProps => { + const REMOVE_FROM_PROJECT_CATEGORIES = gql` + mutation RemoveFromProjectCategories($questionTemplateId: String!, $projectCategoryIds: [String]!) { + removeFromProjectCategories(questionTemplateId: $questionTemplateId, projectCategoryIds: $projectCategoryIds) { id projectCategories { id @@ -386,31 +402,16 @@ const useRemoveFromProjectCategoryMutation = (): RemoveFromProjectCategoryMutati } ` - const [removeFromProjectCategoryApolloFunc, { loading, data, error }] = useMutation(REMOVE_FROM_PROJECT_CATEGORY, { - update(cache, mutationResult) { - const questionTemplateRemovedFrom = mutationResult.data.removeFromProjectCategory - cache.modify({ - id: cache.identify({ - __typename: 'QuestionTemplate', - id: questionTemplateRemovedFrom.id, - }), - fields: { - projectCategories() { - return questionTemplateRemovedFrom.projectCategories - }, - }, - }) - }, - }) + const [removeFromProjectCategoriesApolloFunc, { loading, data, error }] = useMutation(REMOVE_FROM_PROJECT_CATEGORIES) - const removeFromProjectCategory = (data: DataToAddToOrRemoveFromProjectCategory) => { - removeFromProjectCategoryApolloFunc({ + const removeFromProjectCategories = (data: DataToRemoveFromProjectCategories) => { + removeFromProjectCategoriesApolloFunc({ variables: { ...data }, }) } return { - removeFromProjectCategory, + removeFromProjectCategories, loading, error, }