From 57a16d3fd909f65dc5359ef808ccbc6f77192fc6 Mon Sep 17 00:00:00 2001 From: Hayden Cleary <5160414+haydencleary@users.noreply.github.com> Date: Thu, 14 Nov 2024 11:11:08 +0100 Subject: [PATCH] B 2764 add mutation to cancel application (#639) --- .../application/client/index.ts | 1 + .../client/use-delete-application.ts | 30 +++++++++++++++++++ .../application/application-contract.types.ts | 10 +++++++ .../input/application-facade-port.ts | 3 ++ .../outputs/application-storage-port.ts | 3 ++ .../adapters/application-client-adapter.ts | 27 ++++++++++++++++- .../application-client-adapter-mock.ts | 2 ++ .../_features/footer/footer.hooks.tsx | 27 ++++++++++++++++- .../contributions-sidepanel.en.json | 6 ++++ 9 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 core/application/react-query-adapter/application/client/use-delete-application.ts diff --git a/core/application/react-query-adapter/application/client/index.ts b/core/application/react-query-adapter/application/client/index.ts index 2a998fee..c7c71b14 100644 --- a/core/application/react-query-adapter/application/client/index.ts +++ b/core/application/react-query-adapter/application/client/index.ts @@ -1,3 +1,4 @@ export * from "./use-accept-application"; export * from "./use-get-application-by-id"; export * from "./use-patch-application"; +export * from "./use-delete-application"; diff --git a/core/application/react-query-adapter/application/client/use-delete-application.ts b/core/application/react-query-adapter/application/client/use-delete-application.ts new file mode 100644 index 00000000..20ce0154 --- /dev/null +++ b/core/application/react-query-adapter/application/client/use-delete-application.ts @@ -0,0 +1,30 @@ +import { useMutation } from "@tanstack/react-query"; + +import { + UseMutationFacadeParams, + useMutationAdapter, +} from "@/core/application/react-query-adapter/helpers/use-mutation-adapter"; +import { bootstrap } from "@/core/bootstrap"; +import { DeleteApplicationBody } from "@/core/domain/application/application-contract.types"; +import { ApplicationFacadePort } from "@/core/domain/application/input/application-facade-port"; + +export function useDeleteApplication({ + pathParams, + options, +}: UseMutationFacadeParams) { + const applicationStoragePort = bootstrap.getApplicationStoragePortForClient(); + + return useMutation( + useMutationAdapter({ + ...applicationStoragePort.deleteApplication({ pathParams }), + options: { + ...options, + onSuccess: async (data, variables, context) => { + // TODO invalidate application list query + + options?.onSuccess?.(data, variables, context); + }, + }, + }) + ); +} diff --git a/core/domain/application/application-contract.types.ts b/core/domain/application/application-contract.types.ts index 1050c9f0..77f4c07d 100644 --- a/core/domain/application/application-contract.types.ts +++ b/core/domain/application/application-contract.types.ts @@ -31,3 +31,13 @@ export type GetApplicationByIdPortParams = HttpClientParameters<{ PathParams: Ge export type GetApplicationByIdResponse = components["schemas"]["ProjectApplicationResponse"]; export type GetApplicationByIdPortResponse = HttpStorageResponse; + +/* ----------------------------- Delete application ---------------------------- */ + +export type DeleteApplicationBody = components["schemas"]["ProjectApplicationDeleteRequest"]; + +export type DeleteApplicationPathParams = operations["deleteProjectApplication"]["parameters"]["path"]; + +export type DeleteApplicationPortParams = HttpClientParameters<{ PathParams: DeleteApplicationPathParams }>; + +export type DeleteApplicationPortResponse = HttpStorageResponse; diff --git a/core/domain/application/input/application-facade-port.ts b/core/domain/application/input/application-facade-port.ts index b70c7ad7..c6c1a1ac 100644 --- a/core/domain/application/input/application-facade-port.ts +++ b/core/domain/application/input/application-facade-port.ts @@ -1,6 +1,8 @@ import { AcceptApplicationPortParams, AcceptApplicationPortResponse, + DeleteApplicationPortParams, + DeleteApplicationPortResponse, GetApplicationByIdPortParams, GetApplicationByIdPortResponse, PatchApplicationPortParams, @@ -11,4 +13,5 @@ export interface ApplicationFacadePort { patchApplication(p: PatchApplicationPortParams): PatchApplicationPortResponse; acceptApplication(p: AcceptApplicationPortParams): AcceptApplicationPortResponse; getApplicationById(p: GetApplicationByIdPortParams): GetApplicationByIdPortResponse; + deleteApplication(p: DeleteApplicationPortParams): DeleteApplicationPortResponse; } diff --git a/core/domain/application/outputs/application-storage-port.ts b/core/domain/application/outputs/application-storage-port.ts index 5825ebb6..41ac9145 100644 --- a/core/domain/application/outputs/application-storage-port.ts +++ b/core/domain/application/outputs/application-storage-port.ts @@ -1,6 +1,8 @@ import { AcceptApplicationPortParams, AcceptApplicationPortResponse, + DeleteApplicationPortParams, + DeleteApplicationPortResponse, GetApplicationByIdPortParams, GetApplicationByIdPortResponse, PatchApplicationPortParams, @@ -12,4 +14,5 @@ export interface ApplicationStoragePort { patchApplication(p: PatchApplicationPortParams): PatchApplicationPortResponse; acceptApplication(p: AcceptApplicationPortParams): AcceptApplicationPortResponse; getApplicationById(p: GetApplicationByIdPortParams): GetApplicationByIdPortResponse; + deleteApplication(p: DeleteApplicationPortParams): DeleteApplicationPortResponse; } diff --git a/core/infrastructure/marketplace-api-client-adapter/adapters/application-client-adapter.ts b/core/infrastructure/marketplace-api-client-adapter/adapters/application-client-adapter.ts index 9044db99..ffe29fb3 100644 --- a/core/infrastructure/marketplace-api-client-adapter/adapters/application-client-adapter.ts +++ b/core/infrastructure/marketplace-api-client-adapter/adapters/application-client-adapter.ts @@ -1,4 +1,8 @@ -import { GetApplicationByIdResponse, PatchApplicationBody } from "@/core/domain/application/application-contract.types"; +import { + DeleteApplicationBody, + GetApplicationByIdResponse, + PatchApplicationBody, +} from "@/core/domain/application/application-contract.types"; import { Application } from "@/core/domain/application/model/application-model"; import { ApplicationStoragePort } from "@/core/domain/application/outputs/application-storage-port"; import { HttpClient } from "@/core/infrastructure/marketplace-api-client-adapter/http/http-client/http-client"; @@ -11,6 +15,7 @@ export class ApplicationClientAdapter implements ApplicationStoragePort { patchApplication: "applications/:applicationId", acceptApplication: "applications/:applicationId/accept", getApplicationById: "applications/:applicationId", + deleteApplication: "applications/:applicationId", } as const; patchApplication = ({ pathParams }: FirstParameter) => { @@ -72,4 +77,24 @@ export class ApplicationClientAdapter implements ApplicationStoragePort { tag, }; }; + + deleteApplication = ({ pathParams }: FirstParameter) => { + const path = this.routes["deleteApplication"]; + const method = "DELETE"; + const tag = HttpClient.buildTag({ path, pathParams }); + + const request = async (body: DeleteApplicationBody) => + this.client.request({ + path, + method, + tag, + pathParams, + body: JSON.stringify(body), + }); + + return { + request, + tag, + }; + }; } diff --git a/core/infrastructure/marketplace-api-client-adapter/mock-adapters/application-client-adapter-mock.ts b/core/infrastructure/marketplace-api-client-adapter/mock-adapters/application-client-adapter-mock.ts index a7609f8c..3dd814cd 100644 --- a/core/infrastructure/marketplace-api-client-adapter/mock-adapters/application-client-adapter-mock.ts +++ b/core/infrastructure/marketplace-api-client-adapter/mock-adapters/application-client-adapter-mock.ts @@ -11,4 +11,6 @@ export class ApplicationClientAdapterMock implements ApplicationStoragePort { acceptApplication = mockHttpStorageResponse; getApplicationById = mockHttpStorageResponse; + + deleteApplication = mockHttpStorageResponse; } diff --git a/shared/panels/contribution-sidepanel/_features/footer/footer.hooks.tsx b/shared/panels/contribution-sidepanel/_features/footer/footer.hooks.tsx index acef8dc9..ebe1e0e8 100644 --- a/shared/panels/contribution-sidepanel/_features/footer/footer.hooks.tsx +++ b/shared/panels/contribution-sidepanel/_features/footer/footer.hooks.tsx @@ -1,13 +1,17 @@ import { useState } from "react"; +import { ApplicationReactQueryAdapter } from "@/core/application/react-query-adapter/application"; import { ContributionAs } from "@/core/domain/contribution/models/contribution.types"; import { Button } from "@/design-system/atoms/button/variants/button-default"; import { Tooltip } from "@/design-system/atoms/tooltip"; import { CheckboxButton } from "@/design-system/molecules/checkbox-button"; +import { toast } from "@/design-system/molecules/toaster"; import { BaseLink } from "@/shared/components/base-link/base-link"; import { useGithubPermissionsContext } from "@/shared/features/github-permissions/github-permissions.context"; +import { useSidePanelsContext } from "@/shared/features/side-panels/side-panels.context"; +import { useAuthUser } from "@/shared/hooks/auth/use-auth-user"; import { useContributionActions } from "@/shared/hooks/contributions/use-contribution-actions"; import { UseContributionPanelFooter } from "@/shared/panels/contribution-sidepanel/_features/footer/footer.types"; import { Translate } from "@/shared/translation/components/translate/translate"; @@ -87,13 +91,33 @@ export const useContributionPanelFooterAsMaintainer = ({ export const useContributionPanelFooterAsContributor = ({ contribution }: UseContributionPanelFooter) => { const [shouldDeleteComment, setShouldDeleteComment] = useState(false); + const { close } = useSidePanelsContext(); + + const { githubUserId } = useAuthUser(); + + const applicationId = + contribution.applicants.find(applicant => applicant.githubUserId === githubUserId)?.applicationId ?? ""; + + // TODO handle Github permissions + const { mutate, isPending } = ApplicationReactQueryAdapter.client.useDeleteApplication({ + pathParams: { applicationId }, + options: { + onSuccess: () => { + close(); + toast.success(); + }, + onError: () => { + toast.error(); + }, + }, + }); if (contribution.isArchived()) { return
; } function onCancelApplication() { - console.log(`Should cancel : true & should delete comment : ${shouldDeleteComment}`); + mutate({ deleteGithubComment: shouldDeleteComment }); } if (contribution?.isNotAssigned()) { @@ -111,6 +135,7 @@ export const useContributionPanelFooterAsContributor = ({ contribution }: UseCon size={"md"} variant={"secondary"} onClick={onCancelApplication} + isLoading={isPending} translate={{ token: "panels:contribution.footer.actions.asContributor.cancelApplication" }} />
diff --git a/shared/panels/contribution-sidepanel/contributions-sidepanel.en.json b/shared/panels/contribution-sidepanel/contributions-sidepanel.en.json index 16bc30d0..ffc33339 100644 --- a/shared/panels/contribution-sidepanel/contributions-sidepanel.en.json +++ b/shared/panels/contribution-sidepanel/contributions-sidepanel.en.json @@ -54,6 +54,12 @@ "cancelApplication": "Cancel application", "seePrOnGithub": "See PR on GitHub" } + }, + "tooltip": { + "cancelApplication": { + "success": "Application canceled successfully.", + "error": "An error occurred while canceling the application." + } } }, "helper": {