Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add lapse application button #155

Merged
merged 2 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/api/private.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ export const applicationStartFn = async (id: number) => {
return response.data;
};

export const applicationLapseFn = async (id: number) => {
const response = await authApi.post<IApplication>(`applications/${id}/lapse`);
return response.data;
};

export const verifyDataFieldFn = async (awardData: IUpdateBorrower) => {
const { application_id, ...payload } = awardData;
const response = await authApi.put<IApplication>(`applications/${application_id}/verify-data-field`, payload);
Expand Down
3 changes: 3 additions & 0 deletions src/assets/icons/approve.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/assets/icons/lapse.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/assets/icons/reject.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions src/hooks/useLapseApplication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { type UseMutateFunction, useMutation, useQueryClient } from "@tanstack/react-query";
import { useT } from "@transifex/react";
import axios from "axios";
import { useSnackbar } from "notistack";
import { useNavigate } from "react-router-dom";

import { applicationLapseFn } from "../api/private";
import { DISPATCH_ACTIONS, QUERY_KEYS } from "../constants";
import type { IApplication } from "../schemas/application";
import useApplicationContext from "./useSecureApplicationContext";

type IUseLapseApplication = {
lapseApplicationMutation: UseMutateFunction<IApplication, unknown, number, unknown>;
isLoading: boolean;
};

export default function useLapseApplication(): IUseLapseApplication {
const t = useT();
const navigate = useNavigate();
const queryClient = useQueryClient();
const applicationContext = useApplicationContext();
const { enqueueSnackbar } = useSnackbar();

const { mutate: lapseApplicationMutation, isLoading } = useMutation<IApplication, unknown, number, unknown>(
(id) => applicationLapseFn(id),
{
onSuccess: (data) => {
queryClient.setQueryData([QUERY_KEYS.applications, data.id], data);
applicationContext.dispatch({ type: DISPATCH_ACTIONS.SET_APPLICATION, payload: data });
navigate(`/applications/${data.id}/stage-five-lapsed`);
},
onError: (error) => {
if (axios.isAxiosError(error) && error.response) {
if (error.response.data?.detail) {
enqueueSnackbar(t("Error: {error}", { error: error.response.data.detail }), {
variant: "error",
});
}
} else {
enqueueSnackbar(t("Error lapsing the application. {error}", { error }), {
variant: "error",
});
}
},
},
);

return { lapseApplicationMutation, isLoading };
}
2 changes: 1 addition & 1 deletion src/layout/SecureApplicationLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function SecureApplicationLayout() {

if (lastSegment !== "view") {
if (application.status === APPLICATION_STATUS.LAPSED) {
navigate("./view");
if (lastSegment !== "stage-five-lapsed") navigate("./stage-five-lapsed");
} else if (application.status === APPLICATION_STATUS.APPROVED) {
if (lastSegment !== "application-completed") navigate("./application-completed");
} else if (application.status === APPLICATION_STATUS.REJECTED) {
Expand Down
61 changes: 61 additions & 0 deletions src/pages/fi/LapseApplicationDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Box, Dialog } from "@mui/material";
import { useT } from "@transifex/react";
import useApplicationContext from "src/hooks/useSecureApplicationContext";
import Button from "src/stories/button/Button";
import Title from "src/stories/title/Title";

import useLapseApplication from "src/hooks/useLapseApplication";

export interface LapseApplicationDialogProps {
open: boolean;
handleClose: () => void;
}

export function LapseApplicationDialog({ open, handleClose }: LapseApplicationDialogProps) {
const t = useT();
const applicationContext = useApplicationContext();
const application = applicationContext.state.data;
const { isLoading, lapseApplicationMutation } = useLapseApplication();

const rootElement = document.getElementById("root-app");

return (
<Dialog fullWidth maxWidth="sm" container={rootElement} open={open} onClose={handleClose}>
<Box component="form" className="flex flex-col py-7 px-8" autoComplete="off">
<Title type="section" label={t("Confirm you want to mark this application as lapsed")} className="mb-4" />

<div>
{t(
"Are you sure you want to mark this application as lapsed? This action can't be undone. The application won't be listed in your application list anymore and you won't be able to approve or reject the application.",
)}
</div>

<div className="mt-4 grid grid-cols-1 gap-4 md:flex md:justify-end md:gap-0">
<div>
<Button
primary={false}
disabled={isLoading}
className="md:mr-4"
label={t("Cancel")}
onClick={handleClose}
/>
</div>

<div>
<Button
label={t("Lapse")}
disabled={isLoading}
onClick={() => {
if (application?.id) {
lapseApplicationMutation(application?.id);
}
}}
/>
</div>
</div>
</Box>
</Dialog>
);
}

export default LapseApplicationDialog;
37 changes: 29 additions & 8 deletions src/pages/fi/StageFive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import FormInput from "src/stories/form-input/FormInput";
import Text from "src/stories/text/Text";
import Title from "src/stories/title/Title";

import CheckChecked from "../../assets/icons/check-checked.svg";
import WarnRed from "../../assets/icons/warn-red.svg";
import Approve from "../../assets/icons/approve.svg";
import Lapse from "../../assets/icons/lapse.svg";
import Reject from "../../assets/icons/reject.svg";
import CreditProductReview from "../../components/CreditProductReview";
import useApproveApplication from "../../hooks/useApproveApplication";
import useLangContext from "../../hooks/useLangContext";
import { type ApproveApplicationInput, type FormApprovedInput, approveSchema } from "../../schemas/application";
import LapseApplicationDialog from "./LapseApplicationDialog";
import RejectApplicationDialog from "./RejectApplicationDialog";

export function StageFive() {
Expand All @@ -27,6 +29,7 @@ export function StageFive() {
const applicationContext = useApplicationContext();
const application = applicationContext.state.data;
const [openDialog, setOpenDialog] = useState<boolean>(false);
const [openLapseDialog, setOpenLapseDialog] = useState<boolean>(false);

const langContext = useLangContext();
const StepImage = langContext.state.selected.startsWith("en") ? StepImageEN : StepImageES;
Expand All @@ -41,6 +44,14 @@ export function StageFive() {
setOpenDialog(true);
};

const handleCloseLapse = () => {
setOpenLapseDialog(false);
};

const onLapseApplication = () => {
setOpenLapseDialog(true);
};

const methods = useForm<FormApprovedInput>({
resolver: zodResolver(approveSchema),
});
Expand Down Expand Up @@ -110,7 +121,7 @@ export function StageFive() {
type="currency"
placeholder={t("Credit amount")}
/>
<div className="mt-6 md:mb-8 grid grid-cols-1 gap-4 md:flex md:gap-0">
<div className="mt-6 md:mb-8 grid grid-cols-1 gap-5 md:flex md:gap-0">
<div>
<Button primary={false} className="md:mr-4" label={t("Go Home")} onClick={onGoHomeHandler} />
</div>
Expand All @@ -119,18 +130,27 @@ export function StageFive() {
<Button primary={false} className="md:mr-4" label={t("Go Back")} onClick={onGoBackHandler} />
</div>

<div>
<Button className="md:mr-4" icon={Approve} label={t("Approve")} type="submit" disabled={isLoading} />
</div>

<div>
<Button
className="md:mr-4"
icon={CheckChecked}
label={t("Approve")}
type="submit"
label={t("Reject")}
icon={Reject}
onClick={onRejectApplication}
disabled={isLoading}
/>
</div>

<div>
<Button label={t("Reject")} icon={WarnRed} onClick={onRejectApplication} disabled={isLoading} />
<Button
className="md:mr-4"
label={t("Lapse")}
icon={Lapse}
onClick={onLapseApplication}
disabled={isLoading}
/>
</div>
</div>
<Text className="mb-10 text-m font-light">
Expand All @@ -141,6 +161,7 @@ export function StageFive() {
</Box>
</FormProvider>
<RejectApplicationDialog open={openDialog} handleClose={handleClose} />
<LapseApplicationDialog open={openLapseDialog} handleClose={handleCloseLapse} />
</>
);
}
Expand Down
45 changes: 45 additions & 0 deletions src/pages/fi/StageFiveLapsed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useT } from "@transifex/react";
import { useNavigate } from "react-router-dom";
import StepImageEN from "src/assets/pages/en/stage-five.svg";
import StepImageES from "src/assets/pages/es/stage-five.svg";
import useApplicationContext from "src/hooks/useSecureApplicationContext";
import Button from "src/stories/button/Button";
import Text from "src/stories/text/Text";
import Title from "src/stories/title/Title";

import useLangContext from "../../hooks/useLangContext";

export function StageFiveLapsed() {
const t = useT();
const navigate = useNavigate();
const applicationContext = useApplicationContext();
const application = applicationContext.state.data;

const langContext = useLangContext();
const StepImage = langContext.state.selected.startsWith("en") ? StepImageEN : StepImageES;

const onGoHomeHandler = () => {
navigate("/");
};

return (
<>
<Title type="page" label={t("Application Approval Process")} className="mb-4" />
<Text className="text-lg mb-12">{application?.borrower.legal_name}</Text>
<img className="mb-14 ml-8" src={StepImage} alt="step" />
<Title type="section" label={t("Stage 5: Approve")} className="mb-8" />

<Text className="mb-8">
{t("The credit application has been lapsed. You won't see this application in your application list anymore.")}
</Text>

<div className="mt-6 md:mb-8 grid grid-cols-1 gap-4 md:flex md:gap-0">
<div>
<Button className="md:mr-4" label={t("Back to home")} onClick={onGoHomeHandler} />
</div>
</div>
</>
);
}

export default StageFiveLapsed;
4 changes: 2 additions & 2 deletions src/pages/fi/StageFiveRejected.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Title from "src/stories/title/Title";

import useLangContext from "../../hooks/useLangContext";

export function StageFiveApproved() {
export function StageFiveRejected() {
const t = useT();
const navigate = useNavigate();
const applicationContext = useApplicationContext();
Expand Down Expand Up @@ -42,4 +42,4 @@ export function StageFiveApproved() {
);
}

export default StageFiveApproved;
export default StageFiveRejected;
5 changes: 5 additions & 0 deletions src/routes/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import SecureApplicationContextProvider from "src/providers/SecureApplicationCon
import StateContextProvider from "src/providers/StateContextProvider";
import ProtectedRoute from "src/routes/ProtectedRoute";

import StageFiveLapsed from "src/pages/fi/StageFiveLapsed";
import { USER_TYPES } from "../constants";
import PageLayout from "../layout/PageLayout";
import SecureApplicationLayout from "../layout/SecureApplicationLayout";
Expand Down Expand Up @@ -364,6 +365,10 @@ const router = createBrowserRouter([
path: "stage-five-rejected",
element: <StageFiveRejected />,
},
{
path: "stage-five-lapsed",
element: <StageFiveLapsed />,
},
{
path: "application-completed",
element: <ApplicationCompleted />,
Expand Down
Loading