Skip to content

Commit

Permalink
[TM-1536] add delayed job progress alert (#716)
Browse files Browse the repository at this point in the history
* [TM-1536] add delayed job progress alert

* [TM-1536] add disabled and styles to check and fix buttons

* [TM-1536] add abort signal to delayed job progress alert

* [TM-1536] add default value to progress message
  • Loading branch information
LimberHope authored Dec 2, 2024
1 parent 82b52c5 commit f8e2854
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 24 deletions.
80 changes: 80 additions & 0 deletions src/admin/components/Alerts/DelayedJobsProgressAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Alert, AlertTitle, CircularProgress } from "@mui/material";
import { FC, useEffect, useState } from "react";
import { useStore } from "react-redux";

import ApiSlice from "@/store/apiSlice";
import { AppStore } from "@/store/store";

type DelayedJobsProgressAlertProps = {
show: boolean;
title?: string;
setIsLoadingDelayedJob?: (value: boolean) => void;
};

const DelayedJobsProgressAlert: FC<DelayedJobsProgressAlertProps> = ({ show, title, setIsLoadingDelayedJob }) => {
const [delayedJobProcessing, setDelayedJobProcessing] = useState<number>(0);
const [delayedJobTotal, setDalayedJobTotal] = useState<number>(0);
const [proccessMessage, setProccessMessage] = useState<string>("Running 0 out of 0 polygons (0%)");

const store = useStore<AppStore>();
useEffect(() => {
let intervalId: any;
if (show) {
intervalId = setInterval(() => {
const { total_content, processed_content, proccess_message } = store.getState().api;
setDalayedJobTotal(total_content);
setDelayedJobProcessing(processed_content);
if (proccess_message != "") {
setProccessMessage(proccess_message);
}
}, 1000);
}

return () => {
if (intervalId) {
setDelayedJobProcessing(0);
setDalayedJobTotal(0);
setProccessMessage("Running 0 out of 0 polygons (0%)");
clearInterval(intervalId);
}
};
}, [show]);

const abortDelayedJob = () => {
ApiSlice.abortDelayedJob(true);
ApiSlice.addTotalContent(0);
ApiSlice.addProgressContent(0);
ApiSlice.addProgressMessage("Running 0 out of 0 polygons (0%)");
setDelayedJobProcessing(0);
setDalayedJobTotal(0);
setIsLoadingDelayedJob?.(false);
};

if (!show) return null;

const calculatedProgress = delayedJobTotal! > 0 ? Math.round((delayedJobProcessing! / delayedJobTotal!) * 100) : 0;

const severity = calculatedProgress >= 75 ? "success" : calculatedProgress >= 50 ? "info" : "warning";

return (
<div className="fixed bottom-5 left-0 z-50 flex w-full items-center justify-center">
<Alert
severity={severity}
icon={<CircularProgress size={18} color="inherit" />}
action={
<button
onClick={abortDelayedJob}
className="hover:bg-red-300 ml-2 rounded px-2 py-1 text-sm font-medium text-red-200"
>
Cancel
</button>
}
>
<AlertTitle>{title}</AlertTitle>
{proccessMessage ?? "Running 0 out of 0 polygons (0%)"}
</Alert>
</div>
);
};

