From 3750d5d7eeadf7f3fa774386b1bf5c883972ce21 Mon Sep 17 00:00:00 2001 From: James Bithell Date: Fri, 14 Jul 2023 20:14:16 +0100 Subject: [PATCH] v2.4 Feature - Time Clock Triggers - Scheduled Presets (#163) * bump * TS Docs aren't actually provided as far as I can see * Bump * Add database structure * Add edit page * Add config option to macro preset * Allow presets to be recalled over http * Bump version * Improve locked message, and fix logic error in web server * Basic work on time clock triggers * Remove time clock triggers * Lint * Remove stats file * Lint * Update folder.ts * Add badge showing preset type * Add parent name to folders * Add duplicate preset button * Refactor folder icons to allow them to be used with other buttons * Add preset icons, and make icons searchable * Add more icons * Improve admin pin entry * Update settings.json * Start UI * Bump * Fix imports * Add more ui * Remove webpack cache to fix database error in https://github.com/electron-userland/electron-forge/issues/2412#issuecomment-1013740102 * Radically simplify icon file * Basic UI * UI tweaks * Edit functionality complete * Build out function * Update NavbarItem.tsx * Fix bugs * Time clock trigger bugfixes * Clarify skip behaviour * Lint * Closes https://github.com/Paradise-Pi/ParadisePi/issues/164 by removing effect mode * Update index.ts * Add error handling to sampling mode * Add docs --- .github/pull_request_template.md | 6 + .vscode/launch.json | 4 + .../user-guide/admin/scheduled-presets.md | 23 ++ .../admin/admin-scheduled-presets-cog.png | Bin 0 -> 50121 bytes .../admin/admin-scheduled-presets.png | Bin 0 -> 33957 bytes package-lock.json | 4 +- package.json | 2 +- src/api/config/configRouter.ts | 2 +- src/api/database.ts | 15 +- src/api/preset/presetRouter.ts | 6 +- src/api/router.ts | 15 +- .../timeClockTriggers/timeClockTriggers.ts | 29 ++ .../Presets/EditModal/TimeClockTriggers.tsx | 104 +++++++ .../Components/ControlPanel/ButtonIcon.tsx | 167 ++++++----- src/app/Components/Locked.tsx | 7 +- src/app/Navigation/AdminNavigation.tsx | 34 ++- src/app/Navigation/ControlPanelNavigation.tsx | 19 +- src/app/Navigation/NavbarItem.tsx | 5 +- src/app/Navigation/Styles.tsx | 8 +- src/app/Pages/Admin/Folders.tsx | 32 +-- .../Pages/Admin/ModuleConfiguration/E131.tsx | 18 +- src/app/Pages/Admin/Presets.tsx | 54 ++-- src/app/Pages/Admin/TimeClockTriggers.tsx | 261 ++++++++++++++++++ src/app/Pages/ControlPanel/Preset.tsx | 14 +- src/app/app.css | 6 - src/app/index.html | 1 + src/app/router.tsx | 2 + src/database/dataSource.ts | 11 +- .../1686937486497-TimeClockTriggers.ts | 15 + src/database/model/Preset.ts | 5 + src/database/model/TimeClockTrigger.ts | 105 +++++++ src/database/repository/preset.ts | 2 +- src/database/repository/timeClockTrigger.ts | 120 ++++++++ src/globals.d.ts | 1 + src/index.ts | 17 +- src/logger/index.ts | 9 +- src/output/e131/constructor.ts | 3 +- src/output/e131/index.ts | 89 +++--- src/output/osc/meterFunctions.ts | 1 + src/timeClockTriggerRunner.ts | 38 +++ webpack.main.config.ts | 1 + webpack.plugins.ts | 2 +- 42 files changed, 1024 insertions(+), 233 deletions(-) create mode 100644 docs/docs/user-guide/admin/scheduled-presets.md create mode 100644 docs/static/img/tutorial/admin/admin-scheduled-presets-cog.png create mode 100644 docs/static/img/tutorial/admin/admin-scheduled-presets.png create mode 100644 src/api/timeClockTriggers/timeClockTriggers.ts create mode 100644 src/app/Components/Admin/Controls/Presets/EditModal/TimeClockTriggers.tsx create mode 100644 src/app/Pages/Admin/TimeClockTriggers.tsx create mode 100644 src/database/migration/1686937486497-TimeClockTriggers.ts create mode 100644 src/database/model/TimeClockTrigger.ts create mode 100644 src/database/repository/timeClockTrigger.ts create mode 100644 src/timeClockTriggerRunner.ts diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 308ae6ca..b98989c9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -28,6 +28,12 @@ Please describe the changes - [ ] Preset/Faders/Folders sort order is maintained - [ ] Presets can be triggered by HTTP requests +### Preset Triggers + +- [ ] Presets can be triggered by HTTP requests +- [ ] Time clock triggers recall presets successfully +- [ ] Time clock triggers do not recall when device locked + ### Preset Types #### sACN diff --git a/.vscode/launch.json b/.vscode/launch.json index b0d7899c..d2c0c956 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,6 +17,10 @@ "DEV_MODE": "true" }, "console": "integratedTerminal", + "skipFiles": [ + "/**/*.js", + "${workspaceFolder}/node_modules/**/*.js", + ] } ] } diff --git a/docs/docs/user-guide/admin/scheduled-presets.md b/docs/docs/user-guide/admin/scheduled-presets.md new file mode 100644 index 00000000..38b2cb20 --- /dev/null +++ b/docs/docs/user-guide/admin/scheduled-presets.md @@ -0,0 +1,23 @@ +--- +sidebar_position: 30 +title: Scheduled Presets +--- + +The Scheduled Presets page allows you to define times of the day when presets are triggered automatically. + +To start, use the plus button to add a new schedule. Next, select the days of the week you'd like the schedule to run on, and the time to run during those days. Finally, select the preset you'd like to run at that time, and click save. + +To trigger a preset at multiple times during the day, simply add multiple schedules for that preset. + +![scheduled presets page](@site/static/img/tutorial/admin/admin-scheduled-presets.png) + +The cog icon on the right side of each preset allows you to edit the advanced settings for that preset. + +![scheduled presets cog page](@site/static/img/tutorial/admin/admin-scheduled-presets-cog.png) + +The advanced settings allow you to set the following: + + - Notes: A description of why the schedule exists as a reminder in the administrator menu - this can help you differentiate between large numbers of schedules. + - Enabled: whether the schedule is enabled or not. If disabled, the schedule will not run. + - Run when locked: if the control panel is locked, the schedule will still run if this is enabled. + - Timeout: how many minutes to keep trying to trigger the preset for. If the preset is not triggered within this time, the schedule will be skipped for that day. This is particularly relevant if ParadisePi is powered off at the time the schedule is due to run, and later powered on - if the timeout plus the scheduled time is earlier than the time ParadisePi boots, then it will be skipped. \ No newline at end of file diff --git a/docs/static/img/tutorial/admin/admin-scheduled-presets-cog.png b/docs/static/img/tutorial/admin/admin-scheduled-presets-cog.png new file mode 100644 index 0000000000000000000000000000000000000000..1ba49cb1db7119e16ccdc5ba0450d01557807442 GIT binary patch literal 50121 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 literal 0 HcmV?d00001 diff --git a/package-lock.json b/package-lock.json index ee57c220..d0678f12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paradisepi", - "version": "2.3.0", + "version": "2.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "paradisepi", - "version": "2.3.0", + "version": "2.4.0", "license": "GPL-3.0-only", "dependencies": { "@emotion/react": "^11.11.0", diff --git a/package.json b/package.json index 28b0586d..c4446c0b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paradisepi", - "version": "2.3.0", + "version": "2.4.0", "description": "Raspberry Pi based sACN lighting/OSC sound facility control panel", "main": ".webpack/main", "scripts": { diff --git a/src/api/config/configRouter.ts b/src/api/config/configRouter.ts index d9d102c5..2f789e6b 100644 --- a/src/api/config/configRouter.ts +++ b/src/api/config/configRouter.ts @@ -15,7 +15,7 @@ export const configRouter = ( method: 'GET' | 'POST' | 'PUT' | 'DELETE', payload: apiObject ): Promise => { - logger.debug('Preset router has a request', { path, method, payload }) + logger.debug('Config router has a request', { path, method, payload }) return new Promise((resolve, reject) => { if (method === 'POST') { let restartE131 = false diff --git a/src/api/database.ts b/src/api/database.ts index acab28ad..044f7a7d 100644 --- a/src/api/database.ts +++ b/src/api/database.ts @@ -1,11 +1,12 @@ -import { DatabasePreset, PresetRepository } from './../database/repository/preset' -import { DatabaseFolder, FolderRepository } from './../database/repository/folder' +import ip from 'ip' +import { version } from './../../package.json' import { ConfigRepository } from './../database/repository/config' +import { DatabaseFader, FaderRepository } from './../database/repository/fader' +import { DatabaseFolder, FolderRepository } from './../database/repository/folder' +import { DatabasePreset, PresetRepository } from './../database/repository/preset' +import { DatabaseTimeClockTrigger, TimeClockTriggersRepository } from './../database/repository/timeClockTrigger' import { getOperatingSystemName } from './about/operatingSystem/info' -import { version } from './../../package.json' import { broadcast } from './broadcast' -import ip from 'ip' -import { DatabaseFader, FaderRepository } from './../database/repository/fader' export interface Database { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -42,10 +43,10 @@ export interface Database { e131Frequency: number e131FadeTime: number e131Sampler_time: number - e131Sampler_effectMode: number } } presets: Array + timeClockTriggers: Array folders: { [key: number]: DatabaseFolder } @@ -91,10 +92,10 @@ export const createDatabaseObject = async (message: string): Promise = e131Frequency: parseInt(await ConfigRepository.getItem('e131Frequency')), e131FadeTime: parseInt(await ConfigRepository.getItem('e131FadeTime')), e131Sampler_time: parseInt(await ConfigRepository.getItem('e131Sampler_time')), - e131Sampler_effectMode: parseInt(await ConfigRepository.getItem('e131Sampler_effectMode')), }, }, presets: await PresetRepository.getAll(), + timeClockTriggers: await TimeClockTriggersRepository.getAll(), folders: await FolderRepository.getAll(), faders: await FaderRepository.getAll(), } diff --git a/src/api/preset/presetRouter.ts b/src/api/preset/presetRouter.ts index 05d6ff88..a33a33a8 100644 --- a/src/api/preset/presetRouter.ts +++ b/src/api/preset/presetRouter.ts @@ -1,9 +1,9 @@ -import { Preset } from './../../database/model/Preset' -import { DatabasePreset, PresetRepository } from './../../database/repository/preset' -import { createDatabaseObject, Database, sendDatabaseObject } from './../database' import axios from 'axios' import { parseJSON } from '../parseUserJson' +import { Preset } from './../../database/model/Preset' import { ConfigRepository } from './../../database/repository/config' +import { DatabasePreset, PresetRepository } from './../../database/repository/preset' +import { Database, createDatabaseObject, sendDatabaseObject } from './../database' /** * This is a REST router for the preset API. * @param path - The path requested by the original route requestor diff --git a/src/api/router.ts b/src/api/router.ts index 8ed81b75..421ec43d 100644 --- a/src/api/router.ts +++ b/src/api/router.ts @@ -1,12 +1,13 @@ +import { reboot } from './../electron/windowUtilities' import { aboutRouter } from './about/aboutRouter' -import { createDatabaseObject } from './database' -import { presetRouter } from './preset/presetRouter' -import { folderRouter } from './folder/folderRouter' import { configRouter } from './config/configRouter' -import { outputModulesRouter } from './outputModules/outputModulesRouter' -import { reboot } from './../electron/windowUtilities' +import { createDatabaseObject } from './database' import { faderRouter } from './fader/faderRouter' +import { folderRouter } from './folder/folderRouter' import { createImagesObject } from './images' +import { outputModulesRouter } from './outputModules/outputModulesRouter' +import { presetRouter } from './preset/presetRouter' +import { timeClockTriggersRouter } from './timeClockTriggers/timeClockTriggers' /** * This is a REST router that triages all requests and sends them to relevant routers @@ -52,6 +53,10 @@ export const routeRequest = ( // {@link configRouter} - this router handles all about requests for the /config path resolve(configRouter(pathArr.slice(1), method, payload)) break + case 'timeClockTriggers': + // {@link timeClockTriggers} - the timeClockTriggers router handles all about requests for the /timeClockTriggers path + resolve(timeClockTriggersRouter(pathArr.slice(1), method, payload)) + break case 'outputModules': // {@link outputModulesRouter} - this router handles all about requests for the /outputModules path resolve(outputModulesRouter(pathArr.slice(1), method, payload)) diff --git a/src/api/timeClockTriggers/timeClockTriggers.ts b/src/api/timeClockTriggers/timeClockTriggers.ts new file mode 100644 index 00000000..41593702 --- /dev/null +++ b/src/api/timeClockTriggers/timeClockTriggers.ts @@ -0,0 +1,29 @@ +import { Database, createDatabaseObject, sendDatabaseObject } from '../database' +import { DatabaseTimeClockTrigger, TimeClockTriggersRepository } from './../../database/repository/timeClockTrigger' +/** + * This is a REST router for the preset API. + * @param path - The path requested by the original route requestor + * @param method - The method requested by the original route requestor + * @param payload - Any payload sent + * @returns the retrieved response from the given route + * @throws an error if the requested route is not found + */ +export const timeClockTriggersRouter = ( + path: Array, + method: 'GET' | 'POST' | 'PUT' | 'DELETE', + payload: apiObject +): Promise => { + logger.debug('Time clock trigger router has a request', { path, method, payload }) + return new Promise((resolve, reject) => { + if (method === 'PUT') { + return TimeClockTriggersRepository.setAllFromApp(payload as Array) + .then(() => { + return createDatabaseObject('updating all time clock triggers in bulk') + }) + .then((response: Database) => { + sendDatabaseObject(response) + resolve({}) + }) + } else reject(new Error('Path not found')) + }) +} diff --git a/src/app/Components/Admin/Controls/Presets/EditModal/TimeClockTriggers.tsx b/src/app/Components/Admin/Controls/Presets/EditModal/TimeClockTriggers.tsx new file mode 100644 index 00000000..130148fd --- /dev/null +++ b/src/app/Components/Admin/Controls/Presets/EditModal/TimeClockTriggers.tsx @@ -0,0 +1,104 @@ +import React, { useEffect } from 'react' +import { Group, ActionIcon, Button, Text, Checkbox, NumberInput } from '@mantine/core' +import { useForm } from '@mantine/form' +import { FaTrash } from '@react-icons/all-files/fa/FaTrash' +import { randomId } from '@mantine/hooks' +import { InputProps } from '../../../../InputProps' + +interface Trigger { + time: string + enabled: boolean + timeout: number + countdownWarning: number + key: string +} +interface FormValues { + triggers: Array +} + +export const TimeClockTriggersEditor = (props: InputProps) => { + const form = useForm({ + initialValues: { + triggers: [], + }, + }) + useEffect(() => { + if (props.value !== null) { + const valueObject = JSON.parse(props.value) || {} + form.setValues({ + triggers: valueObject.map((item: Trigger) => ({ + time: item.time, + key: item.key, + })), + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [props.value]) + return ( + <> + + Time Clock Triggers + + {form.isDirty() ? ( + + ) : null} + + Time Clock Triggers will recall this preset at the following times (if enabled) + {form.values.triggers.map((item, index) => ( + + + value.match(/\d(?=(?:\D*\d){0,3}$)/g) ? value.match(/\d(?=(?:\D*\d){0,3}$)/g).join('') : '' + } + formatter={value => + !Number.isNaN(parseFloat(value)) + ? value.replace(/\b\d{1,4}\b/g, match => { + const paddedNumber = match.padStart(4, '0') + const hours = paddedNumber.slice(0, 2) + const minutes = paddedNumber.slice(2) + return `${hours}:${minutes}` + }) + : '' + } + {...form.getInputProps(`triggers.${index}.time`)} + /> + + form.removeListItem('triggers', index)} + > + + + + ))} + + + + ) +} diff --git a/src/app/Components/ControlPanel/ButtonIcon.tsx b/src/app/Components/ControlPanel/ButtonIcon.tsx index 3b9c37cb..b0a30a81 100644 --- a/src/app/Components/ControlPanel/ButtonIcon.tsx +++ b/src/app/Components/ControlPanel/ButtonIcon.tsx @@ -1,5 +1,4 @@ import { Avatar, Group, Text } from '@mantine/core' -import { IconType } from '@react-icons/all-files/lib' import React, { forwardRef } from 'react' import { FaArrowDown } from '@react-icons/all-files/fa/FaArrowDown' @@ -21,68 +20,112 @@ import { FaVolumeMute } from '@react-icons/all-files/fa/FaVolumeMute' import { FaVolumeOff } from '@react-icons/all-files/fa/FaVolumeOff' import { FaVolumeUp } from '@react-icons/all-files/fa/FaVolumeUp' -export const AvailableIcons = { - FaLightbulb: 'Lightbulb', - FaRegLightbulb: 'Lightbulb', - FaVolumeUp: 'Volume Up', - FaVolumeDown: 'Volume Down', - FaVolumeMute: 'Volume Mute', - FaVolumeOff: 'Volume Off', - FaPhoneVolume: 'Phone', - FaMicrophone: 'Microphone', - FaMicrophoneAlt: 'Microphone', - FaVideo: 'Camera', - FaFolder: 'Folder', - FaArrowUp: 'Arrow Up', - FaArrowDown: 'Arrow Down', - FaArrowLeft: 'Arrow Left', - FaArrowRight: 'Arrow Right', - FaPowerOff: 'Power Off', - FaToggleOn: 'Toggle On', - FaToggleOff: 'Toggle Off', -} +const iconDatabase = [ + { + id: 'FaLightbulb', + name: 'Lightbulb', + icon: , + }, + { + id: 'FaRegLightbulb', + name: 'Lightbulb', + icon: , + }, + { + id: 'FaVolumeUp', + name: 'Volume Up', + icon: , + }, + { + id: 'FaVolumeDown', + name: 'Volume Down', + icon: , + }, + { + id: 'FaVolumeMute', + name: 'Volume Mute', + icon: , + }, + { + id: 'FaVolumeOff', + name: 'Volume Off', + icon: , + }, + { + id: 'FaPhoneVolume', + name: 'Phone', + icon: , + }, + { + id: 'FaMicrophone', + name: 'Microphone', + icon: , + }, + { + id: 'FaMicrophoneAlt', + name: 'Microphone', + icon: , + }, + { + id: 'FaVideo', + name: 'Camera', + icon: , + }, + { + id: 'FaFolder', + name: 'Folder', + icon: , + }, + { + id: 'FaArrowUp', + name: 'Arrow Up', + icon: , + }, + { + id: 'FaArrowDown', + name: 'Arrow Down', + icon: , + }, + { + id: 'FaArrowLeft', + name: 'Arrow Left', + icon: , + }, + { + id: 'FaArrowRight', + name: 'Arrow Right', + icon: , + }, + { + id: 'FaPowerOff', + name: 'Power Off', + icon: , + }, + { + id: 'FaToggleOn', + name: 'Toggle On', + icon: , + }, + { + id: 'FaToggleOff', + name: 'Toggle Off', + icon: , + }, +] -export const ButtonIcon = (icon: string): IconType => { - if (icon === 'FaLightbulb') return FaLightbulb - else if (icon === 'FaRegLightbulb') return FaRegLightbulb - else if (icon === 'FaVolumeDown') return FaVolumeDown - else if (icon === 'FaVolumeMute') return FaVolumeMute - else if (icon === 'FaVolumeOff') return FaVolumeOff - else if (icon === 'FaVolumeUp') return FaVolumeUp - else if (icon === 'FaPhoneVolume') return FaPhoneVolume - else if (icon === 'FaMicrophone') return FaMicrophone - else if (icon === 'FaMicrophoneAlt') return FaMicrophoneAlt - else if (icon === 'FaVideo') return FaVideo - else if (icon === 'FaFolder') return FaFolder - else if (icon === 'FaArrowUp') return FaArrowUp - else if (icon === 'FaArrowDown') return FaArrowDown - else if (icon === 'FaArrowLeft') return FaArrowLeft - else if (icon === 'FaArrowRight') return FaArrowRight - else if (icon === 'FaPowerOff') return FaPowerOff - else if (icon === 'FaToggleOn') return FaToggleOn - else if (icon === 'FaToggleOff') return FaToggleOff - else return null +export const availableIcons = () => { + const icons: { + [key: string]: string + } = {} + iconDatabase.forEach(icon => { + icons[icon.id] = icon.name + }) + return icons } -// TODO surely we don't need two functions? -export const ButtonIconReact = (props: { icon: string }) => { - if (props.icon === 'FaLightbulb') return - else if (props.icon === 'FaRegLightbulb') return - else if (props.icon === 'FaVolumeDown') return - else if (props.icon === 'FaVolumeMute') return - else if (props.icon === 'FaVolumeOff') return - else if (props.icon === 'FaVolumeUp') return - else if (props.icon === 'FaPhoneVolume') return - else if (props.icon === 'FaMicrophone') return - else if (props.icon === 'FaMicrophoneAlt') return - else if (props.icon === 'FaVideo') return - else if (props.icon === 'FaFolder') return - else if (props.icon === 'FaArrowUp') return - else if (props.icon === 'FaArrowDown') return - else if (props.icon === 'FaArrowLeft') return - else if (props.icon === 'FaArrowRight') return - else if (props.icon === 'FaPowerOff') return - else if (props.icon === 'FaToggleOn') return - else if (props.icon === 'FaToggleOff') return + +export const ButtonIcon = (props: { icon: string }) => { + const icon = iconDatabase.find(icon => icon.id === props.icon) + if (icon) return icon.icon else return <> } @@ -98,7 +141,7 @@ export const ButtonIconSelectItem = forwardRef( {icon ? ( - + ) : ( '' diff --git a/src/app/Components/Locked.tsx b/src/app/Components/Locked.tsx index 9f2371a1..97fc8a17 100644 --- a/src/app/Components/Locked.tsx +++ b/src/app/Components/Locked.tsx @@ -1,7 +1,7 @@ -import { Stack, Title, Collapse, Text } from '@mantine/core' -import React, { useCallback, useEffect, useState } from 'react' +import { Collapse, Stack, Text, Title } from '@mantine/core' import { useEventListener, useViewportSize } from '@mantine/hooks' import { FaLock } from '@react-icons/all-files/fa/FaLock' +import React, { useCallback, useEffect, useState } from 'react' import { useAppSelector } from './../apis/redux/mainStore' const LockedMessage = () => { @@ -12,7 +12,7 @@ const LockedMessage = () => { const port = useAppSelector(state => (state.database ? state.database.about.port : false)) if (count >= 5) { return ( - + Device is locked. To unlock it access the setup and administration area, by ensuring you are on the same network as this device and then navigating to http:// {ipAddress + ':' + port ?? ''} with a web browser @@ -23,6 +23,7 @@ const LockedMessage = () => { Device is locked, unlock it using the setup and administration area on another device diff --git a/src/app/Navigation/AdminNavigation.tsx b/src/app/Navigation/AdminNavigation.tsx index f70e4aa7..5e9acc7c 100644 --- a/src/app/Navigation/AdminNavigation.tsx +++ b/src/app/Navigation/AdminNavigation.tsx @@ -1,17 +1,18 @@ -import React from 'react' -import { Navbar, Group, Code, Text, ScrollArea } from '@mantine/core' +import { Code, Group, Navbar, ScrollArea, Text } from '@mantine/core' import { useViewportSize } from '@mantine/hooks' -import { Link } from 'react-router-dom' import { FaCogs } from '@react-icons/all-files/fa/FaCogs' +import { FaDatabase } from '@react-icons/all-files/fa/FaDatabase' import { FaLevelUpAlt } from '@react-icons/all-files/fa/FaLevelUpAlt' +import { FaRegClock } from '@react-icons/all-files/fa/FaRegClock' import { FaRegFolder } from '@react-icons/all-files/fa/FaRegFolder' -import { FaDatabase } from '@react-icons/all-files/fa/FaDatabase' import { FaRegPlayCircle } from '@react-icons/all-files/fa/FaRegPlayCircle' import { FaWindowClose } from '@react-icons/all-files/fa/FaWindowClose' -import { useStyles } from './Styles' -import { NavbarItem } from './NavbarItem' -import { useAppSelector } from './../apis/redux/mainStore' +import React from 'react' +import { Link } from 'react-router-dom' import { runningInElectron } from '../apis/utilities/version' +import { useAppSelector } from './../apis/redux/mainStore' +import { NavbarItem } from './NavbarItem' +import { useStyles } from './Styles' export const AdminNavigation = () => { const { classes, cx } = useStyles() @@ -32,13 +33,22 @@ export const AdminNavigation = () => { Exit ) : null} - - - - + } + /> + } /> + } /> + } /> + } + /> - + } /> ) diff --git a/src/app/Navigation/ControlPanelNavigation.tsx b/src/app/Navigation/ControlPanelNavigation.tsx index b0fab8bd..35dd9bae 100644 --- a/src/app/Navigation/ControlPanelNavigation.tsx +++ b/src/app/Navigation/ControlPanelNavigation.tsx @@ -1,15 +1,16 @@ -import React from 'react' import { Navbar, ScrollArea } from '@mantine/core' import { useViewportSize } from '@mantine/hooks' import { FaQuestion } from '@react-icons/all-files/fa/FaQuestion' -import { useStyles } from './Styles' -import { NavbarItem } from './NavbarItem' -import { useAppSelector } from './../apis/redux/mainStore' -import { DatabaseFolder } from './../../database/repository/folder' +import React from 'react' import { ButtonIcon } from '../Components/ControlPanel/ButtonIcon' +import { DatabaseFolder } from './../../database/repository/folder' +import { useAppSelector } from './../apis/redux/mainStore' +import { NavbarItem } from './NavbarItem' +import { useStyles } from './Styles' const TopLevelFolders = () => { const folders = useAppSelector(state => (state.database ? state.database.folders : false)) + const { classes } = useStyles() const topLevelFolders: Array = [] if (folders !== false) { Object.entries(folders) @@ -31,7 +32,11 @@ const TopLevelFolders = () => { key={item.id} link={'folder/' + item.id.toString()} label={item.name} - Icon={ButtonIcon(item.icon)} + Icon={ + + + + } /> ))} @@ -46,7 +51,7 @@ export const ControlPanelNavigation = () => { - + } /> ) diff --git a/src/app/Navigation/NavbarItem.tsx b/src/app/Navigation/NavbarItem.tsx index 1d11c4fc..ce7bf17b 100644 --- a/src/app/Navigation/NavbarItem.tsx +++ b/src/app/Navigation/NavbarItem.tsx @@ -1,9 +1,8 @@ import React from 'react' import { Link, useLocation } from 'react-router-dom' -import { IconType } from '@react-icons/all-files/lib' import { useStyles } from './Styles' -export const NavbarItem = ({ link, label, Icon }: { link: string; label: string; Icon: IconType }) => { +export const NavbarItem = ({ link, label, Icon }: { link: string; label: string; Icon: React.ReactNode }) => { const location = useLocation() const pathLink = location.pathname.replace('/admin/', '').replace('/controlPanel/', '') const { classes, cx } = useStyles() @@ -15,7 +14,7 @@ export const NavbarItem = ({ link, label, Icon }: { link: string; label: string; to={link} key={label} > - + {Icon} {label} ) diff --git a/src/app/Navigation/Styles.tsx b/src/app/Navigation/Styles.tsx index a3aeb1ce..ff979a82 100644 --- a/src/app/Navigation/Styles.tsx +++ b/src/app/Navigation/Styles.tsx @@ -1,7 +1,7 @@ import { createStyles } from '@mantine/core' -export const useStyles = createStyles((theme, _params, getRef) => { - const icon = getRef('icon') +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export const useStyles = createStyles((theme, _params) => { return { navbar: { backgroundColor: theme.colors.dark[6], @@ -48,7 +48,6 @@ export const useStyles = createStyles((theme, _params, getRef) => { }, linkIcon: { - ref: icon, color: theme.white, opacity: 0.75, marginRight: theme.spacing.sm, @@ -58,9 +57,6 @@ export const useStyles = createStyles((theme, _params, getRef) => { linkActive: { '&, &:hover': { backgroundColor: theme.colors.dark[7], - [`& .${icon}`]: { - opacity: 0.9, - }, }, }, } diff --git a/src/app/Pages/Admin/Folders.tsx b/src/app/Pages/Admin/Folders.tsx index 923dd675..ef64b832 100644 --- a/src/app/Pages/Admin/Folders.tsx +++ b/src/app/Pages/Admin/Folders.tsx @@ -1,34 +1,34 @@ -import React, { useEffect, useState } from 'react' import { - Group, - TextInput, + ActionIcon, Box, Button, Center, - ActionIcon, - Select, + Group, LoadingOverlay, SelectItem as MantineSelectItem, Modal, - Text, + Select, Table, + Text, + TextInput, Title, } from '@mantine/core' import { useForm } from '@mantine/form' -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' +import { showNotification } from '@mantine/notifications' +import { RichTextEditor } from '@mantine/rte' +import { FaCheck } from '@react-icons/all-files/fa/FaCheck' import { FaFolder } from '@react-icons/all-files/fa/FaFolder' import { FaGripVertical } from '@react-icons/all-files/fa/FaGripVertical' +import { FaPencilAlt } from '@react-icons/all-files/fa/FaPencilAlt' +import { FaPlus } from '@react-icons/all-files/fa/FaPlus' +import { FaSave } from '@react-icons/all-files/fa/FaSave' import { FaTrash } from '@react-icons/all-files/fa/FaTrash' -import { useAppSelector } from './../../apis/redux/mainStore' +import React, { useEffect, useState } from 'react' +import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' +import { ButtonIconSelectItem, availableIcons } from '../../Components/ControlPanel/ButtonIcon' import { DatabaseFolder } from './../../../database/repository/folder' +import { useAppSelector } from './../../apis/redux/mainStore' import { ApiCall } from './../../apis/wrapper' -import { AvailableIcons, ButtonIconSelectItem } from '../../Components/ControlPanel/ButtonIcon' -import { FaPencilAlt } from '@react-icons/all-files/fa/FaPencilAlt' -import { RichTextEditor } from '@mantine/rte' -import { FaSave } from '@react-icons/all-files/fa/FaSave' -import { FaPlus } from '@react-icons/all-files/fa/FaPlus' -import { showNotification } from '@mantine/notifications' -import { FaCheck } from '@react-icons/all-files/fa/FaCheck' interface FormValues { folders: Array @@ -137,7 +137,7 @@ export const FoldersConfigurationPage = () => { searchable={true} nothingFound="No icons found" itemComponent={ButtonIconSelectItem} - data={Object.entries(AvailableIcons).map(([value, name]) => ({ + data={Object.entries(availableIcons()).map(([value, name]) => ({ value: value, icon: value, label: name, diff --git a/src/app/Pages/Admin/ModuleConfiguration/E131.tsx b/src/app/Pages/Admin/ModuleConfiguration/E131.tsx index 6affe7a5..fdf16819 100644 --- a/src/app/Pages/Admin/ModuleConfiguration/E131.tsx +++ b/src/app/Pages/Admin/ModuleConfiguration/E131.tsx @@ -1,16 +1,16 @@ -import { Box, Button, Divider, Loader, LoadingOverlay, TextInput, Checkbox, NumberInput, Text } from '@mantine/core' +import { Box, Button, Checkbox, Divider, Loader, LoadingOverlay, NumberInput, Text, TextInput } from '@mantine/core' import { useForm } from '@mantine/form' -import React, { useEffect, useState } from 'react' -import { ApiCall } from '../../../apis/wrapper' -import { useAppSelector } from '../../../apis/redux/mainStore' -import { FaIdBadge } from '@react-icons/all-files/fa/FaIdBadge' -import { FaPlay } from '@react-icons/all-files/fa/FaPlay' +import { useModals } from '@mantine/modals' import { FaClipboardList } from '@react-icons/all-files/fa/FaClipboardList' import { FaCrown } from '@react-icons/all-files/fa/FaCrown' -import { FaWaveSquare } from '@react-icons/all-files/fa/FaWaveSquare' +import { FaIdBadge } from '@react-icons/all-files/fa/FaIdBadge' +import { FaPlay } from '@react-icons/all-files/fa/FaPlay' import { FaRegClock } from '@react-icons/all-files/fa/FaRegClock' -import { useModals } from '@mantine/modals' import { FaSave } from '@react-icons/all-files/fa/FaSave' +import { FaWaveSquare } from '@react-icons/all-files/fa/FaWaveSquare' +import React, { useEffect, useState } from 'react' +import { useAppSelector } from '../../../apis/redux/mainStore' +import { ApiCall } from '../../../apis/wrapper' export const E131ModuleConfigurationPage = () => { const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false) @@ -26,7 +26,6 @@ export const E131ModuleConfigurationPage = () => { e131Frequency: 0, e131FadeTime: 0, e131Sampler_time: 0, - e131Sampler_effectMode: 0, }, validate: { e131SourceName: value => (value.length > 5 ? null : 'Must be longer than 5 characters'), @@ -43,7 +42,6 @@ export const E131ModuleConfigurationPage = () => { e131Frequency: e131Config.e131Frequency, e131FadeTime: e131Config.e131FadeTime, e131Sampler_time: e131Config.e131Sampler_time, - e131Sampler_effectMode: e131Config.e131Sampler_effectMode, }) } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/app/Pages/Admin/Presets.tsx b/src/app/Pages/Admin/Presets.tsx index 30a5fd2b..d1a1fb0d 100644 --- a/src/app/Pages/Admin/Presets.tsx +++ b/src/app/Pages/Admin/Presets.tsx @@ -1,49 +1,49 @@ -import React, { useEffect, useState } from 'react' import { - Group, - TextInput, + ActionIcon, + Alert, + Badge, Box, Button, Center, - ActionIcon, - Select, - LoadingOverlay, Checkbox, + Chip, ColorInput, + Group, + LoadingOverlay, Modal, - SelectItem, NumberInput, - Chip, + Select, + SelectItem, + Table, Text, + TextInput, Title, - Table, - Alert, - Badge, } from '@mantine/core' import { useForm } from '@mantine/form' -import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd' +import { useModals } from '@mantine/modals' +import { showNotification } from '@mantine/notifications' +import { FaCheck } from '@react-icons/all-files/fa/FaCheck' import { FaFolder } from '@react-icons/all-files/fa/FaFolder' import { FaGripVertical } from '@react-icons/all-files/fa/FaGripVertical' -import { FaRegClock } from '@react-icons/all-files/fa/FaRegClock' -import { FaTrash } from '@react-icons/all-files/fa/FaTrash' -import { FaRegClone } from '@react-icons/all-files/fa/FaRegClone' import { FaPencilAlt } from '@react-icons/all-files/fa/FaPencilAlt' -import { FaSpaceShuttle } from '@react-icons/all-files/fa/FaSpaceShuttle' -import { FaSave } from '@react-icons/all-files/fa/FaSave' import { FaPlus } from '@react-icons/all-files/fa/FaPlus' import { FaRecycle } from '@react-icons/all-files/fa/FaRecycle' -import { useAppSelector } from './../../apis/redux/mainStore' +import { FaRegClock } from '@react-icons/all-files/fa/FaRegClock' +import { FaRegClone } from '@react-icons/all-files/fa/FaRegClone' +import { FaSave } from '@react-icons/all-files/fa/FaSave' +import { FaSpaceShuttle } from '@react-icons/all-files/fa/FaSpaceShuttle' +import { FaTrash } from '@react-icons/all-files/fa/FaTrash' +import React, { useEffect, useState } from 'react' +import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd' import { DatabasePreset, PresetTypes } from './../../../database/repository/preset' -import { ApiCall } from './../../apis/wrapper' -import { OSCPresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/OSC' +import { E131PresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/E131' import { HTTPPresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/HTTP' import { MacroPresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/Macro' -import { E131PresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/E131' -import { useModals } from '@mantine/modals' -import { FaCheck } from '@react-icons/all-files/fa/FaCheck' -import { showNotification } from '@mantine/notifications' +import { OSCPresetEditModal } from './../../Components/Admin/Controls/Presets/EditModal/OSC' import { isValidJson } from './../../Components/Admin/Controls/Presets/EditModal/isValidJson' -import { AvailableIcons, ButtonIconSelectItem } from './../../Components/ControlPanel/ButtonIcon' +import { ButtonIconSelectItem, availableIcons } from './../../Components/ControlPanel/ButtonIcon' +import { useAppSelector } from './../../apis/redux/mainStore' +import { ApiCall } from './../../apis/wrapper' interface FormValues { presets: Array @@ -170,7 +170,7 @@ export const PresetsConfigurationPage = () => { clearable={true} data={[ { value: null, icon: null, label: '' }, - ...Object.entries(AvailableIcons).map(([value, name]) => ({ + ...Object.entries(availableIcons()).map(([value, name]) => ({ value: value, icon: value, label: name, @@ -255,6 +255,7 @@ export const PresetsConfigurationPage = () => { universe: form.values.presets[index].universe, fadeTime: form.values.presets[index].fadeTime, data: form.values.presets[index].data, + timeClockTriggers: null, //Deliberate decision not to copy these httpTriggerEnabled: form.values.presets[index].httpTriggerEnabled, folderId: form.values.presets[index].folderId, color: form.values.presets[index].color, @@ -350,6 +351,7 @@ export const PresetsConfigurationPage = () => { universe: 1, fadeTime: 0, data: null, + timeClockTriggers: null, httpTriggerEnabled: false, folderId: '0', icon: null, diff --git a/src/app/Pages/Admin/TimeClockTriggers.tsx b/src/app/Pages/Admin/TimeClockTriggers.tsx new file mode 100644 index 00000000..93a9624a --- /dev/null +++ b/src/app/Pages/Admin/TimeClockTriggers.tsx @@ -0,0 +1,261 @@ +import { + ActionIcon, + Box, + Button, + Checkbox, + Group, + LoadingOverlay, + Modal, + NumberInput, + Select, + SelectItem, + Table, + Text, + TextInput, + Textarea, + Title, +} from '@mantine/core' +import { useForm } from '@mantine/form' +import { showNotification } from '@mantine/notifications' +import { FaCheck } from '@react-icons/all-files/fa/FaCheck' +import { FaCog } from '@react-icons/all-files/fa/FaCog' +import { FaPlus } from '@react-icons/all-files/fa/FaPlus' +import { FaRegClone } from '@react-icons/all-files/fa/FaRegClone' +import { FaSave } from '@react-icons/all-files/fa/FaSave' +import { FaTrash } from '@react-icons/all-files/fa/FaTrash' +import React, { useEffect, useState } from 'react' +import { DatabaseTimeClockTrigger } from '../../../database/repository/timeClockTrigger' +import { useAppSelector } from '../../apis/redux/mainStore' +import { ApiCall } from '../../apis/wrapper' + +interface FormValues { + triggers: Array +} + +export const TimeClockTriggersConfigurationPage = () => { + const [modalVisible, setModalVisible] = useState(false) + const [loadingOverlayVisible, setLoadingOverlayVisible] = useState(false) + const [formOriginalValues, setFormOriginalValues] = useState('') // Values used to detect unsaved changes + const presets = useAppSelector(state => (state.database ? state.database.presets : false)) + const folders = useAppSelector(state => (state.database ? state.database.folders : false)) + const timeClockTriggers = useAppSelector(state => (state.database ? state.database.timeClockTriggers : false)) + const presetsForSelect: Array = [] + // Prepare folders list for select dropdown + if (presets !== false && folders !== false) { + Object.entries(presets).forEach(([, value]) => { + if (value.folderId !== null) { + presetsForSelect.push({ + value: value.id.toString(), + label: value.name, + group: folders[value.folderId as unknown as number].name, + }) + } + }) + } + // Setup the form + const form = useForm({ + initialValues: { + triggers: Array(), + }, + validate: { + triggers: { + timeout: (value: number) => (value < 0 ? 'Timeout should be a number' : null), + time: (value: string) => + value.match(/^([0-1]?[0-9]|2[0-3]):[0-5][0-9]$/) ? null : 'Time should be in hh:mm format', + presetId: value => + typeof value === 'undefined' || value == null || parseInt(value) == 0 + ? 'Preset must be selected' + : null, + }, + }, + }) + useEffect(() => { + if (timeClockTriggers !== false) { + const formValues = { triggers: timeClockTriggers.map(item => ({ ...item })) } + form.setValues(formValues) // Make a copy of the presets using map because the object is not extensible + setFormOriginalValues(JSON.stringify(formValues)) + setLoadingOverlayVisible(false) + } else if (!loadingOverlayVisible) setLoadingOverlayVisible(true) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [presets]) + const saveByUserNeeded = formOriginalValues !== JSON.stringify(form.values) // Does the user have unsaved changes + // Handle the submit button + const handleSubmit = (values: typeof form.values) => { + setLoadingOverlayVisible(true) + ApiCall.put('/timeClockTriggers', values.triggers).then(() => { + showNotification({ + message: 'Your changes have been saved', + autoClose: 2000, + disallowClose: true, + color: 'green', + icon: , + }) + }) + } + const fields = form.values.triggers.map((_, index) => ( + + + {form.values.triggers[index].notes ? {form.values.triggers[index].notes} : null} + + {['Mon', 'Tues', 'Weds', 'Thurs', 'Fri', 'Sat', 'Sun'].map((day, i) => ( + + ))} + + + + + + +