From 433f2259687b6176f1a5b30e2e36bbdfb714e2ee Mon Sep 17 00:00:00 2001 From: Tarik Alani Date: Thu, 24 Apr 2025 11:13:36 +1200 Subject: [PATCH 01/13] add settings ui --- src/frontend/components/Settings/Settings.tsx | 10 ++- .../Settings/SettingsLayout.stories.tsx | 24 ++++++ .../components/Settings/SettingsLayout.tsx | 86 +++++++++++++++++++ .../Settings/__mocks__/mockBridge.ts | 15 ++++ .../components/BaseSettingsSection.tsx | 44 ++++++++++ .../Settings/components/ToggleSwitch.tsx | 28 ++++++ .../Settings/sections/AdvancedSettings.tsx | 59 +++++++++++++ .../Settings/sections/RelativeSettings.tsx | 24 ++++++ .../Settings/sections/StandingsSettings.tsx | 24 ++++++ .../Settings/sections/TrackMapSettings.tsx | 27 ++++++ .../Settings/sections/WeatherSettings.tsx | 24 ++++++ .../components/Settings/sections/index.ts | 5 ++ src/frontend/components/Settings/types.ts | 23 +++++ .../Standings/DynamicTelemetrySelector.tsx | 3 + .../components/Standings/Relative.stories.tsx | 2 +- .../components/Standings/Relative.tsx | 2 +- .../Standings/Standings.stories.tsx | 2 +- 17 files changed, 395 insertions(+), 7 deletions(-) create mode 100644 src/frontend/components/Settings/SettingsLayout.stories.tsx create mode 100644 src/frontend/components/Settings/SettingsLayout.tsx create mode 100644 src/frontend/components/Settings/__mocks__/mockBridge.ts create mode 100644 src/frontend/components/Settings/components/BaseSettingsSection.tsx create mode 100644 src/frontend/components/Settings/components/ToggleSwitch.tsx create mode 100644 src/frontend/components/Settings/sections/AdvancedSettings.tsx create mode 100644 src/frontend/components/Settings/sections/RelativeSettings.tsx create mode 100644 src/frontend/components/Settings/sections/StandingsSettings.tsx create mode 100644 src/frontend/components/Settings/sections/TrackMapSettings.tsx create mode 100644 src/frontend/components/Settings/sections/WeatherSettings.tsx create mode 100644 src/frontend/components/Settings/sections/index.ts create mode 100644 src/frontend/components/Settings/types.ts diff --git a/src/frontend/components/Settings/Settings.tsx b/src/frontend/components/Settings/Settings.tsx index 9c0723c..188399c 100644 --- a/src/frontend/components/Settings/Settings.tsx +++ b/src/frontend/components/Settings/Settings.tsx @@ -1,5 +1,6 @@ import { useDashboard } from '@irdashies/context'; import { SettingsForm } from './SettingsForm'; +import { SettingsLayout } from './SettingsLayout'; export const Settings = () => { const { currentDashboard, onDashboardUpdated } = useDashboard(); @@ -8,9 +9,10 @@ export const Settings = () => { } return ( - + + // ); }; diff --git a/src/frontend/components/Settings/SettingsLayout.stories.tsx b/src/frontend/components/Settings/SettingsLayout.stories.tsx new file mode 100644 index 0000000..5098775 --- /dev/null +++ b/src/frontend/components/Settings/SettingsLayout.stories.tsx @@ -0,0 +1,24 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { HashRouter } from 'react-router-dom'; +import { DashboardProvider } from '@irdashies/context'; +import { SettingsLayout } from './SettingsLayout'; +import { mockDashboardBridge } from './__mocks__/mockBridge'; + +const meta: Meta = { + component: SettingsLayout, + decorators: [ + (Story) => ( + + +
+ +
+
+
+ ), + ], +}; + +export default meta; + +export const Default: StoryObj = {}; diff --git a/src/frontend/components/Settings/SettingsLayout.tsx b/src/frontend/components/Settings/SettingsLayout.tsx new file mode 100644 index 0000000..4c40c9b --- /dev/null +++ b/src/frontend/components/Settings/SettingsLayout.tsx @@ -0,0 +1,86 @@ +import { Gear } from '@phosphor-icons/react'; +import { Link, Route, Routes, useLocation } from 'react-router-dom'; +import { StandingsSettings } from './sections/StandingsSettings'; +import { RelativeSettings } from './sections/RelativeSettings'; +import { WeatherSettings } from './sections/WeatherSettings'; +import { TrackMapSettings } from './sections/TrackMapSettings'; +import { AdvancedSettings } from './sections/AdvancedSettings'; + +export const SettingsLayout = () => { + const location = useLocation(); + + const isActive = (path: string) => { + return location.pathname === `/settings${path}`; + }; + + const menuItemClass = (path: string) => + `block w-full p-2 rounded cursor-pointer ${ + isActive(path) ? 'bg-slate-700' : 'hover:bg-slate-700' + }`; + + return ( +
+
+ +

Overlay Setup

+
+
+ {/* Left Column - Widget Menu */} +
+
    +
  • + + Standings + +
  • +
  • + + Relative + +
  • +
  • + + Weather + +
  • +
  • + +
    + Track Map + + Experimental + +
    + +
  • +
+ {/* Advanced settings pushed to bottom */} +
+ + Advanced + +
+
+ + {/* Right Column - Widget Settings */} +
+ + } /> + } /> + } /> + } /> + } /> + + Select a widget from the left to customize its settings +
+ } + /> + +
+
+ + ); +}; diff --git a/src/frontend/components/Settings/__mocks__/mockBridge.ts b/src/frontend/components/Settings/__mocks__/mockBridge.ts new file mode 100644 index 0000000..a739c37 --- /dev/null +++ b/src/frontend/components/Settings/__mocks__/mockBridge.ts @@ -0,0 +1,15 @@ +import type { DashboardBridge } from '@irdashies/types'; +import { defaultDashboard } from '../../../../app/storage/defaultDashboard'; + +export const mockDashboardBridge: DashboardBridge = { + reloadDashboard: () => {}, + saveDashboard: () => {}, + dashboardUpdated: (callback) => { + callback(defaultDashboard); + return () => {}; + }, + onEditModeToggled: (callback) => { + callback(false); + return () => {}; + }, +}; \ No newline at end of file diff --git a/src/frontend/components/Settings/components/BaseSettingsSection.tsx b/src/frontend/components/Settings/components/BaseSettingsSection.tsx new file mode 100644 index 0000000..3056a78 --- /dev/null +++ b/src/frontend/components/Settings/components/BaseSettingsSection.tsx @@ -0,0 +1,44 @@ +import { ReactNode } from 'react'; +import { ToggleSwitch } from './ToggleSwitch'; +import { BaseWidgetSettings } from '../types'; + +interface BaseSettingsSectionProps { + title: string; + description: string; + settings: BaseWidgetSettings; + onSettingsChange: (settings: BaseWidgetSettings) => void; + children?: ReactNode; +} + +export const BaseSettingsSection = ({ + title, + description, + settings, + onSettingsChange, + children, +}: BaseSettingsSectionProps) => { + return ( +
+
+

{title}

+

{description}

+
+ +
+
+ onSettingsChange({ ...settings, enabled })} + label="Enable Widget" + /> +
+ + {children && ( +
+ {children} +
+ )} +
+
+ ); +}; \ No newline at end of file diff --git a/src/frontend/components/Settings/components/ToggleSwitch.tsx b/src/frontend/components/Settings/components/ToggleSwitch.tsx new file mode 100644 index 0000000..2325302 --- /dev/null +++ b/src/frontend/components/Settings/components/ToggleSwitch.tsx @@ -0,0 +1,28 @@ +interface ToggleSwitchProps { + enabled: boolean; + onToggle: (enabled: boolean) => void; + label?: string; +} + +export const ToggleSwitch = ({ enabled, onToggle, label }: ToggleSwitchProps) => { + return ( +
+ + {label && {label}} +
+ ); +}; \ No newline at end of file diff --git a/src/frontend/components/Settings/sections/AdvancedSettings.tsx b/src/frontend/components/Settings/sections/AdvancedSettings.tsx new file mode 100644 index 0000000..e4551de --- /dev/null +++ b/src/frontend/components/Settings/sections/AdvancedSettings.tsx @@ -0,0 +1,59 @@ +import { useState } from 'react'; +import { SettingsForm } from '../SettingsForm'; +import { useDashboard } from '@irdashies/context'; +import { DashboardLayout } from '@irdashies/types'; + +export const AdvancedSettings = () => { + const { currentDashboard, onDashboardUpdated } = useDashboard(); + const [dashboardInput, setDashboardInput] = useState( + JSON.stringify(currentDashboard, undefined, 2) + ); + + if (!currentDashboard || !onDashboardUpdated) { + return <>Loading...; + } + + const onInputUpdated = (e: React.ChangeEvent) => { + setDashboardInput(e.target.value); + }; + + const handleSave = () => { + if (!dashboardInput) { + return; + } + + try { + const dashboard = JSON.parse(dashboardInput); + onDashboardUpdated(dashboard); + } catch (e) { + console.error(e); + alert('Invalid JSON format'); + } + }; + + return ( +
+
+

Advanced Settings

+

Configure advanced system settings and preferences.

+
+ +