feature/IO-3702-ESPD-UI-AND-FIXES - Stage 4
This commit is contained in:
File diff suppressed because one or more lines are too long
21
package-lock.json
generated
21
package-lock.json
generated
@@ -27,6 +27,7 @@
|
||||
"@electron-toolkit/tsconfig": "^2.0.0",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@reduxjs/toolkit": "^2.11.2",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/lodash": "^4.17.24",
|
||||
@@ -5089,6 +5090,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/archiver": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-7.0.0.tgz",
|
||||
"integrity": "sha512-/3vwGwx9n+mCQdYZ2IKGGHEFL30I96UgBlk8EtRDDFQ9uxM1l4O5Ci6r00EMAkiDaTqD9DQ6nVrWRICnBPtzzg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/readdir-glob": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@@ -5375,6 +5386,16 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/readdir-glob": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz",
|
||||
"integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/redux-logger": {
|
||||
"version": "3.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/redux-logger/-/redux-logger-3.0.13.tgz",
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
"@electron-toolkit/tsconfig": "^2.0.0",
|
||||
"@playwright/test": "^1.58.2",
|
||||
"@reduxjs/toolkit": "^2.11.2",
|
||||
"@types/archiver": "^7.0.0",
|
||||
"@types/cors": "^2.8.19",
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/lodash": "^4.17.24",
|
||||
|
||||
@@ -5,6 +5,7 @@ import fs from "fs";
|
||||
import _ from "lodash";
|
||||
import path from "path";
|
||||
import errorTypeCheck from "../../util/errorTypeCheck";
|
||||
import ipcTypes from "../../util/ipcTypes.json";
|
||||
import { ScrubEstimate } from "../estimate-scrubber/estimate-scrubber";
|
||||
import store from "../store/store";
|
||||
import setAppProgressbar from "../util/setAppProgressBar";
|
||||
@@ -138,7 +139,9 @@ async function ImportJob(filepath: string): Promise<void> {
|
||||
console.log("Available Job record to upload;", newAvailableJob);
|
||||
|
||||
//Scrub the estimate
|
||||
const scrubPdfURL = await ScrubEstimate({ job: jobObject });
|
||||
const scrubResult = await ScrubEstimate({ job: jobObject });
|
||||
const scrubPdfURL = scrubResult?.pdfUrl;
|
||||
const scrubHistoryJobId = scrubResult?.jobId;
|
||||
|
||||
setAppProgressbar(0.95);
|
||||
const esApiKey = store.get("settings.esApiKey") as string;
|
||||
@@ -160,11 +163,27 @@ async function ImportJob(filepath: string): Promise<void> {
|
||||
],
|
||||
});
|
||||
|
||||
const openScrubHistoryItem = (): void => {
|
||||
const mainWindow = getMainWindow();
|
||||
if (!mainWindow || mainWindow.isDestroyed()) return;
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
mainWindow.show();
|
||||
mainWindow.focus();
|
||||
if (scrubHistoryJobId) {
|
||||
mainWindow.webContents.send(
|
||||
ipcTypes.toRenderer.scrub.openHistoryItem,
|
||||
{ jobId: scrubHistoryJobId },
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
uploadNotification.on("click", openScrubHistoryItem);
|
||||
|
||||
uploadNotification.on("action", (e) => {
|
||||
// e.actionIndex
|
||||
if (e.actionIndex === 0) {
|
||||
const mainWindow = getMainWindow();
|
||||
mainWindow?.show();
|
||||
openScrubHistoryItem();
|
||||
} else if (e.actionIndex === 1) {
|
||||
if (scrubPdfURL) {
|
||||
newWindow(scrubPdfURL);
|
||||
|
||||
@@ -10,6 +10,11 @@ import { insertScrubRun } from "../db/scrub-history-db";
|
||||
import getMainWindow from "../../util/getMainWindow";
|
||||
import { showNotification } from "../util/notification";
|
||||
|
||||
export type ScrubEstimateResult = {
|
||||
pdfUrl?: string;
|
||||
jobId?: string;
|
||||
};
|
||||
|
||||
function getErrorMessage(responseMessage: string | undefined): string | null {
|
||||
if (!responseMessage) {
|
||||
return null;
|
||||
@@ -57,7 +62,7 @@ async function ScrubEstimate({
|
||||
job,
|
||||
}: {
|
||||
job: RawJobDataObject;
|
||||
}): Promise<string | undefined> {
|
||||
}): Promise<ScrubEstimateResult | undefined> {
|
||||
// No transformation here - send raw job to Lambda
|
||||
const currentChannel = autoUpdater.channel;
|
||||
let estimateScrubberUrl: string;
|
||||
@@ -129,14 +134,20 @@ async function ScrubEstimate({
|
||||
|
||||
const { report_issue_url, identified_item, pdf_url } = result?.data ?? {};
|
||||
|
||||
let scrubHistoryJobId: string | undefined;
|
||||
|
||||
try {
|
||||
insertScrubRun({
|
||||
const identifiedItems = Array.isArray(identified_item)
|
||||
? identified_item.slice(1, identified_item.length)
|
||||
: [];
|
||||
const scrubRun = insertScrubRun({
|
||||
job,
|
||||
identifiedItems: identified_item.slice(1, identified_item.length), // Remove first item which is the result metadata
|
||||
identifiedItems, // Remove first item which is the result metadata
|
||||
pdf_url: typeof pdf_url === "string" ? pdf_url : null,
|
||||
report_issue_url:
|
||||
typeof report_issue_url === "string" ? report_issue_url : null,
|
||||
});
|
||||
scrubHistoryJobId = scrubRun.jobId;
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send(ipcTypes.toRenderer.scrub.historyUpdated);
|
||||
}
|
||||
@@ -153,7 +164,10 @@ async function ScrubEstimate({
|
||||
// report_issue_url,
|
||||
// });
|
||||
|
||||
return pdf_url;
|
||||
return {
|
||||
pdfUrl: typeof pdf_url === "string" ? pdf_url : undefined,
|
||||
jobId: scrubHistoryJobId,
|
||||
};
|
||||
} catch (error) {
|
||||
const err = error as AxiosError;
|
||||
log.error("Error while scrubbing estimate:", err.message, err.stack);
|
||||
@@ -187,7 +201,7 @@ async function ScrubEstimate({
|
||||
scrubErrorMessage || "Authentication with Estimate Scrubber failed.",
|
||||
});
|
||||
}
|
||||
return "Error: Unable to scrub estimate.";
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -564,6 +564,9 @@ app.whenReady().then(async () => {
|
||||
const mainWindow = getMainWindow();
|
||||
mainWindow?.webContents.send(ipcTypes.toRenderer.updates.checking);
|
||||
});
|
||||
autoUpdater.on("error", (error) => {
|
||||
console.error("Auto updater error:", error);
|
||||
});
|
||||
autoUpdater.on("update-available", (info) => {
|
||||
log.info("Update available.", info);
|
||||
const mainWindow = getMainWindow();
|
||||
|
||||
@@ -23,7 +23,9 @@ async function checkForAppUpdates(channel?: string | null): Promise<void> {
|
||||
store.set("app.channel", channel);
|
||||
}
|
||||
log.debug("Checking for app updates on channel:", autoUpdater.channel);
|
||||
autoUpdater.checkForUpdates();
|
||||
autoUpdater.checkForUpdates().catch((error) => {
|
||||
console.error("Error checking for app updates:", error);
|
||||
});
|
||||
}
|
||||
|
||||
export { checkForAppUpdates, checkForAppUpdatesContinuously };
|
||||
|
||||
@@ -88,14 +88,18 @@ async function StartWatcher(
|
||||
watcher
|
||||
.on("add", async function (path) {
|
||||
console.log("File", path, "has been added");
|
||||
HandleNewFile(path);
|
||||
HandleNewFile(path).catch((err) => {
|
||||
console.error("Something went wrong in watcher -> add", err);
|
||||
});
|
||||
})
|
||||
// .on("addDir", function (path) {
|
||||
// console.log("Directory", path, "has been added");
|
||||
// })
|
||||
.on("change", async function (path) {
|
||||
console.log("File", path, "has been changed");
|
||||
HandleNewFile(path);
|
||||
HandleNewFile(path).catch((err) => {
|
||||
console.error("Something went wrong in watcher -> change", err);
|
||||
});
|
||||
})
|
||||
// .on("unlink", function (path) {
|
||||
// console.log("File", path, "has been removed");
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
import { ConfigProvider, Layout } from "antd";
|
||||
import { FC } from "react";
|
||||
import { FC, useEffect } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { Provider } from "react-redux";
|
||||
import { HashRouter, Route, Routes } from "react-router";
|
||||
import { HashRouter, Route, Routes, useNavigate } from "react-router";
|
||||
import ErrorBoundaryFallback from "./components/ErrorBoundaryFallback/ErrorBoundaryFallback";
|
||||
import Home from "./components/Home/Home";
|
||||
import Settings from "./components/Settings/Settings";
|
||||
import UpdateAvailable from "./components/UpdateAvailable/UpdateAvailable";
|
||||
import reduxStore from "./redux/redux-store";
|
||||
import { NotificationProvider } from "./util/notificationContext";
|
||||
import ipcTypes from "../../util/ipcTypes.json";
|
||||
|
||||
const ScrubHistoryNavigationBridge: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const removeListener = window.electron.ipcRenderer.on(
|
||||
ipcTypes.toRenderer.scrub.openHistoryItem,
|
||||
(_event: Electron.IpcRendererEvent, payload?: { jobId?: unknown }) => {
|
||||
const jobId =
|
||||
typeof payload?.jobId === "string" ? payload.jobId.trim() : "";
|
||||
if (!jobId) return;
|
||||
|
||||
navigate(`/?jobId=${encodeURIComponent(jobId)}&openedAt=${Date.now()}`);
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
removeListener();
|
||||
};
|
||||
}, [navigate]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const App: FC = () => {
|
||||
return (
|
||||
@@ -28,6 +52,7 @@ const App: FC = () => {
|
||||
<HashRouter>
|
||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||
<NotificationProvider>
|
||||
<ScrubHistoryNavigationBridge />
|
||||
<Layout
|
||||
style={{
|
||||
height: "100vh",
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
SettingOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
Button,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Empty,
|
||||
@@ -25,13 +25,13 @@ import {
|
||||
Spin,
|
||||
Statistic,
|
||||
Table,
|
||||
theme,
|
||||
Tooltip,
|
||||
Typography,
|
||||
theme,
|
||||
} from "antd";
|
||||
import { FC, UIEvent, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { selectWatcherStatus } from "@renderer/redux/app.slice";
|
||||
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
||||
import ipcTypes from "../../../../util/ipcTypes.json";
|
||||
@@ -88,6 +88,7 @@ function isScrubHistoryPage(value: unknown): value is ScrubHistoryPage {
|
||||
const Home: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const { token } = theme.useToken();
|
||||
const ipcRenderer = window.electron.ipcRenderer;
|
||||
|
||||
@@ -99,8 +100,31 @@ const Home: FC = () => {
|
||||
const [totalResults, setTotalResults] = useState<number>(0);
|
||||
const [lastProcessed, setLastProcessed] = useState<number | null>(null);
|
||||
const [loadedPage, setLoadedPage] = useState<number>(1);
|
||||
const [selectedJobId, setSelectedJobId] = useState<string | null>(null);
|
||||
const [manualSelectedJobId, setManualSelectedJobId] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
const [loadingMore, setLoadingMore] = useState<boolean>(false);
|
||||
const routeSelectedJobId = searchParams.get("jobId")?.trim() || null;
|
||||
const selectedJobId = routeSelectedJobId ?? manualSelectedJobId;
|
||||
|
||||
const clearRouteSelection = useCallback(() => {
|
||||
if (routeSelectedJobId) {
|
||||
navigate("/", { replace: true });
|
||||
}
|
||||
}, [navigate, routeSelectedJobId]);
|
||||
|
||||
const selectJob = useCallback(
|
||||
(jobId: string) => {
|
||||
setManualSelectedJobId(jobId);
|
||||
clearRouteSelection();
|
||||
},
|
||||
[clearRouteSelection],
|
||||
);
|
||||
|
||||
const clearSelectedJob = useCallback(() => {
|
||||
setManualSelectedJobId(null);
|
||||
clearRouteSelection();
|
||||
}, [clearRouteSelection]);
|
||||
|
||||
const loadScrubHistoryPage = useCallback(
|
||||
async (page: number, size: number): Promise<unknown> =>
|
||||
@@ -238,19 +262,19 @@ const Home: FC = () => {
|
||||
async (jobId: string) => {
|
||||
await ipcRenderer.invoke(ipcTypes.toMain.scrubHistory.deleteJob, jobId);
|
||||
if (selectedJobId === jobId) {
|
||||
setSelectedJobId(null);
|
||||
clearSelectedJob();
|
||||
}
|
||||
await refresh(1);
|
||||
},
|
||||
[ipcRenderer, refresh, selectedJobId],
|
||||
[clearSelectedJob, ipcRenderer, refresh, selectedJobId],
|
||||
);
|
||||
|
||||
const clearAll = useCallback(async () => {
|
||||
await ipcRenderer.invoke(ipcTypes.toMain.scrubHistory.clearAll);
|
||||
setLoadedPage(1);
|
||||
setSelectedJobId(null);
|
||||
clearSelectedJob();
|
||||
await refresh(1);
|
||||
}, [ipcRenderer, refresh]);
|
||||
}, [clearSelectedJob, ipcRenderer, refresh]);
|
||||
|
||||
const openScrubReport = useCallback((anchor: string | null) => {
|
||||
if (!anchor) return;
|
||||
@@ -554,11 +578,11 @@ const Home: FC = () => {
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => setSelectedJobId(record.id)}
|
||||
onClick={() => selectJob(record.id)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
setSelectedJobId(record.id);
|
||||
selectJob(record.id);
|
||||
}
|
||||
}}
|
||||
style={{
|
||||
@@ -738,7 +762,7 @@ const Home: FC = () => {
|
||||
icon={<ReloadOutlined />}
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
setSelectedJobId(null);
|
||||
clearSelectedJob();
|
||||
refresh(1).catch(() => undefined);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -64,7 +64,8 @@
|
||||
},
|
||||
"scrub": {
|
||||
"scrubError": "toRenderer_scrubError",
|
||||
"historyUpdated": "toRenderer_scrub_historyUpdated"
|
||||
"historyUpdated": "toRenderer_scrub_historyUpdated",
|
||||
"openHistoryItem": "toRenderer_scrub_openHistoryItem"
|
||||
},
|
||||
"updates": {
|
||||
"checking": "toRenderer_updates_checking",
|
||||
|
||||
Reference in New Issue
Block a user