feature/IO-3205-Paint-Scale-Integrations: Checkpoint

This commit is contained in:
Dave Richer
2025-04-23 14:16:01 -04:00
parent 7d881fc9e6
commit 21bf2c7499
6 changed files with 298 additions and 246 deletions

View File

@@ -0,0 +1,17 @@
export interface PaintScaleConfig {
id: string;
path: string | null;
type: PaintScaleType;
pollingInterval: number; // In seconds
}
export enum PaintScaleType {
PPG = "PPG",
SHERWIN = "SHERWIN",
AKZO = "AKZO",
}
export const paintScaleTypeOptions = Object.values(PaintScaleType).map((type) => ({
value: type,
label: type,
}));

View File

@@ -0,0 +1,114 @@
import { useState, useEffect } from 'react';
import { PaintScaleConfig, PaintScaleType } from './types';
import ipcTypes from '../../../../../util/ipcTypes.json';
type ConfigType = 'input' | 'output';
export const usePaintScaleConfig = (configType: ConfigType) => {
const [paintScaleConfigs, setPaintScaleConfigs] = useState<PaintScaleConfig[]>([]);
// Get the appropriate IPC methods based on config type
const getConfigsMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.getInputConfigs
: ipcTypes.toMain.settings.paintScale.getOutputConfigs;
const setConfigsMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.setInputConfigs
: ipcTypes.toMain.settings.paintScale.setOutputConfigs;
const setPathMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.setInputPath
: ipcTypes.toMain.settings.paintScale.setOutputPath;
// Load paint scale configs on mount
useEffect(() => {
window.electron.ipcRenderer
.invoke(getConfigsMethod)
.then((configs: PaintScaleConfig[]) => {
// Ensure all configs have a pollingInterval (for backward compatibility)
const updatedConfigs = configs.map(config => ({
...config,
pollingInterval: config.pollingInterval || 60 // Default to 60 seconds if not set
}));
setPaintScaleConfigs(updatedConfigs || []);
})
.catch((error) => {
console.error(`Failed to load paint scale ${configType} configs:`, error);
});
}, [getConfigsMethod]);
// Save configs to store
const saveConfigs = (configs: PaintScaleConfig[]) => {
window.electron.ipcRenderer
.invoke(setConfigsMethod, configs)
.catch((error) => {
console.error(`Failed to save paint scale ${configType} configs:`, error);
});
};
// Handle adding a new paint scale config
const handleAddConfig = () => {
const newConfig: PaintScaleConfig = {
id: Date.now().toString(),
path: null,
type: PaintScaleType.PPG,
pollingInterval: 60, // Default to 60 seconds
};
const updatedConfigs = [...paintScaleConfigs, newConfig];
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle removing a config
const handleRemoveConfig = (id: string) => {
const updatedConfigs = paintScaleConfigs.filter((config) => config.id !== id);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle path selection
const handlePathChange = (id: string) => {
window.electron.ipcRenderer
.invoke(setPathMethod, id)
.then((path: string | null) => {
if (path) {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, path } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
}
})
.catch((error) => {
console.error(`Failed to set paint scale ${configType} path:`, error);
});
};
// Handle type change
const handleTypeChange = (id: string, type: PaintScaleType) => {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, type } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle polling interval change
const handlePollingIntervalChange = (id: string, pollingInterval: number) => {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, pollingInterval } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
return {
paintScaleConfigs,
handleAddConfig,
handleRemoveConfig,
handlePathChange,
handleTypeChange,
handlePollingIntervalChange
};
};

View File

@@ -1,118 +1,27 @@
// renderer/Settings.PaintScaleInputPaths.tsx import { FileAddFilled, FolderOpenFilled, CheckCircleFilled, WarningFilled } from "@ant-design/icons";
import { FolderOpenFilled } from "@ant-design/icons"; import { Button, Card, Input, Select, Space, Table, Tooltip } from "antd";
import { Button, Card, Input, Select, Space, Table } from "antd"; import { FC } from "react";
import { FC, useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json"; import {
PaintScaleConfig,
interface PaintScaleConfig { PaintScaleType,
id: string; paintScaleTypeOptions,
path: string | null; } from "./PaintScale/types";
type: PaintScaleType; import { usePaintScaleConfig } from "./PaintScale/usePaintScaleConfig";
}
enum PaintScaleType {
PPG = "PPG",
SHERWIN = "SHERWIN",
AKZO = "AKZO",
}
const paintScaleTypeOptions = Object.values(PaintScaleType).map((type) => ({
value: type,
label: type,
}));
const SettingsPaintScaleInputPaths: FC = () => { const SettingsPaintScaleInputPaths: FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [paintScaleConfigs, setPaintScaleConfigs] = useState<PaintScaleConfig[]>([]); const {
paintScaleConfigs,
// Load paint scale input configs from store on mount handleAddConfig,
useEffect(() => { handleRemoveConfig,
window.electron.ipcRenderer handlePathChange,
.invoke(ipcTypes.toMain.settings.paintScale.getInputConfigs) handleTypeChange,
.then((configs: PaintScaleConfig[]) => { handlePollingIntervalChange,
setPaintScaleConfigs(configs || []); } = usePaintScaleConfig("input");
})
.catch((error) => {
console.error("Failed to load paint scale input configs:", error);
});
}, []);
// Handle adding a new paint scale config
const handleAddConfig = () => {
const newConfig: PaintScaleConfig = {
id: Date.now().toString(),
path: null,
type: PaintScaleType.PPG,
};
const updatedConfigs = [...paintScaleConfigs, newConfig];
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle removing a config
const handleRemoveConfig = (id: string) => {
const updatedConfigs = paintScaleConfigs.filter((config) => config.id !== id);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle path selection
const handlePathChange = (id: string) => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.paintScale.setInputPath, id)
.then((path: string | null) => {
if (path) {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, path } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
}
})
.catch((error) => {
console.error("Failed to set paint scale input path:", error);
});
};
// Handle type change
const handleTypeChange = (id: string, type: PaintScaleType) => {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, type } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Save configs to store
const saveConfigs = (configs: PaintScaleConfig[]) => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.paintScale.setInputConfigs, configs)
.catch((error) => {
console.error("Failed to save paint scale input configs:", error);
});
};
// Table columns for paint scale configs // Table columns for paint scale configs
const columns = [ const columns = [
{
title: t("settings.labels.paintScalePath"),
dataIndex: "path",
key: "path",
render: (path: string | null, record: PaintScaleConfig) => (
<Space>
<Input
value={path || ""}
placeholder={t("settings.labels.paintScalePath")}
disabled
/>
<Button
onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />}
/>
</Space>
),
},
{ {
title: t("settings.labels.paintScaleType"), title: t("settings.labels.paintScaleType"),
dataIndex: "type", dataIndex: "type",
@@ -126,6 +35,55 @@ const SettingsPaintScaleInputPaths: FC = () => {
/> />
), ),
}, },
{
title: t("settings.labels.paintScalePath"),
dataIndex: "path",
key: "path",
render: (path: string | null, record: PaintScaleConfig) => {
const isValid = path && path.trim() !== ""; // Simple validity check
return (
<Space>
<Input
value={path || ""}
placeholder={t("settings.labels.paintScalePath")}
disabled
style={{
borderColor: isValid ? "#52c41a" : "#d9d9d9", // Green for valid, default for invalid
}}
suffix={
<Tooltip title={isValid ? t("settings.labels.validPath") : t("settings.labels.invalidPath")}>
{isValid ? (
<CheckCircleFilled style={{ color: "#52c41a" }} />
) : (
<WarningFilled style={{ color: "#faad14" }} />
)}
</Tooltip>
}
/>
<Button
onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />}
/>
</Space>
);
},
},
{
title: t("settings.labels.pollingInterval"),
dataIndex: "pollingInterval",
key: "pollingInterval",
render: (pollingInterval: number, record: PaintScaleConfig) => (
<Input
type="number"
value={pollingInterval}
onChange={(e) =>
handlePollingIntervalChange(record.id, Number(e.target.value))
}
style={{ width: 100 }}
placeholder={t("settings.labels.pollingInterval")}
/>
),
},
{ {
title: t("settings.labels.actions"), title: t("settings.labels.actions"),
key: "actions", key: "actions",
@@ -138,18 +96,20 @@ const SettingsPaintScaleInputPaths: FC = () => {
]; ];
return ( return (
<Card title={t("settings.labels.paintScaleSettingsInput")}> <Card
<Space direction="vertical" style={{ width: "100%" }}> title={t("settings.labels.paintScaleSettingsInput")}
<Button type="primary" onClick={handleAddConfig}> extra={
{t("settings.labels.addPaintScalePath")} <Button icon={<FileAddFilled />} onClick={handleAddConfig}>
</Button> {t("settings.actions.addpath")}
<Table </Button>
dataSource={paintScaleConfigs} }
columns={columns} >
rowKey="id" <Table
pagination={false} dataSource={paintScaleConfigs}
/> columns={columns}
</Space> rowKey="id"
pagination={false}
/>
</Card> </Card>
); );
}; };

