mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 00:09:51 +03:00
add ADD and put in a validator as cmd can't be empty
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import { FC, useState, useEffect, useCallback } from 'react';
|
import { FC, useState, useEffect, useCallback } from 'react';
|
||||||
|
|
||||||
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
import { unstable_useBlocker as useBlocker } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -27,6 +26,7 @@ import CheckIcon from '@mui/icons-material/Check';
|
|||||||
import WarningIcon from '@mui/icons-material/Warning';
|
import WarningIcon from '@mui/icons-material/Warning';
|
||||||
import CancelIcon from '@mui/icons-material/Cancel';
|
import CancelIcon from '@mui/icons-material/Cancel';
|
||||||
import DoneIcon from '@mui/icons-material/Done';
|
import DoneIcon from '@mui/icons-material/Done';
|
||||||
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ValidatedTextField,
|
ValidatedTextField,
|
||||||
@@ -41,9 +41,13 @@ import {
|
|||||||
import * as EMSESP from './api';
|
import * as EMSESP from './api';
|
||||||
|
|
||||||
import { extractErrorMessage, updateValue } from 'utils';
|
import { extractErrorMessage, updateValue } from 'utils';
|
||||||
|
import { validate } from 'validators';
|
||||||
|
import { schedulerItemValidation } from './validators';
|
||||||
|
|
||||||
import { ScheduleItem, ScheduleFlag } from './types';
|
import { ScheduleItem, ScheduleFlag } from './types';
|
||||||
|
|
||||||
|
import Schema, { ValidateFieldsError } from 'async-validator';
|
||||||
|
|
||||||
import { useI18nContext } from 'i18n/i18n-react';
|
import { useI18nContext } from 'i18n/i18n-react';
|
||||||
|
|
||||||
export const APIURL = window.location.origin + '/api/';
|
export const APIURL = window.location.origin + '/api/';
|
||||||
@@ -56,24 +60,27 @@ const SettingsScheduler: FC = () => {
|
|||||||
const [numChanges, setNumChanges] = useState<number>(0);
|
const [numChanges, setNumChanges] = useState<number>(0);
|
||||||
const blocker = useBlocker(numChanges !== 0);
|
const blocker = useBlocker(numChanges !== 0);
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState<string>();
|
|
||||||
|
|
||||||
const emptySchedule = {
|
const emptySchedule = {
|
||||||
id: '',
|
id: '0',
|
||||||
active: false,
|
active: false,
|
||||||
deleted: false,
|
deleted: false,
|
||||||
flags: 0,
|
flags: 0,
|
||||||
time: '',
|
time: '12:00',
|
||||||
cmd: '',
|
cmd: '',
|
||||||
value: '',
|
value: '',
|
||||||
description: ''
|
description: ''
|
||||||
};
|
};
|
||||||
const [schedule, setSchedule] = useState<ScheduleItem[]>([emptySchedule]);
|
const [schedule, setSchedule] = useState<ScheduleItem[]>([emptySchedule]);
|
||||||
const [scheduleItem, setScheduleItem] = useState<ScheduleItem>();
|
const [scheduleItem, setScheduleItem] = useState<ScheduleItem>();
|
||||||
|
|
||||||
const [ntpAvailable, setNTPavailable] = useState<boolean>(true);
|
const [ntpAvailable, setNTPavailable] = useState<boolean>(true);
|
||||||
|
|
||||||
const [dow, setDow] = useState<string[]>([]);
|
const [dow, setDow] = useState<string[]>([]);
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [fieldErrors, setFieldErrors] = useState<ValidateFieldsError>();
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const [flags, setFlags] = useState(() => ['']);
|
||||||
|
|
||||||
function getDayNames() {
|
function getDayNames() {
|
||||||
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });
|
const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' });
|
||||||
@@ -84,9 +91,6 @@ const SettingsScheduler: FC = () => {
|
|||||||
return days.map((date) => formatter.format(date));
|
return days.map((date) => formatter.format(date));
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line
|
|
||||||
const [flags, setFlags] = useState(() => ['']);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNumChanges(getNumChanges());
|
setNumChanges(getNumChanges());
|
||||||
});
|
});
|
||||||
@@ -238,15 +242,15 @@ const SettingsScheduler: FC = () => {
|
|||||||
ntp_available: ntpAvailable,
|
ntp_available: ntpAvailable,
|
||||||
schedule: schedule
|
schedule: schedule
|
||||||
.filter((si) => !si.deleted)
|
.filter((si) => !si.deleted)
|
||||||
.map((new_si) => {
|
.map((condensed_si) => {
|
||||||
return {
|
return {
|
||||||
id: new_si.id,
|
id: condensed_si.id, // will be ignored
|
||||||
active: new_si.active,
|
active: condensed_si.active,
|
||||||
flags: new_si.flags,
|
flags: condensed_si.flags,
|
||||||
time: new_si.time,
|
time: condensed_si.time,
|
||||||
cmd: new_si.cmd,
|
cmd: condensed_si.cmd,
|
||||||
value: new_si.value,
|
value: condensed_si.value,
|
||||||
description: new_si.description
|
description: condensed_si.description
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -301,28 +305,41 @@ const SettingsScheduler: FC = () => {
|
|||||||
si.description = '';
|
si.description = '';
|
||||||
}
|
}
|
||||||
setScheduleItem(si);
|
setScheduleItem(si);
|
||||||
|
setCreating(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
function makeid() {
|
||||||
|
let result = '';
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
let counter = 0;
|
||||||
|
while (counter < 4) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const addScheduleItem = () => {
|
||||||
|
setScheduleItem({
|
||||||
|
id: makeid(), // random ID of 4 strings
|
||||||
|
active: false,
|
||||||
|
deleted: false,
|
||||||
|
flags: 0,
|
||||||
|
time: '12:00',
|
||||||
|
cmd: '',
|
||||||
|
value: '',
|
||||||
|
description: ''
|
||||||
|
});
|
||||||
|
setCreating(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateScheduleItem = () => {
|
const updateScheduleItem = () => {
|
||||||
if (scheduleItem) {
|
if (scheduleItem) {
|
||||||
setSchedule((prevState) => {
|
const new_schedule = [...schedule.filter((si) => si.id !== scheduleItem.id), scheduleItem];
|
||||||
const newState = prevState.map((obj) => {
|
setSchedule(new_schedule);
|
||||||
if (obj.id === scheduleItem.id) {
|
setScheduleItem(undefined);
|
||||||
return {
|
|
||||||
...obj,
|
|
||||||
active: scheduleItem.active,
|
|
||||||
time: scheduleItem.time,
|
|
||||||
cmd: scheduleItem.cmd,
|
|
||||||
value: scheduleItem.value,
|
|
||||||
description: scheduleItem.description
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
});
|
|
||||||
return newState;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
setScheduleItem(undefined);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderSchedule = () => {
|
const renderSchedule = () => {
|
||||||
@@ -401,12 +418,24 @@ const SettingsScheduler: FC = () => {
|
|||||||
updateScheduleItem();
|
updateScheduleItem();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateScheduleItem = async () => {
|
||||||
|
if (scheduleItem) {
|
||||||
|
try {
|
||||||
|
setFieldErrors(undefined);
|
||||||
|
await validate(schedulerItemValidation(scheduleItem, creating), scheduleItem);
|
||||||
|
updateScheduleItem();
|
||||||
|
} catch (errors: any) {
|
||||||
|
setFieldErrors(errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const renderEditSchedule = () => {
|
const renderEditSchedule = () => {
|
||||||
if (scheduleItem) {
|
if (scheduleItem) {
|
||||||
return (
|
return (
|
||||||
<Dialog open={!!scheduleItem} onClose={() => setScheduleItem(undefined)}>
|
<Dialog open={!!scheduleItem} onClose={() => setScheduleItem(undefined)}>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
{LL.EDIT() +
|
{(creating ? LL.ADD(0) : LL.EDIT()) +
|
||||||
' ' +
|
' ' +
|
||||||
((scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER
|
((scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER
|
||||||
? LL.TIMER()
|
? LL.TIMER()
|
||||||
@@ -428,9 +457,8 @@ const SettingsScheduler: FC = () => {
|
|||||||
control={<Checkbox checked={scheduleItem.active} onChange={updateValue(setScheduleItem)} name="active" />}
|
control={<Checkbox checked={scheduleItem.active} onChange={updateValue(setScheduleItem)} name="active" />}
|
||||||
label={LL.ACTIVE()}
|
label={LL.ACTIVE()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER ? (
|
{(scheduleItem.flags & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER ? (
|
||||||
<ValidatedTextField
|
<TextField
|
||||||
name="time"
|
name="time"
|
||||||
label={LL.TIMER()}
|
label={LL.TIMER()}
|
||||||
value={scheduleItem.time}
|
value={scheduleItem.time}
|
||||||
@@ -443,7 +471,7 @@ const SettingsScheduler: FC = () => {
|
|||||||
<MenuItem value={'00:00'}>{LL.SCHEDULE_TIMER_1()}</MenuItem>
|
<MenuItem value={'00:00'}>{LL.SCHEDULE_TIMER_1()}</MenuItem>
|
||||||
<MenuItem value={'00:01'}>{LL.SCHEDULE_TIMER_2()}</MenuItem>
|
<MenuItem value={'00:01'}>{LL.SCHEDULE_TIMER_2()}</MenuItem>
|
||||||
<MenuItem value={'01:00'}>{LL.SCHEDULE_TIMER_3()}</MenuItem>
|
<MenuItem value={'01:00'}>{LL.SCHEDULE_TIMER_3()}</MenuItem>
|
||||||
</ValidatedTextField>
|
</TextField>
|
||||||
) : (
|
) : (
|
||||||
<TextField
|
<TextField
|
||||||
name="time"
|
name="time"
|
||||||
@@ -454,9 +482,9 @@ const SettingsScheduler: FC = () => {
|
|||||||
onChange={updateValue(setScheduleItem)}
|
onChange={updateValue(setScheduleItem)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<ValidatedTextField
|
||||||
<TextField
|
fieldErrors={fieldErrors}
|
||||||
name="command"
|
name="cmd"
|
||||||
label={LL.COMMAND()}
|
label={LL.COMMAND()}
|
||||||
fullWidth
|
fullWidth
|
||||||
value={scheduleItem.cmd}
|
value={scheduleItem.cmd}
|
||||||
@@ -474,16 +502,18 @@ const SettingsScheduler: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}>
|
{!creating && (
|
||||||
<Button
|
<Box flexGrow={1} sx={{ '& button': { mt: 0 } }}>
|
||||||
startIcon={<RemoveIcon />}
|
<Button
|
||||||
variant="outlined"
|
startIcon={<RemoveIcon />}
|
||||||
color="error"
|
variant="outlined"
|
||||||
onClick={() => removeScheduleItem(scheduleItem)}
|
color="error"
|
||||||
>
|
onClick={() => removeScheduleItem(scheduleItem)}
|
||||||
{LL.REMOVE()}
|
>
|
||||||
</Button>
|
{LL.REMOVE()}
|
||||||
</Box>
|
</Button>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<Button
|
<Button
|
||||||
startIcon={<CancelIcon />}
|
startIcon={<CancelIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -496,10 +526,10 @@ const SettingsScheduler: FC = () => {
|
|||||||
startIcon={<DoneIcon />}
|
startIcon={<DoneIcon />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
type="submit"
|
type="submit"
|
||||||
onClick={() => updateScheduleItem()}
|
onClick={() => validateScheduleItem()}
|
||||||
color="primary"
|
color="primary"
|
||||||
>
|
>
|
||||||
{LL.UPDATE()}
|
{creating ? LL.ADD(0) : LL.UPDATE()}
|
||||||
</Button>
|
</Button>
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
@@ -534,6 +564,13 @@ const SettingsScheduler: FC = () => {
|
|||||||
</ButtonRow>
|
</ButtonRow>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box flexWrap="nowrap" whiteSpace="nowrap">
|
||||||
|
<ButtonRow>
|
||||||
|
<Button startIcon={<AddIcon />} variant="outlined" color="secondary" onClick={() => addScheduleItem()}>
|
||||||
|
{LL.ADD(0)}
|
||||||
|
</Button>
|
||||||
|
</ButtonRow>
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Schema, { InternalRuleItem } from 'async-validator';
|
import Schema, { InternalRuleItem } from 'async-validator';
|
||||||
import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared';
|
import { IP_OR_HOSTNAME_VALIDATOR } from 'validators/shared';
|
||||||
import { Settings } from './types';
|
import { Settings, ScheduleItem } from './types';
|
||||||
|
|
||||||
export const GPIO_VALIDATOR = {
|
export const GPIO_VALIDATOR = {
|
||||||
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
validator(rule: InternalRuleItem, value: number, callback: (error?: string) => void) {
|
||||||
@@ -84,3 +84,8 @@ export const createSettingsValidator = (settings: Settings) =>
|
|||||||
shower_alert_coldshot: [{ type: 'number', min: 1, max: 10, message: 'Time must be between 1 and 10 seconds' }]
|
shower_alert_coldshot: [{ type: 'number', min: 1, max: 10, message: 'Time must be between 1 and 10 seconds' }]
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const schedulerItemValidation = (si: ScheduleItem, creating: boolean) =>
|
||||||
|
new Schema({
|
||||||
|
cmd: [{ required: true, message: 'Command is required' }]
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user