From cfcc84d0c488ce51a996474e0012a1ce67854310 Mon Sep 17 00:00:00 2001 From: proddy Date: Sun, 7 Jun 2026 12:37:34 +0200 Subject: [PATCH] call scheduler immediate from dashboard --- interface/src/app/main/Dashboard.tsx | 51 ++++--- interface/src/app/main/DevicesDialog.tsx | 153 ++++++++++++--------- interface/src/app/main/SchedulerDialog.tsx | 10 +- interface/src/i18n/cz/index.ts | 3 +- interface/src/i18n/de/index.ts | 3 +- interface/src/i18n/en/index.ts | 3 +- interface/src/i18n/fr/index.ts | 3 +- interface/src/i18n/it/index.ts | 3 +- interface/src/i18n/nl/index.ts | 3 +- interface/src/i18n/no/index.ts | 3 +- interface/src/i18n/pl/index.ts | 3 +- interface/src/i18n/sk/index.ts | 3 +- interface/src/i18n/sv/index.ts | 3 +- interface/src/i18n/tr/index.ts | 3 +- mock-api/restServer.ts | 35 +++-- src/web/WebDataService.cpp | 21 +-- src/web/WebSchedulerService.cpp | 14 +- src/web/WebSchedulerService.h | 2 +- 18 files changed, 195 insertions(+), 124 deletions(-) diff --git a/interface/src/app/main/Dashboard.tsx b/interface/src/app/main/Dashboard.tsx index 964f68e43..2ac3ee729 100644 --- a/interface/src/app/main/Dashboard.tsx +++ b/interface/src/app/main/Dashboard.tsx @@ -7,6 +7,7 @@ import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import EditIcon from '@mui/icons-material/Edit'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import HelpOutlineIcon from '@mui/icons-material/HelpOutlined'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import UnfoldLessIcon from '@mui/icons-material/UnfoldLess'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import { @@ -75,24 +76,25 @@ const Dashboard = memo(() => { { immediate: false } - ); + ) + .onSuccess(() => { + toast.success(LL.WRITE_CMD_SENT()); + }) + .onError((error) => { + toast.error(String(error.error?.message || 'An error occurred')); + }); const deviceValueDialogSave = async (devicevalue: DeviceValue) => { if (!selectedDashboardItem) { return; } - const id = selectedDashboardItem.parentNode.id; // this is the parent ID - await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v }) - .then(() => { - toast.success(LL.WRITE_CMD_SENT()); - }) - .catch((error: Error) => { - toast.error(error.message); - }) - .finally(() => { - setDeviceValueDialogOpen(false); - setSelectedDashboardItem(undefined); - }); + // skip if we're executing an immediate schedule + if (devicevalue.v !== undefined) { + const id = selectedDashboardItem.parentNode.id; // this is the parent ID + await sendDeviceValue({ id, c: devicevalue.c ?? '', v: devicevalue.v }); + } + setDeviceValueDialogOpen(false); + setSelectedDashboardItem(undefined); }; const dashboard_theme = useTheme({ @@ -210,7 +212,12 @@ const Dashboard = memo(() => { (parseInt(id.slice(0, 2), 16) & mask) === mask; const editDashboardValue = (di: DashboardItem) => { - if (me.admin && di.dv?.c) { + // don't execute on parent nodes + if (!me.admin || di.id <= 99) { + return; + } + + if (di.dv?.c) { setSelectedDashboardItem(di); setDeviceValueDialogOpen(true); } @@ -321,7 +328,19 @@ const Dashboard = memo(() => { {me.admin && di.dv?.c && - !hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && ( + !hasMask(di.dv.id, DeviceEntityMask.DV_READONLY) && + (di.dv.v === '' || di.dv.v === undefined ? ( + editDashboardValue(di)} + > + + + ) : ( { sx={{ fontSize: 16 }} /> - )} + ))} ) : ( diff --git a/interface/src/app/main/DevicesDialog.tsx b/interface/src/app/main/DevicesDialog.tsx index 706c2d608..30fafa6c3 100644 --- a/interface/src/app/main/DevicesDialog.tsx +++ b/interface/src/app/main/DevicesDialog.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; import CancelIcon from '@mui/icons-material/Cancel'; import WarningIcon from '@mui/icons-material/Warning'; @@ -18,7 +19,9 @@ import { Typography } from '@mui/material'; +import { callAction } from '@/api/app'; import { dialogStyle } from 'CustomTheme'; +import { useRequest } from 'alova/client'; import type Schema from 'async-validator'; import type { ValidateFieldsError } from 'async-validator'; import { ValidatedTextField } from 'components'; @@ -61,10 +64,25 @@ const DevicesDialog = ({ } }, [open, selectedItem]); - const save = async () => { + const { send: executeSchedule } = useRequest( + (id: string) => callAction({ action: 'executeSchedule', param: id }), + { immediate: false } + ) + .onSuccess(() => { + toast.success(LL.EXECUTE_SCHEDULE_SENT()); + }) + .onError((error) => { + toast.error(String(error.error?.message || 'An error occurred')); + }); + + const doAction = async () => { try { setFieldErrors(undefined); - await validate(validator, editItem); + if (editItem.v === undefined && editItem.c !== undefined) { + await executeSchedule(editItem.c); + } else { + await validate(validator, editItem); + } onSave(editItem); } catch (error) { setFieldErrors((error as ValidationError).fieldErrors); @@ -100,9 +118,14 @@ const DevicesDialog = ({ return undefined; }; - const isCommand = selectedItem.v === '' && selectedItem.c; + const isCommand = + (selectedItem.v === '' || selectedItem.v === undefined) && + Boolean(selectedItem.c); + const isSchedulerImmediate = selectedItem.v === undefined; const dialogTitle = isCommand - ? LL.RUN_COMMAND() + ? isSchedulerImmediate + ? LL.EXECUTE() + ' ' + LL.SCHEDULE(0) + : LL.RUN_COMMAND() : writeable ? LL.CHANGE_VALUE() : LL.VALUE(0); @@ -118,67 +141,69 @@ const DevicesDialog = ({ {editItem.id.slice(2)} - - - {editItem.l ? ( - - {editItem.l.map((val) => ( - - {val} - - ))} - - ) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? ( - - {setUom(editItem.u)} - - ) - } - }} - /> - ) : ( - + {!isSchedulerImmediate && ( + + + {editItem.l ? ( + + {editItem.l.map((val) => ( + + {val} + + ))} + + ) : editItem.s || editItem.u !== DeviceValueUOM.NONE ? ( + + {setUom(editItem.u)} + + ) + } + }} + /> + ) : ( + + )} + + {writeable && helperText && ( + + {helperText} + )} - {writeable && helperText && ( - - {helperText} - - )} - + )} @@ -202,7 +227,7 @@ const DevicesDialog = ({