diff --git a/app/admin/system/settings/page.tsx b/app/admin/system/settings/page.tsx index cbb7d7af..2bf7f708 100644 --- a/app/admin/system/settings/page.tsx +++ b/app/admin/system/settings/page.tsx @@ -35,6 +35,15 @@ export default async function IntegrationsPage() { description: 'Every how many seconds should displays "phone home" for updated information?', }, + { + setting: 'display-dev-environment-link', + type: 'string', + defaultValue: '', + + label: 'Dev environment link', + description: + 'When set, a menu item will be included linking through to this URL.', + }, { setting: 'display-max-time-since-check-in-days', type: 'number', diff --git a/app/api/admin/updateSettings.ts b/app/api/admin/updateSettings.ts index dd29dee6..19c3f44f 100644 --- a/app/api/admin/updateSettings.ts +++ b/app/api/admin/updateSettings.ts @@ -27,7 +27,7 @@ export const kUpdateSettingsDefinition = z.object({ /** * Value that the setting should be set to. */ - value: z.number().or(z.string().min(1)), + value: z.number().or(z.string()), })).min(1), }), diff --git a/app/api/display/route.ts b/app/api/display/route.ts index 9596fc76..ce74d0cf 100644 --- a/app/api/display/route.ts +++ b/app/api/display/route.ts @@ -6,7 +6,7 @@ import { z } from 'zod'; import type { ApiDefinition, ApiRequest, ApiResponse } from '../Types'; import { executeAction, noAccess, type ActionProps } from '../Action'; -import { readSetting } from '@lib/Settings'; +import { readSettings } from '@lib/Settings'; import { getDisplayIdFromHeaders, writeDisplayIdToHeaders } from '@lib/auth/DisplaySession'; import db, { tActivitiesLocations, tDisplays, tEvents } from '@lib/database'; @@ -26,6 +26,21 @@ const kDisplayDefinition = z.object({ */ label: z.string(), + /** + * Optional link to the development environment. + */ + devEnvironment: z.string().optional(), + + /** + * Whether the device should be locked. + */ + locked: z.boolean(), + + /** + * Whether the device has been fully provisioned and is ready for use. + */ + provisioned: z.boolean(), + /** * Timezone in which the display operates. Will affect the local time. */ @@ -66,7 +81,12 @@ async function display(request: Request, props: ActionProps): Promise if (!props.ip) noAccess(); - const updateFrequencySeconds = await readSetting('display-check-in-rate-seconds') ?? 300; + const settings = await readSettings([ + 'display-check-in-rate-seconds', + 'display-dev-environment-link', + ]); + + const updateFrequencySeconds = settings['display-check-in-rate-seconds'] ?? 300; const updateFrequencyMs = Math.max(10, updateFrequencySeconds) * 1000; // --------------------------------------------------------------------------------------------- @@ -119,26 +139,23 @@ async function display(request: Request, props: ActionProps): Promise .select({ identifier: tDisplays.displayIdentifier, label: tDisplays.displayLabel, + locked: tDisplays.displayLocked.equals(/* true= */ 1), + timezone: eventsJoin.eventTimezone, // Event information: eventId: eventsJoin.eventId, - timezone: eventsJoin.eventTimezone, // Location information: locationId: activitiesLocationsJoin.locationId, }) .executeSelectOne(); - // --------------------------------------------------------------------------------------------- - // Step 3: Read dynamic information about the volunteers at the location - // --------------------------------------------------------------------------------------------- - - // TODO: Include the list of volunteers - // TODO: Include the device locked status - return { identifier: configuration.identifier, label: configuration.label ?? 'AnimeCon Volunteering Teams', + devEnvironment: settings['display-dev-environment-link'], + locked: configuration.locked, + provisioned: !!configuration.eventId && !!configuration.locationId, timezone: configuration.timezone ?? 'Europe/Amsterdam', updateFrequencyMs, }; diff --git a/app/display/DisplayController.tsx b/app/display/DisplayController.tsx index 0dbb640a..7ef05000 100644 --- a/app/display/DisplayController.tsx +++ b/app/display/DisplayController.tsx @@ -65,7 +65,7 @@ export function DisplayController(props: React.PropsWithChildren) { return () => clearInterval(timer); - }, [ updateFrequencyMs ]) + }, [ updateFrequencyMs ]); // Effect that actually updates the context. This is done by making a network call to the // Display API, which identifies the displays based on an identifier assigned by the server, diff --git a/app/display/DisplayHeader.tsx b/app/display/DisplayHeader.tsx index d93d90c2..e0809818 100644 --- a/app/display/DisplayHeader.tsx +++ b/app/display/DisplayHeader.tsx @@ -4,10 +4,12 @@ 'use client'; import { useCallback, useContext, useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; import type { SvgIconProps } from '@mui/material/SvgIcon'; import Box from '@mui/material/Box'; import CircularProgress from '@mui/material/CircularProgress'; +import DeveloperBoardIcon from '@mui/icons-material/DeveloperBoard'; import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; import Paper from '@mui/material/Paper'; @@ -44,6 +46,34 @@ function CurrentTime(props: { timezone?: string }) { */ export function DisplayHeader() { const context = useContext(DisplayContext); + const router = useRouter(); + + const [ showDevEnvironment, setShowDevEnvironment ] = useState(false); + + const handleDevEnvironment = useCallback(() => { + if (!!context?.devEnvironment) + router.push(context.devEnvironment); + + }, [ context?.devEnvironment, router ]); + + useEffect(() => { + if (!!context && !!context.devEnvironment) { + try { + const currentUrl = new URL(window.location.href); + const devUrl = new URL(context.devEnvironment); + + if (currentUrl.host !== devUrl.host) { + setShowDevEnvironment(true); + return; + } + } catch (error: any) { + console.error(`Invalid devEnvironment setting: ${context.devEnvironment}`); + } + } + + setShowDevEnvironment(false); + + }, [ context ]); const [ refreshActive, setRefreshActive ] = useState(false); const [ refreshResult, setRefreshResult ] = useState('inherit'); @@ -75,6 +105,10 @@ export function DisplayHeader() { } + { !!showDevEnvironment && + + + } diff --git a/app/lib/Settings.ts b/app/lib/Settings.ts index d74b45b1..ca67999b 100644 --- a/app/lib/Settings.ts +++ b/app/lib/Settings.ts @@ -14,6 +14,7 @@ type SettingsMap = { // --------------------------------------------------------------------------------------------- 'display-check-in-rate-seconds': number; + 'display-dev-environment-link': string; 'display-max-time-since-check-in-days': number; // ---------------------------------------------------------------------------------------------