View File

@@ -1,118 +1,27 @@
// renderer/Settings.PaintScaleOutputPaths.tsx import { FileAddFilled, FolderOpenFilled, CheckCircleFilled, WarningFilled } from "@ant-design/icons";
import { FolderOpenFilled } from "@ant-design/icons";
import { Button, Card, Input, Select, Space, Table } from "antd"; import { Button, Card, Input, Select, Space, Table } from "antd";
import { FC, useEffect, useState } from "react"; import { FC } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import ipcTypes from "../../../../util/ipcTypes.json"; import {
PaintScaleConfig,
interface PaintScaleConfig { PaintScaleType,
id: string; paintScaleTypeOptions,
path: string | null; } from "./PaintScale/types";
type: PaintScaleType; import { usePaintScaleConfig } from "./PaintScale/usePaintScaleConfig";
}
enum PaintScaleType {
PPG = "PPG",
SHERWIN = "SHERWIN",
AKZO = "AKZO",
}
const paintScaleTypeOptions = Object.values(PaintScaleType).map((type) => ({
value: type,
label: type,
}));
const SettingsPaintScaleOutputPaths: FC = () => { const SettingsPaintScaleOutputPaths: FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [paintScaleConfigs, setPaintScaleConfigs] = useState<PaintScaleConfig[]>([]); const {
paintScaleConfigs,
// Load paint scale output configs from store on mount handleAddConfig,
useEffect(() => { handleRemoveConfig,
window.electron.ipcRenderer handlePathChange,
.invoke(ipcTypes.toMain.settings.paintScale.getOutputConfigs) handleTypeChange,
.then((configs: PaintScaleConfig[]) => { handlePollingIntervalChange,
setPaintScaleConfigs(configs || []); } = usePaintScaleConfig("output");
})
.catch((error) => {
console.error("Failed to load paint scale output configs:", error);
});
}, []);
// Handle adding a new paint scale config
const handleAddConfig = () => {
const newConfig: PaintScaleConfig = {
id: Date.now().toString(),
path: null,
type: PaintScaleType.PPG,
};
const updatedConfigs = [...paintScaleConfigs, newConfig];
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle removing a config
const handleRemoveConfig = (id: string) => {
const updatedConfigs = paintScaleConfigs.filter((config) => config.id !== id);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Handle path selection
const handlePathChange = (id: string) => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.paintScale.setOutputPath, id)
.then((path: string | null) => {
if (path) {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, path } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
}
})
.catch((error) => {
console.error("Failed to set paint scale output path:", error);
});
};
// Handle type change
const handleTypeChange = (id: string, type: PaintScaleType) => {
const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, type } : config,
);
setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs);
};
// Save configs to store
const saveConfigs = (configs: PaintScaleConfig[]) => {
window.electron.ipcRenderer
.invoke(ipcTypes.toMain.settings.paintScale.setOutputConfigs, configs)
.catch((error) => {
console.error("Failed to save paint scale output configs:", error);
});
};
// Table columns for paint scale configs // Table columns for paint scale configs
const columns = [ const columns = [
{
title: t("settings.labels.paintScalePath"),
dataIndex: "path",
key: "path",
render: (path: string | null, record: PaintScaleConfig) => (
<Space>
<Input
value={path || ""}
placeholder={t("settings.labels.paintScalePath")}
disabled
/>
<Button
onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />}
/>
</Space>
),
},
{ {
title: t("settings.labels.paintScaleType"), title: t("settings.labels.paintScaleType"),
dataIndex: "type", dataIndex: "type",
@@ -126,6 +35,53 @@ const SettingsPaintScaleOutputPaths: FC = () => {
/> />
), ),
}, },
{
title: t("settings.labels.paintScalePath"),
dataIndex: "path",
key: "path",
render: (path: string | null, record: PaintScaleConfig) => {
const isValid = path && path.trim() !== ""; // Simple validity check
return (
<Space>
<Input
value={path || ""}
placeholder={t("settings.labels.paintScalePath")}
disabled
style={{
borderColor: isValid ? "#52c41a" : "#d9d9d9", // Green for valid, default for invalid
}}
suffix={
isValid ? (
<CheckCircleFilled style={{ color: "#52c41a" }} />
) : (
<WarningFilled style={{ color: "#faad14" }} />
)
}
/>
<Button
onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />}
/>
</Space>
);
},
},
{
title: t("settings.labels.pollingInterval"),
dataIndex: "pollingInterval",
key: "pollingInterval",
render: (pollingInterval: number, record: PaintScaleConfig) => (
<Input
type="number"
value={pollingInterval}
onChange={(e) =>
handlePollingIntervalChange(record.id, Number(e.target.value))
}
style={{ width: 100 }}
placeholder={t("settings.labels.pollingInterval")}
/>
),
},
{ {
title: t("settings.labels.actions"), title: t("settings.labels.actions"),
key: "actions", key: "actions",
@@ -138,18 +94,20 @@ const SettingsPaintScaleOutputPaths: FC = () => {
]; ];
return ( return (
<Card title={t("settings.labels.paintScaleSettingsOutput")}> <Card
<Space direction="vertical" style={{ width: "100%" }}> title={t("settings.labels.paintScaleSettingsOutput")}
<Button type="primary" onClick={handleAddConfig}> extra={
{t("settings.labels.addPaintScalePath")} <Button onClick={handleAddConfig} icon={<FileAddFilled />}>
</Button> {t("settings.actions.addpath")}
<Table </Button>
dataSource={paintScaleConfigs} }
columns={columns} >
rowKey="id" <Table
pagination={false} dataSource={paintScaleConfigs}
/> columns={columns}
</Space> rowKey="id"
pagination={false}
/>
</Card> </Card>
); );
}; };

