From 3a1f8b8fd52b93225beb310ad30510ab435f2ce0 Mon Sep 17 00:00:00 2001 From: Marwen Abid Date: Mon, 6 Jan 2025 17:14:08 -0800 Subject: [PATCH] SDP-1453 Delete Draft Disbursement --- src/api/deleteDisbursementDraft.ts | 20 +++++++ src/pages/DisbursementDraftDetails.tsx | 75 +++++++++++++++++++++++++- src/store/ducks/disbursementDrafts.ts | 40 ++++++++++++++ src/types/index.ts | 2 +- 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 src/api/deleteDisbursementDraft.ts diff --git a/src/api/deleteDisbursementDraft.ts b/src/api/deleteDisbursementDraft.ts new file mode 100644 index 0000000..c076a25 --- /dev/null +++ b/src/api/deleteDisbursementDraft.ts @@ -0,0 +1,20 @@ +import { handleApiResponse } from "api/handleApiResponse"; +import { API_URL } from "constants/envVariables"; +import { getSdpTenantName } from "helpers/getSdpTenantName"; +import { ApiDisbursement } from "types"; + +export const deleteDisbursementDraft = async ( + token: string, + draftId: string, +): Promise => { + const response = await fetch(`${API_URL}/disbursements/${draftId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${token}`, + "SDP-Tenant-Name": getSdpTenantName(), + }, + }); + + return handleApiResponse(response); +}; diff --git a/src/pages/DisbursementDraftDetails.tsx b/src/pages/DisbursementDraftDetails.tsx index 78bb1fb..b38eed0 100644 --- a/src/pages/DisbursementDraftDetails.tsx +++ b/src/pages/DisbursementDraftDetails.tsx @@ -1,6 +1,14 @@ import { useCallback, useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; -import { Badge, Heading, Link, Notification } from "@stellar/design-system"; +import { + Badge, + Heading, + Link, + Notification, + Button, + Icon, + Modal, +} from "@stellar/design-system"; import { useDispatch } from "react-redux"; import { useRedux } from "hooks/useRedux"; import { useDownloadCsvFile } from "hooks/useDownloadCsvFile"; @@ -15,6 +23,7 @@ import { import { clearCsvUpdatedAction, clearDisbursementDraftsErrorAction, + deleteDisbursementDraftAction, resetDisbursementDraftsAction, saveNewCsvFileAction, setDraftIdAction, @@ -61,6 +70,8 @@ export const DisbursementDraftDetails = () => { const [isResponseSuccess, setIsResponseSuccess] = useState(false); const [futureBalance, setFutureBalance] = useState(0); + const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); + const dispatch: AppDispatch = useDispatch(); const navigate = useNavigate(); const { isLoading: csvDownloadIsLoading } = useDownloadCsvFile( @@ -259,6 +270,31 @@ export const DisbursementDraftDetails = () => { ); }; + const handleDeleteDraft = ( + event: React.MouseEvent, + ) => { + event.preventDefault(); + if (draftId) { + dispatch(deleteDisbursementDraftAction(draftId)); + setIsDeleteModalVisible(false); + navigate(Routes.DISBURSEMENT_DRAFTS); + } + }; + + const showDeleteModal = ( + event: React.MouseEvent, + ) => { + event.preventDefault(); + setIsDeleteModalVisible(true); + }; + + const hideDeleteModal = ( + event?: React.MouseEvent, + ) => { + event?.preventDefault(); + setIsDeleteModalVisible(false); + }; + const renderButtons = (variant: DisbursementStep) => { const canUserSubmit = organization.data.isApprovalRequired ? // If approval is required, a different user must submit the draft @@ -437,6 +473,15 @@ export const DisbursementDraftDetails = () => { > Changes saved + @@ -460,6 +505,34 @@ export const DisbursementDraftDetails = () => { ) : null} {renderContent()} + + + Delete draft permanently? + +
+ Clicking 'Delete draft' will permanently remove this draft and + cannot be undone. +
+
+ + + + +
); }; diff --git a/src/store/ducks/disbursementDrafts.ts b/src/store/ducks/disbursementDrafts.ts index dc90b4a..c6e9434 100644 --- a/src/store/ducks/disbursementDrafts.ts +++ b/src/store/ducks/disbursementDrafts.ts @@ -1,5 +1,6 @@ import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit"; import { RootState } from "store"; +import { deleteDisbursementDraft } from "api/deleteDisbursementDraft"; import { getDisbursementDrafts } from "api/getDisbursementDrafts"; import { postDisbursement } from "api/postDisbursement"; import { postDisbursementFile } from "api/postDisbursementFile"; @@ -223,6 +224,31 @@ export const submitDisbursementSavedDraftAction = createAsyncThunk< }, ); +export const deleteDisbursementDraftAction = createAsyncThunk< + void, + string, + { rejectValue: RejectMessage; state: RootState } +>( + "disbursementDrafts/deleteDisbursementDraftAction", + async (draftId, { rejectWithValue, getState, dispatch }) => { + const { token } = getState().userAccount; + + try { + await deleteDisbursementDraft(token, draftId); + refreshSessionToken(dispatch); + return; + } catch (error: unknown) { + const apiError = normalizeApiError(error as ApiError); + const errorString = apiError.message; + endSessionIfTokenInvalid(errorString, dispatch); + + return rejectWithValue({ + errorString: `Error deleting draft: ${errorString}`, + }); + } + }, +); + const initialState: DisbursementDraftsInitialState = { items: [], status: undefined, @@ -357,6 +383,20 @@ const disbursementDraftsSlice = createSlice({ state.newDraftId = action.payload?.newDraftId; }, ); + // Delete disbursement draft + builder.addCase(deleteDisbursementDraftAction.pending, (state) => { + state.status = "PENDING"; + state.actionType = "delete"; + }); + builder.addCase(deleteDisbursementDraftAction.fulfilled, (state) => { + state.status = "SUCCESS"; + state.actionType = "delete"; + }); + builder.addCase(deleteDisbursementDraftAction.rejected, (state, action) => { + state.status = "ERROR"; + state.actionType = "delete"; + state.errorString = action.payload?.errorString; + }); }, }); diff --git a/src/types/index.ts b/src/types/index.ts index b888e60..4fad2d7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -219,7 +219,7 @@ export const VerificationFieldMap: Record< NATIONAL_ID_NUMBER: "National ID Number", }; -export type DisbursementDraftAction = "save" | "submit"; +export type DisbursementDraftAction = "save" | "submit" | "delete"; export interface DisbursementInstructions { csvName?: string;