feature/IO-3702-ESPD-UI-AND-FIXES - Stage 5 (Lint)

This commit is contained in:
Dave
2026-05-27 13:47:21 -04:00
parent 1e7e13ff32
commit 527a5ef16d
16 changed files with 188 additions and 196 deletions

View File

@@ -172,10 +172,9 @@ async function ImportJob(filepath: string): Promise<void> {
mainWindow.show();
mainWindow.focus();
if (scrubHistoryJobId) {
mainWindow.webContents.send(
ipcTypes.toRenderer.scrub.openHistoryItem,
{ jobId: scrubHistoryJobId },
);
mainWindow.webContents.send(ipcTypes.toRenderer.scrub.openHistoryItem, {
jobId: scrubHistoryJobId,
});
}
};
@@ -362,6 +361,13 @@ export function ReplaceOwnerInfoWithClaimant<
>
>,
>(jobObject: T): T {
const mutableJob = jobObject as T &
Record<string, unknown> & {
owner?: {
data?: Record<string, unknown>;
};
};
// Promote claimant data first if owner identity is entirely missing; otherwise fallback to insured data.
const identityKeys = ["ln", "fn", "co_nm"] as const; // keys used to determine presence
const copyKeys = [
@@ -381,27 +387,23 @@ export function ReplaceOwnerInfoWithClaimant<
] as const; // full set of fields to copy/delete
const ownerMissing = identityKeys.every((k) =>
_.isEmpty((jobObject as any)[`ownr_${k}`]),
_.isEmpty(mutableJob[`ownr_${k}`]),
);
const claimantHasSome = identityKeys.some(
(k) => !_.isEmpty((jobObject as any)[`clmt_${k}`]),
(k) => !_.isEmpty(mutableJob[`clmt_${k}`]),
);
const claimantMissing = identityKeys.every((k) =>
_.isEmpty((jobObject as any)[`clmt_${k}`]),
_.isEmpty(mutableJob[`clmt_${k}`]),
);
const { owner } = jobObject as any; // destructure for optional nested updates
const { owner } = mutableJob; // destructure for optional nested updates
// Copy helper inline (no extra function as requested)
const promote = (sourcePrefix: "clmt" | "insd"): void => {
copyKeys.forEach((suffix) => {
(jobObject as any)[`ownr_${suffix}`] = (jobObject as any)[
`${sourcePrefix}_${suffix}`
];
mutableJob[`ownr_${suffix}`] = mutableJob[`${sourcePrefix}_${suffix}`];
if (owner?.data) {
owner.data[`ownr_${suffix}`] = (jobObject as any)[
`${sourcePrefix}_${suffix}`
];
owner.data[`ownr_${suffix}`] = mutableJob[`${sourcePrefix}_${suffix}`];
}
});
};
@@ -413,9 +415,9 @@ export function ReplaceOwnerInfoWithClaimant<
}
// Delete the claimant info as it's not needed.
copyKeys.forEach((suffix) => delete (jobObject as any)[`clmt_${suffix}`]);
copyKeys.forEach((suffix) => delete mutableJob[`clmt_${suffix}`]);
// Delete the insured info as it's not needed.
copyKeys.forEach((suffix) => delete (jobObject as any)[`insd_${suffix}`]);
copyKeys.forEach((suffix) => delete mutableJob[`insd_${suffix}`]);
return jobObject;
}

View File

