Add settings page and temporary home page.
This commit is contained in:
16
package-lock.json
generated
16
package-lock.json
generated
@@ -22,7 +22,6 @@
|
||||
"winax": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.1.0",
|
||||
"@electron-toolkit/tsconfig": "^2.0.0",
|
||||
@@ -174,21 +173,6 @@
|
||||
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ant-design/v5-patch-for-react-19": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@ant-design/v5-patch-for-react-19/-/v5-patch-for-react-19-1.0.3.tgz",
|
||||
"integrity": "sha512-iWfZuSUl5kuhqLUw7jJXUQFMMkM7XpW7apmKzQBQHU0cpifYW4A79xIBt9YVO5IBajKpPG5UKP87Ft7Yrw1p/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.x"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"antd": ">=5.22.6",
|
||||
"react": ">=19.0.0",
|
||||
"react-dom": ">=19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@apm-js-collab/code-transformer": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz",
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
"winax": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.1.0",
|
||||
"@electron-toolkit/tsconfig": "^2.0.0",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import "@ant-design/v5-patch-for-react-19";
|
||||
import { ConfigProvider, Layout, Space } from "antd";
|
||||
import { ConfigProvider, Layout } from "antd";
|
||||
import { FC } from "react";
|
||||
import { ErrorBoundary } from "react-error-boundary";
|
||||
import { Provider } from "react-redux";
|
||||
import { HashRouter, Route, Routes } 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";
|
||||
@@ -32,7 +32,8 @@ const App: FC = () => {
|
||||
<Layout.Content style={{ padding: "24px" }}>
|
||||
<UpdateAvailable />
|
||||
<Routes>
|
||||
<Route path="/" element={<Settings />} />
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/settings" element={<Settings />} />
|
||||
</Routes>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
|
||||
284
src/renderer/src/components/Home/Home.tsx
Normal file
284
src/renderer/src/components/Home/Home.tsx
Normal file
@@ -0,0 +1,284 @@
|
||||
// renderer/Home.tsx
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Col,
|
||||
Flex,
|
||||
Row,
|
||||
Space,
|
||||
Statistic,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
theme,
|
||||
List,
|
||||
Avatar,
|
||||
Divider,
|
||||
} from "antd";
|
||||
import { FC } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import {
|
||||
FileTextOutlined,
|
||||
CheckCircleOutlined,
|
||||
ClockCircleOutlined,
|
||||
SettingOutlined,
|
||||
EyeOutlined,
|
||||
RightOutlined,
|
||||
} from "@ant-design/icons";
|
||||
|
||||
const { Title, Text, Paragraph } = Typography;
|
||||
|
||||
// Placeholder data for recently scrubbed estimates
|
||||
const placeholderEstimates = [
|
||||
{
|
||||
id: "EST-2026-001",
|
||||
fileName: "estimate_john_doe.pdf",
|
||||
scrubbedAt: "2026-01-12T10:30:00",
|
||||
status: "completed",
|
||||
itemsProcessed: 24,
|
||||
},
|
||||
{
|
||||
id: "EST-2026-002",
|
||||
fileName: "estimate_jane_smith.pdf",
|
||||
scrubbedAt: "2026-01-12T09:15:00",
|
||||
status: "completed",
|
||||
itemsProcessed: 18,
|
||||
},
|
||||
{
|
||||
id: "EST-2026-003",
|
||||
fileName: "estimate_acme_corp.pdf",
|
||||
scrubbedAt: "2026-01-11T16:45:00",
|
||||
status: "completed",
|
||||
itemsProcessed: 32,
|
||||
},
|
||||
{
|
||||
id: "EST-2026-004",
|
||||
fileName: "estimate_bob_johnson.pdf",
|
||||
scrubbedAt: "2026-01-11T14:20:00",
|
||||
status: "completed",
|
||||
itemsProcessed: 15,
|
||||
},
|
||||
{
|
||||
id: "EST-2026-005",
|
||||
fileName: "estimate_tech_solutions.pdf",
|
||||
scrubbedAt: "2026-01-11T11:00:00",
|
||||
status: "completed",
|
||||
itemsProcessed: 27,
|
||||
},
|
||||
];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: "Estimate",
|
||||
dataIndex: "id",
|
||||
key: "id",
|
||||
responsive: ["md"] as const,
|
||||
render: (text: string, record: (typeof placeholderEstimates)[0]) => (
|
||||
<Space orientation="vertical" size={0}>
|
||||
<Text strong>{text}</Text>
|
||||
<Text type="secondary" style={{ fontSize: "12px" }}>
|
||||
{record.fileName}
|
||||
</Text>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "File",
|
||||
dataIndex: "fileName",
|
||||
key: "fileName",
|
||||
responsive: ["xs"] as const,
|
||||
render: (text: string, record: (typeof placeholderEstimates)[0]) => (
|
||||
<Space orientation="vertical" size={0}>
|
||||
<Text strong>{record.id}</Text>
|
||||
<Text type="secondary" style={{ fontSize: "12px" }}>
|
||||
{text}
|
||||
</Text>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Scrubbed",
|
||||
dataIndex: "scrubbedAt",
|
||||
key: "scrubbedAt",
|
||||
responsive: ["sm"] as const,
|
||||
render: (text: string) => {
|
||||
const date = new Date(text);
|
||||
return (
|
||||
<Space orientation="vertical" size={0}>
|
||||
<Text>{date.toLocaleDateString()}</Text>
|
||||
<Text type="secondary" style={{ fontSize: "12px" }}>
|
||||
{date.toLocaleTimeString()}
|
||||
</Text>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Items",
|
||||
dataIndex: "itemsProcessed",
|
||||
key: "itemsProcessed",
|
||||
responsive: ["lg"] as const,
|
||||
render: (value: number) => <Text strong>{value}</Text>,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
dataIndex: "status",
|
||||
key: "status",
|
||||
responsive: ["sm"] as const,
|
||||
render: (status: string) => (
|
||||
<Tag
|
||||
color={status === "completed" ? "success" : "processing"}
|
||||
icon={<CheckCircleOutlined />}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
{status === "completed" ? "Done" : "Processing"}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "",
|
||||
key: "action",
|
||||
width: 100,
|
||||
render: () => (
|
||||
<Button type="link" icon={<EyeOutlined />} size="small">
|
||||
View
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const Home: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { token } = theme.useToken();
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: "1400px", margin: "0 auto" }}>
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="large"
|
||||
style={{ width: "100%", display: "flex" }}
|
||||
>
|
||||
{/* Header */}
|
||||
<Flex justify="space-between" align="center" wrap="wrap" gap="small">
|
||||
<Title level={2} style={{ margin: 0 }}>
|
||||
Dashboard
|
||||
</Title>
|
||||
<Button
|
||||
type="default"
|
||||
icon={<SettingOutlined />}
|
||||
onClick={() => navigate("/settings")}
|
||||
size="large"
|
||||
>
|
||||
Settings
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<Row gutter={[16, 16]}>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<Card
|
||||
bordered={false}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, ${token.colorPrimary} 0%, ${token.colorPrimaryHover} 100%)`,
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title={
|
||||
<span style={{ color: "rgba(255,255,255,0.85)" }}>
|
||||
Total Estimates
|
||||
</span>
|
||||
}
|
||||
value={placeholderEstimates.length}
|
||||
prefix={<FileTextOutlined style={{ color: "#fff" }} />}
|
||||
valueStyle={{ color: "#fff", fontWeight: 600 }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<Card
|
||||
bordered={false}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, #52c41a 0%, #73d13d 100%)`,
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title={
|
||||
<span style={{ color: "rgba(255,255,255,0.85)" }}>
|
||||
Items Scrubbed
|
||||
</span>
|
||||
}
|
||||
value={placeholderEstimates.reduce(
|
||||
(acc, est) => acc + est.itemsProcessed,
|
||||
0,
|
||||
)}
|
||||
prefix={<CheckCircleOutlined style={{ color: "#fff" }} />}
|
||||
valueStyle={{ color: "#fff", fontWeight: 600 }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col xs={24} sm={24} lg={8}>
|
||||
<Card
|
||||
bordered={false}
|
||||
style={{
|
||||
background: `linear-gradient(135deg, #722ed1 0%, #9254de 100%)`,
|
||||
}}
|
||||
>
|
||||
<Statistic
|
||||
title={
|
||||
<span style={{ color: "rgba(255,255,255,0.85)" }}>
|
||||
Last Processed
|
||||
</span>
|
||||
}
|
||||
value={new Date(
|
||||
placeholderEstimates[0].scrubbedAt,
|
||||
).toLocaleTimeString()}
|
||||
prefix={<ClockCircleOutlined style={{ color: "#fff" }} />}
|
||||
valueStyle={{ color: "#fff", fontWeight: 600 }}
|
||||
/>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* Recent Estimates Card */}
|
||||
<Card
|
||||
bordered={false}
|
||||
title={
|
||||
<Flex align="center" gap="small">
|
||||
<FileTextOutlined style={{ fontSize: "20px" }} />
|
||||
<span>Recently Scrubbed Estimates</span>
|
||||
</Flex>
|
||||
}
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<RightOutlined />}
|
||||
iconPlacement="end"
|
||||
onClick={() => alert("View all estimates - coming soon!")}
|
||||
>
|
||||
View All
|
||||
</Button>
|
||||
}
|
||||
styles={{
|
||||
header: {
|
||||
borderBottom: `2px solid ${token.colorBorderSecondary}`,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* Table view for larger screens */}
|
||||
<div style={{ display: "block" }}>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={placeholderEstimates}
|
||||
rowKey="id"
|
||||
pagination={{ pageSize: 5, showSizeChanger: false }}
|
||||
scroll={{ x: 800 }}
|
||||
style={{ overflow: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
</Space>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
@@ -1,26 +1,18 @@
|
||||
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
||||
import {
|
||||
CheckCircleFilled,
|
||||
ExclamationCircleFilled,
|
||||
PauseCircleOutlined,
|
||||
PlayCircleOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
selectWatcherError,
|
||||
selectWatcherStatus,
|
||||
} from "@renderer/redux/app.slice";
|
||||
import { useAppSelector } from "@renderer/redux/reduxHooks";
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
Divider,
|
||||
Flex,
|
||||
InputNumber,
|
||||
Radio,
|
||||
RadioChangeEvent,
|
||||
Space,
|
||||
Switch,
|
||||
Typography,
|
||||
} from "antd";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
|
||||
@@ -1,12 +1,25 @@
|
||||
// renderer/Settings.tsx
|
||||
import { Space } from "antd";
|
||||
import { Button, Space } from "antd";
|
||||
import { ArrowLeftOutlined } from "@ant-design/icons";
|
||||
import { FC } from "react";
|
||||
import { useNavigate } from "react-router";
|
||||
import SettingsConfig from "./Settings.Config";
|
||||
import SettingsWatcher from "./Settings.Watcher";
|
||||
|
||||
const Settings: FC = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Space orientation="vertical" size="large" style={{ width: "100%" }}>
|
||||
<Button
|
||||
type="text"
|
||||
icon={<ArrowLeftOutlined />}
|
||||
onClick={() => navigate("/")}
|
||||
style={{ alignSelf: "flex-start" }}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
|
||||
<SettingsWatcher />
|
||||
|
||||
<SettingsConfig />
|
||||
|
||||
Reference in New Issue
Block a user