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

This commit is contained in:
Dave Richer
2025-04-28 09:26:15 -04:00
parent 1cd0ee4f5c
commit c73db112c7
5 changed files with 276 additions and 173 deletions

View File

@@ -3,7 +3,9 @@
<component name="MaterialThemeProjectNewConfig"> <component name="MaterialThemeProjectNewConfig">
<option name="metadata"> <option name="metadata">
<MTProjectMetadataState> <MTProjectMetadataState>
<option name="userId" value="-70fa916f:1961b191ca1:-748b" /> <option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="-4002d172:18ee315e3ba:-7ffe" />
</MTProjectMetadataState> </MTProjectMetadataState>
</option> </option>
</component> </component>

View File

@@ -8,51 +8,52 @@ export const usePaintScaleConfig = (configType: ConfigType) => {
const [paintScaleConfigs, setPaintScaleConfigs] = useState<PaintScaleConfig[]>([]); const [paintScaleConfigs, setPaintScaleConfigs] = useState<PaintScaleConfig[]>([]);
// Get the appropriate IPC methods based on config type // Get the appropriate IPC methods based on config type
const getConfigsMethod = configType === 'input' const getConfigsMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.getInputConfigs ? ipcTypes.toMain.settings.paintScale.getInputConfigs
: ipcTypes.toMain.settings.paintScale.getOutputConfigs; : ipcTypes.toMain.settings.paintScale.getOutputConfigs;
const setConfigsMethod = configType === 'input' const setConfigsMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.setInputConfigs ? ipcTypes.toMain.settings.paintScale.setInputConfigs
: ipcTypes.toMain.settings.paintScale.setOutputConfigs; : ipcTypes.toMain.settings.paintScale.setOutputConfigs;
const setPathMethod = configType === 'input' const setPathMethod = configType === 'input'
? ipcTypes.toMain.settings.paintScale.setInputPath ? ipcTypes.toMain.settings.paintScale.setInputPath
: ipcTypes.toMain.settings.paintScale.setOutputPath; : ipcTypes.toMain.settings.paintScale.setOutputPath;
// Load paint scale configs on mount // Load paint scale configs on mount
useEffect(() => { useEffect(() => {
window.electron.ipcRenderer window.electron.ipcRenderer
.invoke(getConfigsMethod) .invoke(getConfigsMethod)
.then((configs: PaintScaleConfig[]) => { .then((configs: PaintScaleConfig[]) => {
// Ensure all configs have a pollingInterval (for backward compatibility) // Ensure all configs have a pollingInterval and type (for backward compatibility)
const updatedConfigs = configs.map(config => ({ const updatedConfigs = configs.map(config => ({
...config, ...config,
pollingInterval: config.pollingInterval || 60 // Default to 60 seconds if not set pollingInterval: config.pollingInterval || 1440, // Default to 1440 seconds if not set
})); type: config.type || PaintScaleType.PPG, // Default type if missing
setPaintScaleConfigs(updatedConfigs || []); }));
}) setPaintScaleConfigs(updatedConfigs || []);
.catch((error) => { })
console.error(`Failed to load paint scale ${configType} configs:`, error); .catch((error) => {
}); console.error(`Failed to load paint scale ${configType} configs:`, error);
});
}, [getConfigsMethod]); }, [getConfigsMethod]);
// Save configs to store // Save configs to store
const saveConfigs = (configs: PaintScaleConfig[]) => { const saveConfigs = (configs: PaintScaleConfig[]) => {
window.electron.ipcRenderer window.electron.ipcRenderer
.invoke(setConfigsMethod, configs) .invoke(setConfigsMethod, configs)
.catch((error) => { .catch((error) => {
console.error(`Failed to save paint scale ${configType} configs:`, error); console.error(`Failed to save paint scale ${configType} configs:`, error);
}); });
}; };
// Handle adding a new paint scale config // Handle adding a new paint scale config
const handleAddConfig = () => { const handleAddConfig = (type: PaintScaleType) => {
const newConfig: PaintScaleConfig = { const newConfig: PaintScaleConfig = {
id: Date.now().toString(), id: Date.now().toString(),
path: null, path: null,
type: PaintScaleType.PPG, type,
pollingInterval: 60, // Default to 60 seconds pollingInterval: 1440, // Default to 1440 seconds
}; };
const updatedConfigs = [...paintScaleConfigs, newConfig]; const updatedConfigs = [...paintScaleConfigs, newConfig];
setPaintScaleConfigs(updatedConfigs); setPaintScaleConfigs(updatedConfigs);
@@ -69,34 +70,25 @@ export const usePaintScaleConfig = (configType: ConfigType) => {
// Handle path selection // Handle path selection
const handlePathChange = (id: string) => { const handlePathChange = (id: string) => {
window.electron.ipcRenderer window.electron.ipcRenderer
.invoke(setPathMethod, id) .invoke(setPathMethod, id)
.then((path: string | null) => { .then((path: string | null) => {
if (path) { if (path) {
const updatedConfigs = paintScaleConfigs.map((config) => const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, path } : config, config.id === id ? { ...config, path } : config,
); );
setPaintScaleConfigs(updatedConfigs); setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs); saveConfigs(updatedConfigs);
} }
}) })
.catch((error) => { .catch((error) => {
console.error(`Failed to set paint scale ${configType} path:`, 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 // Handle polling interval change
const handlePollingIntervalChange = (id: string, pollingInterval: number) => { const handlePollingIntervalChange = (id: string, pollingInterval: number) => {
const updatedConfigs = paintScaleConfigs.map((config) => const updatedConfigs = paintScaleConfigs.map((config) =>
config.id === id ? { ...config, pollingInterval } : config, config.id === id ? { ...config, pollingInterval } : config,
); );
setPaintScaleConfigs(updatedConfigs); setPaintScaleConfigs(updatedConfigs);
saveConfigs(updatedConfigs); saveConfigs(updatedConfigs);
@@ -107,8 +99,6 @@ export const usePaintScaleConfig = (configType: ConfigType) => {
handleAddConfig, handleAddConfig,
handleRemoveConfig, handleRemoveConfig,
handlePathChange, handlePathChange,
handleTypeChange, handlePollingIntervalChange,
handlePollingIntervalChange
}; };
}; };

View File

@@ -1,6 +1,21 @@
import { FileAddFilled, FolderOpenFilled, CheckCircleFilled, WarningFilled } from "@ant-design/icons"; import {
import { Button, Card, Input, Select, Space, Table, Tooltip } from "antd"; CheckCircleFilled,
import { FC } from "react"; FileAddFilled,
FolderOpenFilled,
WarningFilled,
} from "@ant-design/icons";
import {
Button,
Card,
Input,
Modal,
Select,
Space,
Table,
Tag,
Tooltip,
} from "antd";
import { FC, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
PaintScaleConfig, PaintScaleConfig,
@@ -16,55 +31,85 @@ const SettingsPaintScaleInputPaths: FC = () => {
handleAddConfig, handleAddConfig,
handleRemoveConfig, handleRemoveConfig,
handlePathChange, handlePathChange,
handleTypeChange,
handlePollingIntervalChange, handlePollingIntervalChange,
} = usePaintScaleConfig("input"); } = usePaintScaleConfig("input");
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectedType, setSelectedType] = useState<PaintScaleType | null>(null);
// Show modal when adding a new path
const showAddPathModal = () => {
setSelectedType(null);
setIsModalVisible(true);
};
// Handle modal confirmation
const handleModalOk = () => {
if (selectedType) {
handleAddConfig(selectedType);
setIsModalVisible(false);
}
};
// Handle modal cancellation
const handleModalCancel = () => {
setIsModalVisible(false);
};
// Table columns for paint scale configs // Table columns for paint scale configs
const columns = [ const columns = [
{ {
title: t("settings.labels.paintScaleType"), title: t("settings.labels.paintScaleType"),
dataIndex: "type", dataIndex: "type",
key: "type", key: "type",
render: (type: PaintScaleType, record: PaintScaleConfig) => ( render: (type: PaintScaleType) => {
<Select const typeOption = paintScaleTypeOptions.find(
value={type} (option) => option.value === type,
options={paintScaleTypeOptions} );
onChange={(value) => handleTypeChange(record.id, value)} const label = typeOption ? typeOption.label : type;
style={{ width: 120 }} const colorMap: Partial<Record<PaintScaleType, string>> = {
/> [PaintScaleType.PPG]: "blue",
), // Add other types and colors as needed
};
return <Tag color={colorMap[type] || "default"}>{label}</Tag>;
},
}, },
{ {
title: t("settings.labels.paintScalePath"), title: t("settings.labels.paintScalePath"),
dataIndex: "path", dataIndex: "path",
key: "path", key: "path",
render: (path: string | null, record: PaintScaleConfig) => { render: (path: string | null, record: PaintScaleConfig) => {
const isValid = path && path.trim() !== ""; // Simple validity check const isValid = path && path.trim() !== "";
return ( return (
<Space> <Space>
<Input <Input
value={path || ""} value={path || ""}
placeholder={t("settings.labels.paintScalePath")} placeholder={t("settings.labels.paintScalePath")}
disabled disabled
style={{ style={{
borderColor: isValid ? "#52c41a" : "#d9d9d9", // Green for valid, default for invalid borderColor: isValid ? "#52c41a" : "#d9d9d9",
}} }}
suffix={ suffix={
<Tooltip title={isValid ? t("settings.labels.validPath") : t("settings.labels.invalidPath")}> <Tooltip
{isValid ? ( title={
<CheckCircleFilled style={{ color: "#52c41a" }} /> isValid
) : ( ? t("settings.labels.validPath")
<WarningFilled style={{ color: "#faad14" }} /> : t("settings.labels.invalidPath")
)}
</Tooltip>
} }
/> >
<Button {isValid ? (
onClick={() => handlePathChange(record.id)} <CheckCircleFilled style={{ color: "#52c41a" }} />
icon={<FolderOpenFilled />} ) : (
/> <WarningFilled style={{ color: "#faad14" }} />
</Space> )}
</Tooltip>
}
/>
<Button
onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />}
/>
</Space>
); );
}, },
}, },
@@ -73,45 +118,63 @@ const SettingsPaintScaleInputPaths: FC = () => {
dataIndex: "pollingInterval", dataIndex: "pollingInterval",
key: "pollingInterval", key: "pollingInterval",
render: (pollingInterval: number, record: PaintScaleConfig) => ( render: (pollingInterval: number, record: PaintScaleConfig) => (
<Input <Input
type="number" type="number"
value={pollingInterval} value={pollingInterval}
onChange={(e) => onChange={(e) =>
handlePollingIntervalChange(record.id, Number(e.target.value)) handlePollingIntervalChange(record.id, Number(e.target.value))
} }
style={{ width: 100 }} style={{ width: 100 }}
placeholder={t("settings.labels.pollingInterval")} placeholder={t("settings.labels.pollingInterval")}
/> />
), ),
}, },
{ {
title: t("settings.labels.actions"), title: t("settings.labels.actions"),
key: "actions", key: "actions",
render: (_: any, record: PaintScaleConfig) => ( render: (_: any, record: PaintScaleConfig) => (
<Button danger onClick={() => handleRemoveConfig(record.id)}> <Button danger onClick={() => handleRemoveConfig(record.id)}>
{t("settings.labels.remove")} {t("settings.labels.remove")}
</Button> </Button>
), ),
}, },
]; ];
return ( return (
<>
<Card <Card
title={t("settings.labels.paintScaleSettingsInput")} title={t("settings.labels.paintScaleSettingsInput")}
extra={ extra={
<Button icon={<FileAddFilled />} onClick={handleAddConfig}> <Button onClick={showAddPathModal} icon={<FileAddFilled />}>
{t("settings.actions.addpath")} {t("settings.actions.addpath")}
</Button> </Button>
} }
> >
<Table <Table
dataSource={paintScaleConfigs} dataSource={paintScaleConfigs}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
pagination={false} pagination={false}
/> />
</Card> </Card>
<Modal
title={t("settings.labels.selectPaintScaleType")}
open={isModalVisible}
onOk={handleModalOk}
onCancel={handleModalCancel}
okButtonProps={{ disabled: !selectedType }}
>
<Select
value={selectedType}
options={paintScaleTypeOptions}
onChange={(value) => setSelectedType(value)}
style={{ width: "100%" }}
placeholder={t("settings.labels.selectPaintScaleType")}
/>
</Modal>
</>
); );
}; };
export default SettingsPaintScaleInputPaths; export default SettingsPaintScaleInputPaths;

View File

@@ -1,6 +1,11 @@
import { FileAddFilled, FolderOpenFilled, CheckCircleFilled, WarningFilled } from "@ant-design/icons"; import {
import { Button, Card, Input, Select, Space, Table } from "antd"; CheckCircleFilled,
import { FC } from "react"; FileAddFilled,
FolderOpenFilled,
WarningFilled,
} from "@ant-design/icons";
import { Button, Card, Input, Modal, Select, Space, Table, Tag } from "antd";
import { FC, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
PaintScaleConfig, PaintScaleConfig,
@@ -16,53 +21,77 @@ const SettingsPaintScaleOutputPaths: FC = () => {
handleAddConfig, handleAddConfig,
handleRemoveConfig, handleRemoveConfig,
handlePathChange, handlePathChange,
handleTypeChange,
handlePollingIntervalChange, handlePollingIntervalChange,
} = usePaintScaleConfig("output"); } = usePaintScaleConfig("output");
const [isModalVisible, setIsModalVisible] = useState(false);
const [selectedType, setSelectedType] = useState<PaintScaleType | null>(null);
// Show modal when adding a new path
const showAddPathModal = () => {
setSelectedType(null);
setIsModalVisible(true);
};
// Handle modal confirmation
const handleModalOk = () => {
if (selectedType) {
handleAddConfig(selectedType);
setIsModalVisible(false);
}
};
// Handle modal cancellation
const handleModalCancel = () => {
setIsModalVisible(false);
};
// Table columns for paint scale configs // Table columns for paint scale configs
const columns = [ const columns = [
{ {
title: t("settings.labels.paintScaleType"), title: t("settings.labels.paintScaleType"),
dataIndex: "type", dataIndex: "type",
key: "type", key: "type",
render: (type: PaintScaleType, record: PaintScaleConfig) => ( render: (type: PaintScaleType) => {
<Select const typeOption = paintScaleTypeOptions.find(
value={type} (option) => option.value === type,
options={paintScaleTypeOptions} );
onChange={(value) => handleTypeChange(record.id, value)} const label = typeOption ? typeOption.label : type;
style={{ width: 120 }} const colorMap: Partial<Record<PaintScaleType, string>> = {
/> [PaintScaleType.PPG]: "blue",
), // Add other types and colors as needed
};
return <Tag color={colorMap[type] || "default"}>{label}</Tag>;
},
}, },
{ {
title: t("settings.labels.paintScalePath"), title: t("settings.labels.paintScalePath"),
dataIndex: "path", dataIndex: "path",
key: "path", key: "path",
render: (path: string | null, record: PaintScaleConfig) => { render: (path: string | null, record: PaintScaleConfig) => {
const isValid = path && path.trim() !== ""; // Simple validity check const isValid = path && path.trim() !== "";
return ( return (
<Space> <Space>
<Input <Input
value={path || ""} value={path || ""}
placeholder={t("settings.labels.paintScalePath")} placeholder={t("settings.labels.paintScalePath")}
disabled disabled
style={{ style={{
borderColor: isValid ? "#52c41a" : "#d9d9d9", // Green for valid, default for invalid borderColor: isValid ? "#52c41a" : "#d9d9d9",
}} }}
suffix={ suffix={
isValid ? ( isValid ? (
<CheckCircleFilled style={{ color: "#52c41a" }} /> <CheckCircleFilled style={{ color: "#52c41a" }} />
) : ( ) : (
<WarningFilled style={{ color: "#faad14" }} /> <WarningFilled style={{ color: "#faad14" }} />
) )
} }
/> />
<Button <Button
onClick={() => handlePathChange(record.id)} onClick={() => handlePathChange(record.id)}
icon={<FolderOpenFilled />} icon={<FolderOpenFilled />}
/> />
</Space> </Space>
); );
}, },
}, },
@@ -71,45 +100,63 @@ const SettingsPaintScaleOutputPaths: FC = () => {
dataIndex: "pollingInterval", dataIndex: "pollingInterval",
key: "pollingInterval", key: "pollingInterval",
render: (pollingInterval: number, record: PaintScaleConfig) => ( render: (pollingInterval: number, record: PaintScaleConfig) => (
<Input <Input
type="number" type="number"
value={pollingInterval} value={pollingInterval}
onChange={(e) => onChange={(e) =>
handlePollingIntervalChange(record.id, Number(e.target.value)) handlePollingIntervalChange(record.id, Number(e.target.value))
} }
style={{ width: 100 }} style={{ width: 100 }}
placeholder={t("settings.labels.pollingInterval")} placeholder={t("settings.labels.pollingInterval")}
/> />
), ),
}, },
{ {
title: t("settings.labels.actions"), title: t("settings.labels.actions"),
key: "actions", key: "actions",
render: (_: any, record: PaintScaleConfig) => ( render: (_: any, record: PaintScaleConfig) => (
<Button danger onClick={() => handleRemoveConfig(record.id)}> <Button danger onClick={() => handleRemoveConfig(record.id)}>
{t("settings.labels.remove")} {t("settings.labels.remove")}
</Button> </Button>
), ),
}, },
]; ];
return ( return (
<>
<Card <Card
title={t("settings.labels.paintScaleSettingsOutput")} title={t("settings.labels.paintScaleSettingsOutput")}
extra={ extra={
<Button onClick={handleAddConfig} icon={<FileAddFilled />}> <Button onClick={showAddPathModal} icon={<FileAddFilled />}>
{t("settings.actions.addpath")} {t("settings.actions.addpath")}
</Button> </Button>
} }
> >
<Table <Table
dataSource={paintScaleConfigs} dataSource={paintScaleConfigs}
columns={columns} columns={columns}
rowKey="id" rowKey="id"
pagination={false} pagination={false}
/> />
</Card> </Card>
<Modal
title={t("settings.labels.selectPaintScaleType")}
open={isModalVisible}
onOk={handleModalOk}
onCancel={handleModalCancel}
okButtonProps={{ disabled: !selectedType }}
>
<Select
value={selectedType}
options={paintScaleTypeOptions}
onChange={(value) => setSelectedType(value)}
style={{ width: "100%" }}
placeholder={t("settings.labels.selectPaintScaleType")}
/>
</Modal>
</>
); );
}; };
export default SettingsPaintScaleOutputPaths; export default SettingsPaintScaleOutputPaths;

View File

@@ -42,9 +42,10 @@
"addPaintScalePath": "Add Paint Scale Path", "addPaintScalePath": "Add Paint Scale Path",
"remove": "Remove", "remove": "Remove",
"actions": "Actions", "actions": "Actions",
"pollingInterval": "Polling Interval (s)", "pollingInterval": "Polling Interval (m)",
"validPath": "Valid path", "validPath": "Valid path",
"invalidPath": "Path not set or invalid" "invalidPath": "Path not set or invalid",
"selectPaintScaleType": "Select Paint Scale Type"
} }
}, },
"title": { "title": {