@@ -107,7 +107,7 @@ function createWindow(): void {
const template = [
// { role: 'appMenu' }
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
...(isMac
? [
{
@@ -128,7 +128,7 @@ function createWindow(): void {
{
label: "File",
submenu: [
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
isMac ? { role: "close" } : { role: "quit" },
],
},
@@ -142,7 +142,7 @@ function createWindow(): void {
{ role: "cut" },
{ role: "copy" },
{ role: "paste" },
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
...(isMac
? [
{ role: "pasteAndMatchStyle" },
@@ -160,7 +160,7 @@ function createWindow(): void {
// { role: 'viewMenu' }
{
label: "View",
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
submenu: [
{ role: "reload" },
{ role: "forceReload" },
@@ -175,7 +175,7 @@ function createWindow(): void {
},
{
label: "Application",
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
submenu: [
{
label: "Open on Startup",
@@ -376,7 +376,7 @@ function createWindow(): void {
submenu: [
{ role: "minimize" },
{ role: "zoom" },
// @ts-ignore
// @ts-ignore -- Electron menu role unions are narrower than this conditional template.
...(isMac
? [
{ type: "separator" },
@@ -399,7 +399,7 @@ function createWindow(): void {
// Update the menu to make the hidden item visible
// Find the menu item dynamically by its id
const fileMenu = template.find((item) => item.label === "Application");
// @ts-ignore
// @ts-ignore -- Electron menu submenu typing does not expose array helpers after casting.
const hiddenItem = fileMenu?.submenu?.find(
(item: { id: string }) => item.id === "development",
);

View File

@@ -15,8 +15,8 @@ export interface GraphQLResponse {
shopname: string;
};
jobs?: Array<{
labhrs: any;
larhrs: any;
labhrs: unknown;
larhrs: unknown;
ro_number: string;
ownr_ln: string;
ownr_fn: string;

View File

@@ -14,15 +14,15 @@ import {
const getSetting = async (
_event: IpcMainInvokeEvent,
key: string,
): Promise<any> => {
): Promise<unknown> => {
return Store.get(`settings.${key}`);
};
const setSetting = async (
_event: IpcMainInvokeEvent,
key: string,
value: any,
): Promise<any> => {
value: unknown,
): Promise<unknown> => {
Store.set(`settings.${key}`, value);
return Store.get(`settings.${key}`);
};

View File

@@ -1,11 +1,11 @@
import {BrowserWindow} from "electron";
import { BrowserWindow } from "electron";
import log from "electron-log/main";
const getMainWindow = (): Electron.BrowserWindow => {
return BrowserWindow.getAllWindows()[0];
};
const sendIpcToRenderer = (ipcMessage: string, ...args: any[]): void => {
const sendIpcToRenderer = (ipcMessage: string, ...args: unknown[]): void => {
const window = getMainWindow();
if (window) {
window.webContents.send(ipcMessage, ...args);

View File

@@ -1,4 +1,5 @@
import { nativeImage, type NativeImage, type Tray } from "electron";
import { PNG } from "pngjs";
let trayInstance: Tray | undefined;
let trayBaseImage: NativeImage | undefined;
@@ -29,29 +30,8 @@ function buildStatusTrayImage(
started: boolean,
): NativeImage {
try {
const pngjs = (() => {
try {
return require("pngjs") as {
PNG?: {
sync?: {
read?: (b: Buffer) => unknown;
write?: (p: unknown) => Buffer;
};
};
};
} catch {
return null;
}
})();
if (!pngjs?.PNG?.sync?.read || !pngjs.PNG.sync.write) return base;
const basePng = base.toPNG();
const png = pngjs.PNG.sync.read(basePng) as {
width: number;
height: number;
data: Uint8Array;
};
const png = PNG.sync.read(basePng);
const width = png.width;
const height = png.height;
@@ -86,7 +66,7 @@ function buildStatusTrayImage(
}
}
return nativeImage.createFromBuffer(pngjs.PNG.sync.write(png));
return nativeImage.createFromBuffer(PNG.sync.write(png));
} catch {
return base;
}

View File

@@ -3,22 +3,22 @@
* @param obj The object whose keys need to be converted to uppercase
* @returns A new object with all keys converted to uppercase
*/
function uppercaseObjectKeys<T extends Record<string, any>>(
obj: T,
): Record<string, any> {
if (typeof obj !== "object" || obj === null) {
return obj;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
function uppercaseObjectKeys(
obj: Record<string, unknown>,
): Record<string, unknown> {
return Object.entries(obj).reduce(
(result, [key, value]) => {
const uppercaseKey = key.toUpperCase();
result[uppercaseKey] = typeof value === "object" && value !== null
? uppercaseObjectKeys(value)
: value;
result[uppercaseKey] = isRecord(value)
? uppercaseObjectKeys(value)
: value;
return result;
},
{} as Record<string, any>,
{} as Record<string, unknown>,
);
}
export default uppercaseObjectKeys;

View File

@@ -1,5 +1,6 @@
import { Card, Form, Input, Space } from "antd";
import { FC, useEffect } from "react";
import type { FormProps } from "antd";
import { FC, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
@@ -7,31 +8,36 @@ const SettingsConfig: FC = () => {
const { t } = useTranslation();
const [form] = Form.useForm();
const settingFields = [
{
name: "esApiKey",
label: t("settings.labels.esApiKey"),
component: Input.Password,
hasFeedback: true,
componentProps: {
placeholder: "Enter your API key",
const settingFields = useMemo(
() => [
{
name: "esApiKey",
label: t("settings.labels.esApiKey"),
component: Input.Password,
hasFeedback: true,
componentProps: {
placeholder: "Enter your API key",
},
},
},
];
],
[t],
);
useEffect(() => {
settingFields.forEach((field) => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.get, field.name)
.then((value: any) => {
.then((value: unknown) => {
form.setFieldsValue({ [field.name]: value });
});
});
}, []);
}, [form, settingFields]);
const handleFieldChange = (changedFields: any): void => {
const handleFieldChange: FormProps["onFieldsChange"] = (changedFields) => {
changedFields.forEach((field) => {
const fieldName = field.name[0];
const fieldName = Array.isArray(field.name)
? String(field.name[0])
: String(field.name);
const { value } = field;
// Placeholder for validation
@@ -46,7 +52,7 @@ const SettingsConfig: FC = () => {
});
};
const validateField = (fieldName: string, value: any): boolean => {
const validateField = (fieldName: string, value: unknown): boolean => {
// Placeholder for actual validation logic
console.log(`Validating ${fieldName}:`, value);
return true;

View File

@@ -1,17 +1,19 @@
import {createContext, FC, ReactNode, useContext} from "react";
import { createContext, FC, ReactNode, useContext } from "react";
import { notification } from "antd";
/**
* Create our NotificationContext to store the `api` object
* returned by notification.useNotification().
*/
const NotificationContext = createContext(null);
type NotificationApi = ReturnType<typeof notification.useNotification>[0];
const NotificationContext = createContext<NotificationApi | null>(null);
/**
* A custom hook to make usage easier in child components.
*/
// eslint-disable-next-line react-refresh/only-export-components, @typescript-eslint/explicit-function-return-type
export const useNotification = () => {
// eslint-disable-next-line react-refresh/only-export-components
export const useNotification = (): NotificationApi | null => {
return useContext(NotificationContext);
};
@@ -26,8 +28,7 @@ interface NotificationProviderProps {
}
export const NotificationProvider: FC<NotificationProviderProps> = ({
// eslint-disable-next-line react/prop-types
children, //TODO: Unable to resolve this. Adding an eslint disable.
children,
}) => {
const [api, contextHolder] = notification.useNotification({
placement: "bottomRight",
@@ -36,7 +37,6 @@ export const NotificationProvider: FC<NotificationProviderProps> = ({
});
return (
// @ts-ignore
<NotificationContext.Provider value={api}>
{/* contextHolder must be rendered in the DOM so notifications can appear */}
{contextHolder}

View File

@@ -3,6 +3,7 @@ import log from "electron-log/main";
import fs from "fs";
import os from "os";
import path from "path";
import * as v8 from "v8";
import Store from "../main/store/store";
/**
* Human-readable memory/cpu/resource snapshot.
@@ -39,6 +40,10 @@ export type MemoryUsageStats = {
};
// (merged into top import)
type GlobalWithGc = typeof globalThis & {
gc?: () => void;
};
/**
* Options for dumpMemoryStats.
*/
@@ -89,9 +94,11 @@ export async function dumpMemoryStats(
} = options;
// Allow GC if requested and available to get a cleaner snapshot
if (runGc && typeof (global as any).gc === "function") {
const runtimeGlobal = globalThis as GlobalWithGc;
if (runGc && typeof runtimeGlobal.gc === "function") {
try {
(global as any).gc();
runtimeGlobal.gc();
} catch {
// ignore GC errors
}
@@ -143,8 +150,6 @@ export async function dumpMemoryStats(
if (includeHeapSpaces) {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const v8: typeof import("v8") = require("v8");
if (typeof v8.getHeapSpaceStatistics === "function") {
stats.heapSpaces = v8.getHeapSpaceStatistics();
}
@@ -155,15 +160,13 @@ export async function dumpMemoryStats(
if (writeHeapSnapshot) {
try {
if (!runGc && typeof (global as any).gc === "function") {
if (!runGc && typeof runtimeGlobal.gc === "function") {
try {
(global as any).gc();
runtimeGlobal.gc();
} catch {
/* ignore */
}
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const v8: typeof import("v8") = require("v8");
if (typeof v8.writeHeapSnapshot === "function") {
const baseDir =
heapSnapshotDir || path.dirname(log.transports.file.getFile().path);