export default DelayedJobsProgressAlert;
6 changes: 6 additions & 0 deletions src/admin/components/ResourceTabs/PolygonReviewTab/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ import SitePolygonStatus from "./components/SitePolygonStatus/SitePolygonStatus"
interface IProps extends Omit<TabProps, "label" | "children"> {
type: EntityName;
label: string;
setIsLoadingDelayedJob?: (isLoading: boolean) => void;
isLoadingDelayedJob?: boolean;
setAlertTitle?: (value: string) => void;
}
export interface IPolygonItem {
id: string;
Expand Down Expand Up @@ -656,6 +659,9 @@ const PolygonReviewTab: FC<IProps> = props => {
tooltipType="edit"
sitePolygonData={sitePolygonData}
modelFilesData={modelFilesData?.data}
setIsLoadingDelayedJob={props.setIsLoadingDelayedJob}
isLoadingDelayedJob={props.isLoadingDelayedJob}
setAlertTitle={props.setAlertTitle}
/>
<div className="mb-6">
<div className="mb-4">
Expand Down
19 changes: 17 additions & 2 deletions src/admin/modules/sites/components/SiteShow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { FC } from "react";
import { FC, useState } from "react";
import { Show, TabbedShowLayout } from "react-admin";

import ShowActions from "@/admin/components/Actions/ShowActions";
import DelayedJobsProgressAlert from "@/admin/components/Alerts/DelayedJobsProgressAlert";
import AuditLogTab from "@/admin/components/ResourceTabs/AuditLogTab/AuditLogTab";
import { AuditLogButtonStates } from "@/admin/components/ResourceTabs/AuditLogTab/constants/enum";
import ChangeRequestsTab from "@/admin/components/ResourceTabs/ChangeRequestsTab/ChangeRequestsTab";
Expand All @@ -14,6 +15,9 @@ import { RecordFrameworkProvider } from "@/context/framework.provider";
import { MapAreaProvider } from "@/context/mapArea.provider";

const SiteShow: FC = () => {
const [isLoadingDelayedJob, setIsLoadingDelayedJob] = useState(false);
const [alertTitle, setAlertTitle] = useState("");

return (
<Show
title={<ShowTitle moduleName="Site" getTitle={record => record?.name} />}
Expand All @@ -25,7 +29,13 @@ const SiteShow: FC = () => {
<InformationTab type="sites" />
<TabbedShowLayout.Tab label="Polygon Review">
<MapAreaProvider>
<PolygonReviewTab label="" type={"sites"} />
<PolygonReviewTab
label=""
type={"sites"}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
isLoadingDelayedJob={isLoadingDelayedJob!}
setAlertTitle={setAlertTitle!}
/>
</MapAreaProvider>
</TabbedShowLayout.Tab>
<GalleryTab label="Site Gallery" entity="sites" />
Expand All @@ -35,6 +45,11 @@ const SiteShow: FC = () => {
<AuditLogTab entity={AuditLogButtonStates.SITE} />
</TabbedShowLayout>
</RecordFrameworkProvider>
<DelayedJobsProgressAlert
show={isLoadingDelayedJob}
title={alertTitle}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
/>
</Show>
);
};
Expand Down
21 changes: 19 additions & 2 deletions src/components/elements/Map-mapbox/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ interface MapProps extends Omit<DetailedHTMLProps<HTMLAttributes<HTMLDivElement>
role?: any;
selectedCountry?: string | null;
setLoader?: (value: boolean) => void;
setIsLoadingDelayedJob?: (value: boolean) => void;
isLoadingDelayedJob?: boolean;
setAlertTitle?: (value: string) => void;
showViewGallery?: boolean;
legendPosition?: ControlMapPosition;
}
Expand Down Expand Up @@ -158,6 +161,9 @@ export const MapContainer = ({
centroids,
listViewProjects,
showImagesButton,
setIsLoadingDelayedJob,
isLoadingDelayedJob,
setAlertTitle,
showViewGallery = true,
legendPosition,
...props
Expand Down Expand Up @@ -548,7 +554,12 @@ export const MapContainer = ({
</When>
<When condition={selectedPolygonsInCheckbox.length}>
<ControlGroup position={siteData ? "top-centerSite" : "top-centerPolygonsInCheckbox"}>
<ProcessBulkPolygonsControl entityData={record} />
<ProcessBulkPolygonsControl
entityData={record}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
isLoadingDelayedJob={isLoadingDelayedJob!}
setAlertTitle={setAlertTitle!}
/>
</ControlGroup>
</When>
<When condition={isDashboard !== "dashboard"}>
Expand All @@ -561,7 +572,13 @@ export const MapContainer = ({
</ControlGroup>
<When condition={!!record?.uuid && validationType === "bulkValidation"}>
<ControlGroup position={siteData ? "top-left-site" : "top-left"}>
<CheckPolygonControl siteRecord={record} polygonCheck={!siteData} />
<CheckPolygonControl
siteRecord={record}
polygonCheck={!siteData}
setIsLoadingDelayedJob={setIsLoadingDelayedJob!}
isLoadingDelayedJob={isLoadingDelayedJob!}
setAlertTitle={setAlertTitle!}
/>
</ControlGroup>
</When>
<When condition={formMap}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
usePostV2TerrafundValidationSitePolygons
} from "@/generated/apiComponents";
import { ClippedPolygonResponse, SitePolygon } from "@/generated/apiSchemas";
import ApiSlice from "@/store/apiSlice";
import Log from "@/utils/log";

import Button from "../../Button/Button";
Expand All @@ -32,6 +33,9 @@ export interface CheckSitePolygonProps {
uuid: string;
};
polygonCheck: boolean;
setIsLoadingDelayedJob?: (isLoading: boolean) => void;
isLoadingDelayedJob?: boolean;
setAlertTitle?: (value: string) => void;
}

interface CheckedPolygon {
Expand All @@ -50,7 +54,7 @@ interface TransformedData {
}

const CheckPolygonControl = (props: CheckSitePolygonProps) => {
const { siteRecord, polygonCheck } = props;
const { siteRecord, polygonCheck, setIsLoadingDelayedJob, isLoadingDelayedJob, setAlertTitle } = props;
const siteUuid = siteRecord?.uuid;
const [openCollapse, setOpenCollapse] = useState(false);
const [sitePolygonCheckData, setSitePolygonCheckData] = useState<TransformedData[]>([]);
Expand All @@ -59,7 +63,7 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {
const context = useSitePolygonData();
const sitePolygonData = context?.sitePolygonData;
const sitePolygonRefresh = context?.reloadSiteData;
const { showLoader, hideLoader } = useLoading();
const { hideLoader } = useLoading();
const { setShouldRefetchValidation, setShouldRefetchPolygonData, setSelectedPolygonsInCheckbox } =
useMapAreaContext();
const { openModal, closeModal } = useModalContext();
Expand Down Expand Up @@ -92,11 +96,29 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {
"success",
t("Success! TerraMatch reviewed all polygons")
);
setIsLoadingDelayedJob?.(false);
ApiSlice.addTotalContent(0);
ApiSlice.addProgressContent(0);
ApiSlice.addProgressMessage("");
},
onError: () => {
hideLoader();
setIsLoadingDelayedJob?.(false);
setClickedValidation(false);
displayNotification(t("Please try again later."), "error", t("Error! TerraMatch could not review polygons"));
if (ApiSlice.apiDataStore.abort_delayed_job) {
displayNotification(
t("The Check Polygons processing was cancelled."),
"warning",
t("You can try again later.")
);

ApiSlice.abortDelayedJob(false);
ApiSlice.addTotalContent(0);
ApiSlice.addProgressContent(0);
ApiSlice.addProgressMessage("");
} else {
displayNotification(t("Please try again later."), "error", t("Error! TerraMatch could not review polygons"));
}
}
});

Expand All @@ -105,6 +127,7 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {
if (!data.updated_polygons?.length) {
openNotification("warning", t("No polygon have been fixed"), t("Please run 'Check Polygons' again."));
hideLoader();
setIsLoadingDelayedJob?.(false);
closeModal(ModalId.FIX_POLYGONS);
return;
}
Expand All @@ -118,13 +141,23 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {
.join(", ");
openNotification("success", t("Success! The following polygons have been fixed:"), updatedPolygonNames);
hideLoader();
setIsLoadingDelayedJob?.(false);
}
closeModal(ModalId.FIX_POLYGONS);
},
onError: error => {
Log.error("Error clipping polygons:", error);
displayNotification(t("An error occurred while fixing polygons. Please try again."), "error", t("Error"));
if (ApiSlice.apiDataStore.abort_delayed_job) {
displayNotification(t("The Fix Polygons processing was cancelled."), "warning", t("You can try again later."));
ApiSlice.abortDelayedJob(false);
ApiSlice.addTotalContent(0);
ApiSlice.addProgressContent(0);
ApiSlice.addProgressMessage("");
} else {
Log.error("Error clipping polygons:", error);
displayNotification(t("An error occurred while fixing polygons. Please try again."), "error", t("Error"));
}
hideLoader();
setIsLoadingDelayedJob?.(false);
}
});

Expand Down Expand Up @@ -167,7 +200,8 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {

const runFixPolygonOverlaps = () => {
if (siteUuid) {
showLoader();
setIsLoadingDelayedJob?.(true);
setAlertTitle?.("Fix Polygons");
clipPolygons({ pathParams: { uuid: siteUuid } });
} else {
displayNotification(t("Cannot fix polygons: Site UUID is missing."), "error", t("Error"));
Expand Down Expand Up @@ -216,7 +250,8 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {

useEffect(() => {
if (clickedValidation) {
showLoader();
setIsLoadingDelayedJob?.(true);
setAlertTitle?.("Check Polygons");
getValidations({ queryParams: { uuid: siteUuid ?? "" } });
}
}, [clickedValidation]);
Expand All @@ -226,20 +261,24 @@ const CheckPolygonControl = (props: CheckSitePolygonProps) => {
<div className="rounded-lg bg-[#ffffff26] p-3 text-center text-white backdrop-blur-md">
<Button
variant="text"
className="text-10-bold my-2 flex w-full justify-center rounded-lg border border-tertiary-600 bg-tertiary-600 p-2 hover:border-white"
className="text-10-bold my-2 flex w-full justify-center rounded-lg border border-tertiary-600 bg-tertiary-600 p-2 hover:border-white
disabled:cursor-not-allowed disabled:opacity-60"
onClick={() => {
setClickedValidation(true);
setOpenCollapse(true);
setSelectedPolygonsInCheckbox([]);
}}
disabled={isLoadingDelayedJob}
>
{polygonCheck ? t("Check Polygons") : t("Check All Polygons")}
</Button>
<When condition={hasOverlaps}>
<Button
variant="text"
className="text-10-bold my-2 flex w-full justify-center rounded-lg border border-white bg-white p-2 text-darkCustom-100 hover:border-primary"
className="text-10-bold my-2 flex w-full justify-center rounded-lg border border-white bg-white p-2 text-darkCustom-100 hover:border-primary
disabled:cursor-not-allowed disabled:opacity-60"
onClick={openFormModalHandlerSubmitPolygon}
disabled={isLoadingDelayedJob}
>
{t("Fix Polygons")}
</Button>
Expand Down
Loading

0 comments on commit f8e2854

Please sign in to comment.