import { useCallback, useEffect, useState } from 'react'; import { useBlocker } from 'react-router-dom'; import { toast } from 'react-toastify'; import AddIcon from '@mui/icons-material/Add'; import CancelIcon from '@mui/icons-material/Cancel'; import CircleIcon from '@mui/icons-material/Circle'; import WarningIcon from '@mui/icons-material/Warning'; import { Box, Button, Divider, Stack, Typography } from '@mui/material'; import { Body, Cell, Header, HeaderCell, HeaderRow, Row, Table } from '@table-library/react-table-library/table'; import { useTheme } from '@table-library/react-table-library/theme'; import { updateState, useRequest } from 'alova/client'; import { BlockNavigation, ButtonRow, FormLoader, SectionContent, useLayoutTitle } from 'components'; import { useI18nContext } from 'i18n/i18n-react'; import { readSchedule, writeSchedule } from '../../api/app'; import SettingsSchedulerDialog from './SchedulerDialog'; import { ScheduleFlag } from './types'; import type { Schedule, ScheduleItem } from './types'; import { schedulerItemValidation } from './validators'; const Scheduler = () => { const { LL, locale } = useI18nContext(); const [numChanges, setNumChanges] = useState(0); const blocker = useBlocker(numChanges !== 0); const [selectedScheduleItem, setSelectedScheduleItem] = useState(); const [dow, setDow] = useState([]); const [creating, setCreating] = useState(false); const [dialogOpen, setDialogOpen] = useState(false); useLayoutTitle(LL.SCHEDULER()); const { data: schedule, send: fetchSchedule, error } = useRequest(readSchedule, { initialData: [] }); const { send: updateSchedule } = useRequest( (data: Schedule) => writeSchedule(data), { immediate: false } ); 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 ); } useEffect(() => { 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`); }); setDow(days.map((date) => formatter.format(date))); }, [locale]); const schedule_theme = useTheme({ Table: ` --data-table-library_grid-template-columns: 36px 210px 100px 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-bottom: 1px solid #565656; } &:hover .td { background-color: #177ac9; } ` }); const saveSchedule = async () => { await updateSchedule({ schedule: schedule .filter((si) => !si.deleted) .map((condensed_si) => ({ 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 })) }) .then(() => { toast.success(LL.SCHEDULE_UPDATED()); }) .catch((error: Error) => { toast.error(error.message); }) .finally(async () => { await fetchSchedule(); setNumChanges(0); }); }; const editScheduleItem = useCallback((si: ScheduleItem) => { setCreating(false); setSelectedScheduleItem(si); setDialogOpen(true); if (si.o_name === undefined) { si.o_name = si.name; } }, []); const onDialogClose = () => { setDialogOpen(false); }; const onDialogCancel = async () => { await fetchSchedule().then(() => { setNumChanges(0); }); }; const onDialogSave = (updatedItem: ScheduleItem) => { setDialogOpen(false); void updateState(readSchedule(), (data: ScheduleItem[]) => { const new_data = creating ? [ ...data.filter((si) => creating || si.o_id !== updatedItem.o_id), updatedItem ] : data.map((si) => si.id === updatedItem.id ? { ...si, ...updatedItem } : si ); setNumChanges(new_data.filter((si) => hasScheduleChanged(si)).length); return new_data; }); }; const addScheduleItem = () => { setCreating(true); setSelectedScheduleItem({ id: Math.floor(Math.random() * (Math.floor(200) - 100) + 100), active: false, deleted: false, flags: ScheduleFlag.SCHEDULE_DAY, time: '', cmd: '', value: '', name: '' }); setDialogOpen(true); }; const renderSchedule = () => { if (!schedule) { return ; } const dayBox = (si: ScheduleItem, flag: number) => ( <> {dow[Math.log(flag) / Math.log(2)]} ); const scheduleType = (si: ScheduleItem) => ( {si.flags === ScheduleFlag.SCHEDULE_IMMEDIATE ? ( <>Immediate ) : si.flags === ScheduleFlag.SCHEDULE_TIMER ? ( <>Timer ) : si.flags === ScheduleFlag.SCHEDULE_CONDITION ? ( <>Condition ) : si.flags === ScheduleFlag.SCHEDULE_ONCHANGE ? ( <>On Change ) : ( <> )} ); return ( !si.deleted) .sort((a, b) => a.flags - b.flags) }} theme={schedule_theme} layout={{ custom: true }} > {(tableList: ScheduleItem[]) => ( <>
{LL.SCHEDULE(0)} {LL.TIME(0)}/Cond. {LL.COMMAND(0)} {LL.VALUE(0)} {LL.NAME(0)}
{tableList.map((si: ScheduleItem) => ( editScheduleItem(si)}> {si.active ? ( ) : ( )} {si.flags > 127 ? ( scheduleType(si) ) : ( <> {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)} )} {si.time} {si.cmd} {si.value} {si.name} ))} )}
); }; return ( {blocker ? : null} {LL.SCHEDULER_HELP_1()}. {renderSchedule()} {selectedScheduleItem && ( )} {numChanges !== 0 && ( )} ); }; export default Scheduler;