View File

@@ -1,13 +1,13 @@
import { import {
ApolloClient, ApolloClient,
ApolloLink, ApolloLink,
HttpLink, // HttpLink,
InMemoryCache, InMemoryCache,
} from "@apollo/client"; } from "@apollo/client";
const httpLink: HttpLink = new HttpLink({ // const httpLink: HttpLink = new HttpLink({
uri: import.meta.env.VITE_GRAPHQL_URL, // uri: import.meta.env.VITE_GRAPHQL_URL,
}); // });
const middlewares = []; const middlewares = [];

View File

@@ -35,13 +35,16 @@
"watchermodepolling": "Polling", "watchermodepolling": "Polling",
"watchermoderealtime": "Real Time", "watchermoderealtime": "Real Time",
"watcherstatus": "Watcher Status", "watcherstatus": "Watcher Status",
"paintScaleSettingsInput": "Paint Scale Settings Input", "paintScaleSettingsInput": "Paint Scale Input Paths",
"paintScaleSettingsOutput": "Paint Scale Settings Output", "paintScaleSettingsOutput": "Paint Scale Output Paths",
"paintScalePath": "Paint Scale Path", "paintScalePath": "Paint Scale Path",
"paintScaleType": "Paint Scale Type", "paintScaleType": "Paint Scale Type",
"addPaintScalePath": "Add Paint Scale Path", "addPaintScalePath": "Add Paint Scale Path",
"remove": "Remove", "remove": "Remove",
"actions": "Actions" "actions": "Actions",
"pollingInterval": "Polling Interval (s)",
"validPath": "Valid path",
"invalidPath": "Path not set or invalid"
} }
}, },
"title": { "title": {