import { FC, useState, useEffect, useCallback } from 'react'; import { unstable_useBlocker as useBlocker } from 'react-router-dom'; import { Button, Typography, Box, Stack, Dialog, DialogActions, DialogContent, DialogTitle, ToggleButton, ToggleButtonGroup, Checkbox, Grid, TextField, Divider } from '@mui/material'; import { useTheme } from '@table-library/react-table-library/theme'; import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table'; import { toast } from 'react-toastify'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import WarningIcon from '@mui/icons-material/Warning'; import CancelIcon from '@mui/icons-material/Cancel'; import DoneIcon from '@mui/icons-material/Done'; import AddIcon from '@mui/icons-material/Add'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { ValidatedTextField, ButtonRow, FormLoader, BlockFormControlLabel, SectionContent, BlockNavigation } from 'components'; import { extractErrorMessage, updateValue } from 'utils'; import { validate } from 'validators'; import { schedulerItemValidation } from './validators'; import { ValidateFieldsError } from 'async-validator'; import { ScheduleItem, ScheduleFlag } from './types'; import { useI18nContext } from 'i18n/i18n-react'; import * as EMSESP from './api'; function makeid() { return Math.floor(Math.random() * (Math.floor(200) - 100) + 100); } const SettingsScheduler: FC = () => { const { LL, locale } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); const emptySchedule = { id: 0, active: false, deleted: false, flags: 0, time: '12:00', cmd: '', value: '', name: '', o_name: '' }; const [schedule, setSchedule] = useState([emptySchedule]); const [scheduleItem, setScheduleItem] = useState(); const [dow, setDow] = useState([]); const [errorMessage, setErrorMessage] = useState(); const [creating, setCreating] = useState(false); const [fieldErrors, setFieldErrors] = useState(); // eslint-disable-next-line const [flags, setFlags] = useState(() => ['']); function getDayNames() { const formatter = new Intl.DateTimeFormat(locale, { weekday: 'short', timeZone: 'UTC' }); const days = [1, 2, 3, 4, 5, 6, 7].map((day) => { const dd = day < 10 ? `0${day}` : day; return new Date(`2017-01-${dd}T00:00:00+00:00`); }); return days.map((date) => formatter.format(date)); } function hasScheduleChanged(si: ScheduleItem) { return ( si.id !== si.o_id || (si?.name || '') !== (si?.o_name || '') || si.active !== si.o_active || si.deleted !== si.o_deleted || si.flags !== si.o_flags || si.time !== si.o_time || si.cmd !== si.o_cmd || si.value !== si.o_value ); } const getNumChanges = () => { if (!schedule) { return 0; } return schedule.filter((si) => hasScheduleChanged(si)).length; }; useEffect(() => { setNumChanges(getNumChanges()); }); const schedule_theme = useTheme({ Table: ` --data-table-library_grid-template-columns: 36px 324px 50px 192px repeat(1, minmax(100px, 1fr)) 160px; `, BaseRow: ` font-size: 14px; .td { height: 32px; } `, BaseCell: ` &:nth-of-type(2) { text-align: center; } &:nth-of-type(1) { text-align: center; } `, HeaderRow: ` text-transform: uppercase; background-color: black; color: #90CAF9; .th { border-bottom: 1px solid #565656; height: 36px; } `, Row: ` background-color: #1e1e1e; position: relative; cursor: pointer; .td { border-top: 1px solid #565656; border-bottom: 1px solid #565656; } &:hover .td { border-top: 1px solid #177ac9; border-bottom: 1px solid #177ac9; } &:nth-of-type(odd) .td { background-color: #303030; } ` }); const setOriginalSchedule = (data: ScheduleItem[]) => { setSchedule( data.map((si) => ({ ...si, o_id: si.id, o_active: si.active, o_deleted: si.deleted, o_flags: si.flags, o_time: si.time, o_cmd: si.cmd, o_value: si.value, o_name: si.name })) ); }; const fetchSchedule = useCallback(async () => { try { const response = await EMSESP.readSchedule(); setOriginalSchedule(response.data.schedule); } catch (error) { setErrorMessage(extractErrorMessage(error, LL.PROBLEM_LOADING())); } setDow(getDayNames()); }, [LL]); useEffect(() => { fetchSchedule(); }, [fetchSchedule]); const getFlagNumber = (newFlag: string[]) => { let new_flag = 0; for (const entry of newFlag) { new_flag |= Number(entry); } return new_flag; }; const getFlagString = (f: number) => { const new_flags: string[] = []; if ((f & 1) === 1) { new_flags.push('1'); } if ((f & 2) === 2) { new_flags.push('2'); } if ((f & 4) === 4) { new_flags.push('4'); } if ((f & 8) === 8) { new_flags.push('8'); } if ((f & 16) === 16) { new_flags.push('16'); } if ((f & 32) === 32) { new_flags.push('32'); } if ((f & 64) === 64) { new_flags.push('64'); } if ((f & 128) === 128) { new_flags.push('128'); } return new_flags; }; const saveSchedule = async () => { if (schedule) { try { const response = await EMSESP.writeSchedule({ schedule: schedule .filter((si) => !si.deleted) .map((condensed_si) => { return { id: condensed_si.id, active: condensed_si.active, flags: condensed_si.flags, time: condensed_si.time, cmd: condensed_si.cmd, value: condensed_si.value, name: condensed_si.name }; }) }); if (response.status === 200) { toast.success(LL.SCHEDULE_SAVED()); } else { toast.error(LL.PROBLEM_UPDATING()); } } catch (error) { toast.error(extractErrorMessage(error, LL.PROBLEM_UPDATING())); } setOriginalSchedule(schedule); } }; function getFlagName(flag: number) { if ((flag & ScheduleFlag.SCHEDULE_MON) === ScheduleFlag.SCHEDULE_MON) { return dow[1]; } if ((flag & ScheduleFlag.SCHEDULE_TUE) === ScheduleFlag.SCHEDULE_TUE) { return dow[2]; } if ((flag & ScheduleFlag.SCHEDULE_WED) === ScheduleFlag.SCHEDULE_WED) { return dow[3]; } if ((flag & ScheduleFlag.SCHEDULE_THU) === ScheduleFlag.SCHEDULE_THU) { return dow[4]; } if ((flag & ScheduleFlag.SCHEDULE_FRI) === ScheduleFlag.SCHEDULE_FRI) { return dow[5]; } if ((flag & ScheduleFlag.SCHEDULE_SAT) === ScheduleFlag.SCHEDULE_SAT) { return dow[6]; } if ((flag & ScheduleFlag.SCHEDULE_SUN) === ScheduleFlag.SCHEDULE_SUN) { return dow[0]; } if ((flag & ScheduleFlag.SCHEDULE_TIMER) === ScheduleFlag.SCHEDULE_TIMER) { return LL.TIMER(0); } return ''; } const dayBox = (si: ScheduleItem, flag: number) => ( <> {getFlagName(flag)} ); const showFlag = (si: ScheduleItem, flag: number) => ( {getFlagName(flag)} ); const editScheduleItem = (si: ScheduleItem) => { if (si.name === undefined) { si.name = ''; } setCreating(false); setScheduleItem(si); }; const addScheduleItem = () => { setCreating(true); setScheduleItem({ id: makeid(), active: false, deleted: false, flags: 0, time: '12:00', cmd: '', value: '', name: '' }); }; const updateScheduleItem = () => { if (scheduleItem) { setSchedule([...schedule.filter((si) => creating || si.o_id !== scheduleItem.o_id), scheduleItem]); } setScheduleItem(undefined); }; const renderSchedule = () => { if (!schedule) { return ; } return ( !si.deleted).sort((a, b) => a.time.localeCompare(b.time)) }} theme={schedule_theme} layout={{ custom: true }} > {(tableList: any) => ( <>
{LL.SCHEDULE(0)} {LL.TIME(0)} {LL.COMMAND(0)} {LL.VALUE(0)} {LL.NAME(0)}
{tableList.map((si: ScheduleItem) => ( editScheduleItem(si)}> {si.active && } {dayBox(si, ScheduleFlag.SCHEDULE_MON)} {dayBox(si, ScheduleFlag.SCHEDULE_TUE)} {dayBox(si, ScheduleFlag.SCHEDULE_WED)} {dayBox(si, ScheduleFlag.SCHEDULE_THU)} {dayBox(si, ScheduleFlag.SCHEDULE_FRI)} {dayBox(si, ScheduleFlag.SCHEDULE_SAT)} {dayBox(si, ScheduleFlag.SCHEDULE_SUN)} {dayBox(si, ScheduleFlag.SCHEDULE_TIMER)} {si.time} {si.cmd} {si.value} {si.name} ))} )}
); }; const removeScheduleItem = (si: ScheduleItem) => { si.deleted = true; setScheduleItem(si); updateScheduleItem(); }; const validateScheduleItem = async () => { if (scheduleItem) { try { setFieldErrors(undefined); await validate(schedulerItemValidation(schedule, scheduleItem), scheduleItem); updateScheduleItem(); } catch (errors: any) { setFieldErrors(errors); } } }; const closeDialog = () => { setScheduleItem(undefined); setFieldErrors(undefined); }; const renderEditSchedule = () => { if (scheduleItem) { const isTimer = scheduleItem.flags === ScheduleFlag.SCHEDULE_TIMER; return ( closeDialog()}> {creating ? LL.ADD(1) + ' ' + LL.NEW() : LL.EDIT()} {LL.SCHEDULE(1)} { scheduleItem.flags = getFlagNumber(flag) & 127; setFlags(['']); // forces refresh }} > {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_MON)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_TUE)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_WED)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_THU)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_FRI)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_SAT)} {showFlag(scheduleItem, ScheduleFlag.SCHEDULE_SUN)} {isTimer ? ( ) : ( )} } label={LL.ACTIVE()} /> {scheduleItem.active && ( )} {isTimer && ( {LL.SCHEDULER_HELP_2()} )} {!creating && ( )} ); } }; return ( {blocker ? : null} {LL.SCHEDULER_HELP_1()} {renderSchedule()} {renderEditSchedule()} {numChanges !== 0 && ( )} ); }; export default SettingsScheduler;