UI Refactor.

This commit is contained in:
Patrick Fic
2026-01-12 16:06:13 -08:00
parent 8bf8d0bcac
commit ea4131102c
25 changed files with 160 additions and 159 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,6 +1,6 @@
appId: com.imex.esdp
copyright: ImEX Systems Inc.
productName: EMS Uploader
productName: EMS Data Pump
generateUpdatesFilesForAllChannels: true
directories:
@@ -17,7 +17,7 @@ files:
asarUnpack:
- resources/**
win:
executableName: EMSUploader
executableName: EMSDP
icon: resources/icon.png
azureSignOptions:
endpoint: https://eus.codesigning.azure.net

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 190 KiB

View File

@@ -1,5 +1,5 @@
import "@ant-design/v5-patch-for-react-19";
import { Badge, ConfigProvider, Layout, Skeleton } from "antd";
import { ConfigProvider, Layout, Space } from "antd";
import { FC } from "react";
import { ErrorBoundary } from "react-error-boundary";
import { Provider } from "react-redux";
@@ -11,17 +11,15 @@ import reduxStore from "./redux/redux-store";
import { NotificationProvider } from "./util/notificationContext";
const App: FC = () => {
const isTest = window.api.isTest();
return (
<ConfigProvider
theme={{
token: {},
token: {
borderRadius: 8,
},
components: {
Card: {
borderRadius: 8,
colorBgBase: "#ffaacc",
boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
},
},
}}
@@ -30,21 +28,14 @@ const App: FC = () => {
<HashRouter>
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
<NotificationProvider>
<Skeleton loading={false} active>
<Layout style={{ minHeight: "100vh" }}>
<Badge.Ribbon
text={isTest && "Connected to Test"}
color={isTest ? "red" : undefined}
>
<Layout.Content style={{ padding: "0 24px" }}>
<UpdateAvailable />
<Routes>
<Route path="/" element={<Settings />} />
</Routes>
</Layout.Content>
</Badge.Ribbon>
</Layout>
</Skeleton>
<Layout style={{ minHeight: "100vh", background: "#f0f2f5" }}>
<Layout.Content style={{ padding: "24px" }}>
<UpdateAvailable />
<Routes>
<Route path="/" element={<Settings />} />
</Routes>
</Layout.Content>
</Layout>
</NotificationProvider>
</ErrorBoundary>
</HashRouter>

View File

@@ -1,4 +1,4 @@
import { Card, Form, Input } from "antd";
import { Card, Form, Input, Space } from "antd";
import { FC, useEffect } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
@@ -11,9 +11,11 @@ const SettingsConfig: FC = () => {
{
name: "esApiKey",
label: t("settings.labels.esApiKey"),
component: Input,
component: Input.Password,
hasFeedback: true,
componentProps: {},
componentProps: {
placeholder: "Enter your API key",
},
},
];
@@ -52,24 +54,30 @@ const SettingsConfig: FC = () => {
return (
<Card title={t("settings.labels.config")}>
<Form form={form} onFieldsChange={handleFieldChange} layout="vertical">
{settingFields.map((field) => (
<Form.Item
key={field.name}
label={t(`settings.labels.${field.name}`, field.label)}
name={field.name}
hasFeedback={field.hasFeedback}
validateStatus="validating"
rules={[
{
required: true,
message: t(`settings.validation.${field.name}Required`),
},
]}
>
<field.component {...field.componentProps} />
</Form.Item>
))}
<Form
form={form}
onFieldsChange={handleFieldChange}
layout="vertical"
size="large"
>
<Space direction="vertical" size="middle" style={{ width: "100%" }}>
{settingFields.map((field) => (
<Form.Item
key={field.name}
label={t(`settings.labels.${field.name}`, field.label)}
name={field.name}
hasFeedback={field.hasFeedback}
rules={[
{
required: true,
message: t(`settings.validation.${field.name}Required`),
},
]}
>
<field.component {...field.componentProps} />
</Form.Item>
))}
</Space>
</Form>
</Card>
);

View File

@@ -1,5 +1,5 @@
import { DeleteFilled, FileAddFilled } from "@ant-design/icons";
import { Button, Card, Space, Timeline } from "antd";
import { DeleteOutlined, FolderAddOutlined } from "@ant-design/icons";
import { Button, Card, Empty, List, Space, Typography } from "antd";
import { useEffect, useState, FC } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
@@ -33,32 +33,46 @@ const SettingsWatchedPaths: FC = () => {
};
return (
<Card
title={t("settings.labels.watchedpaths")}
extra={
<Button onClick={handleAddPath} icon={<FileAddFilled />}>
{t("settings.actions.addpath")}
</Button>
}
>
<Timeline
items={watchedPaths.map((path, index) => ({
key: index,
children: (
<Space align="baseline">
{path}
<>
<List
locale={{
emptyText: (
<Empty
description={"No watched paths configured"}
image={Empty.PRESENTED_IMAGE_SIMPLE}
/>
),
}}
dataSource={watchedPaths}
renderItem={(path) => (
<List.Item
actions={[
<Button
size="small"
key="delete"
danger
type="text"
icon={<DeleteFilled />}
icon={<DeleteOutlined />}
onClick={() => handleRemovePath(path)}
/>
</Space>
),
}))}
>
Remove
</Button>,
]}
>
<Typography.Text code copyable>
{path}
</Typography.Text>
</List.Item>
)}
/>
</Card>
<Button
type="dashed"
style={{ width: "100%", marginTop: 16 }}
onClick={handleAddPath}
icon={<FolderAddOutlined />}
>
{t("settings.actions.addpath")}
</Button>
</>
);
};
export default SettingsWatchedPaths;

View File

@@ -14,20 +14,19 @@ import {
Badge,
Button,
Card,
Col,
Divider,
Flex,
InputNumber,
Row,
Radio,
RadioChangeEvent,
Space,
Switch,
Typography,
} from "antd";
import { FC, useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json";
const colSpans = {
md: 12,
sm: 24,
};
import SettingsWatchedPaths from "./Settings.WatchedPaths";
const SettingsWatcher: FC = () => {
const { t } = useTranslation();
@@ -63,10 +62,10 @@ const SettingsWatcher: FC = () => {
window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.stop);
};
const toggleWatcherMode = (checked: boolean): void => {
const toggleWatcherMode = (e: RadioChangeEvent): void => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.watcher.setpolling, {
enabled: !checked,
enabled: !e.target.value,
interval: pollingState.interval,
})
.then((storePollingState: { enabled: boolean; interval: number }) => {
@@ -89,69 +88,70 @@ const SettingsWatcher: FC = () => {
};
return (
<Badge.Ribbon
text={
<Card
title={t("settings.labels.watcherstatus")}
extra={
isWatcherStarted ? (
<Space>
<CheckCircleFilled />
{t("settings.labels.started")}
</Space>
<Button
danger
block
icon={<PauseCircleOutlined />}
onClick={handleStop}
>
{t("settings.actions.stopwatcher")}
</Button>
) : (
<Space>
<ExclamationCircleFilled />
{t("settings.labels.stopped")}
</Space>
<Button
type="primary"
block
icon={<PlayCircleOutlined />}
onClick={handleStart}
>
{t("settings.actions.startwatcher")}
</Button>
)
}
color={isWatcherStarted ? "green" : "red"}
>
<Card title={t("settings.labels.watcherstatus")}>
<Row gutter={[16, 16]}>
<Col {...colSpans}>
{isWatcherStarted ? (
<Button
danger
icon={<PauseCircleOutlined />}
onClick={handleStop}
>
{t("settings.actions.stopwatcher")}
</Button>
) : (
<Button
type="primary"
icon={<PlayCircleOutlined />}
onClick={handleStart}
>
{t("settings.actions.startwatcher")}
</Button>
)}
</Col>
<Col {...colSpans}>
<Space direction="vertical" wrap>
<Switch
checked={!pollingState.enabled}
onChange={toggleWatcherMode}
checkedChildren={t("settings.labels.watchermoderealtime")}
unCheckedChildren={t("settings.labels.watchermodepolling")}
/>
{pollingState.enabled && (
<Space size="small" direction="vertical" wrap>
<span>{t("settings.labels.pollinginterval")}</span>
<InputNumber
title={t("settings.labels.pollinginterval")}
disabled={!pollingState.enabled}
min={1000}
value={pollingState.interval}
onChange={handlePollingIntervalChange}
/>
</Space>
)}
{watcherError && <Alert message={watcherError} />}
</Space>
</Col>
</Row>
</Card>
</Badge.Ribbon>
<Space orientation="vertical" size="middle" style={{ width: "100%" }}>
<SettingsWatchedPaths />
<Radio.Group
block
options={[
{ label: "Real-time", value: true },
{ label: "Polling", value: false },
]}
value={!pollingState.enabled}
onChange={toggleWatcherMode}
optionType="button"
buttonStyle="solid"
/>
{pollingState.enabled && (
<Flex align="center" gap="middle">
<Typography.Text>
{t("settings.labels.pollinginterval")}
</Typography.Text>
<InputNumber
min={1000}
step={1000}
value={pollingState.interval}
onChange={handlePollingIntervalChange}
style={{ width: 150 }}
/>
</Flex>
)}
{watcherError && (
<Alert
title="Watcher Error"
description={watcherError}
type="error"
showIcon
/>
)}
</Space>
</Card>
);
};
export default SettingsWatcher;

View File

@@ -1,27 +1,16 @@
// renderer/Settings.tsx
import { Col, Row } from "antd";
import { Space } from "antd";
import { FC } from "react";
import SettingsWatchedPaths from "./Settings.WatchedPaths";
import SettingsWatcher from "./Settings.Watcher";
import SettingsConfig from "./Settings.Config";
const colSpans = {
span: 24,
};
import SettingsWatcher from "./Settings.Watcher";
const Settings: FC = () => {
return (
<Row gutter={[16, 16]}>
<Col {...colSpans}>
<SettingsWatchedPaths />
</Col>
<Col {...colSpans}>
<SettingsWatcher />
</Col>
<Col {...colSpans}>
<SettingsConfig />
</Col>
</Row>
<Space orientation="vertical" size="large" style={{ width: "100%" }}>
<SettingsWatcher />
<SettingsConfig />
</Space>
);
};

View File

@@ -7,14 +7,13 @@ import * as Sentry from "@sentry/electron/renderer";
// Extend the Window interface to include the api property
declare global {
interface Window {
api: {
isTest: () => boolean;
};
}
interface Window {
api: {
isTest: () => boolean;
};
}
}
Sentry.init({
dsn: "https://ba41d22656999a8c1fd63bcb7df98650@o492140.ingest.us.sentry.io/4509074139447296",
});