Skip to content

Commit ff7c43c

Browse files
feat: add ToggleSwitchWidget to dashboard builder with configurable state mapping and write support
1 parent b8eae4a commit ff7c43c

6 files changed

Lines changed: 409 additions & 10 deletions

File tree

src/components/dashboard-builder/ComponentSidebar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { Card, CardContent } from '@/components/ui/card';
3-
import { Gauge, LineChart, Hash, Settings, PieChart } from 'lucide-react';
3+
import { Gauge, LineChart, Hash, Settings, PieChart, ToggleRight } from 'lucide-react';
44
import { useBuilderStore } from '../../store/useBuilderStore';
55
import { WIDGET_SIZE_CONSTRAINTS } from './widgetConfig';
66

@@ -9,7 +9,8 @@ const WIDGET_TYPES = [
99
{ id: 'HistoricalTrendWidget', name: 'Historical Trend', icon: <LineChart size={18} /> },
1010
{ id: 'ValueDisplayWidget', name: 'Value Display', icon: <Hash size={18} /> },
1111
{ id: 'ValueStoreWidget', name: 'Value Store Control', icon: <Settings size={18} /> },
12-
{ id: 'DonutChartWidget', name: 'Donut Chart', icon: <PieChart size={18} /> },
12+
{ id: 'DonutChartWidget', name: 'Donut Chart', icon: <PieChart size={18} /> },
13+
{ id: 'ToggleSwitchWidget', name: 'Toggle Switch', icon: <ToggleRight size={18} /> },
1314
];
1415

1516
export default function ComponentSidebar() {

src/components/dashboard-builder/PropertiesPanel.tsx

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,111 @@ export default function PropertiesPanel() {
530530
);
531531
})()}
532532

533+
{/* ── ToggleSwitchWidget ───────────────────────────────────── */}
534+
{widget.type === 'ToggleSwitchWidget' && (
535+
<div className="space-y-4">
536+
537+
{/* ValueStore key */}
538+
<div className="space-y-2">
539+
<Label>ValueStore Key</Label>
540+
<Input
541+
placeholder="e.g. generator_state"
542+
value={draftConfig.deviceKey || ''}
543+
onChange={(e) => handleConfigChange({ deviceKey: e.target.value })}
544+
/>
545+
<p className="text-xs text-muted-foreground">
546+
The key in Anedya ValueStore that holds this toggle's state.
547+
</p>
548+
</div>
549+
550+
{/* Data Type */}
551+
<div className="space-y-2">
552+
<Label>Expected Data Type</Label>
553+
<Select
554+
value={draftConfig.dataType || 'auto'}
555+
onValueChange={(val) => handleConfigChange({ dataType: val === 'auto' ? undefined : val })}
556+
>
557+
<SelectTrigger className="h-8">
558+
<SelectValue placeholder="Auto-detect" />
559+
</SelectTrigger>
560+
<SelectContent>
561+
<SelectItem value="auto">Auto-detect (Default)</SelectItem>
562+
<SelectItem value="boolean">Boolean (true / false)</SelectItem>
563+
<SelectItem value="float">Number</SelectItem>
564+
<SelectItem value="string">String</SelectItem>
565+
</SelectContent>
566+
</Select>
567+
</div>
568+
569+
{/* ON / OFF raw values */}
570+
<div className="grid grid-cols-2 gap-3">
571+
<div className="space-y-1">
572+
<Label className="text-xs">Value when ON</Label>
573+
<Input
574+
className="h-8 text-xs"
575+
placeholder="1"
576+
value={draftConfig.onValue ?? '1'}
577+
onChange={(e) => handleConfigChange({ onValue: e.target.value })}
578+
/>
579+
</div>
580+
<div className="space-y-1">
581+
<Label className="text-xs">Value when OFF</Label>
582+
<Input
583+
className="h-8 text-xs"
584+
placeholder="0"
585+
value={draftConfig.offValue ?? '0'}
586+
onChange={(e) => handleConfigChange({ offValue: e.target.value })}
587+
/>
588+
</div>
589+
</div>
590+
591+
{/* Status labels */}
592+
<div className="space-y-2">
593+
<Label className="flex items-center gap-1.5">Status Labels</Label>
594+
<div className="grid grid-cols-2 gap-3">
595+
<div className="space-y-1">
596+
<Label className="text-xs">ON label</Label>
597+
<Input
598+
className="h-8 text-xs"
599+
placeholder="Active"
600+
value={draftConfig.onLabel || ''}
601+
onChange={(e) => handleConfigChange({ onLabel: e.target.value })}
602+
/>
603+
</div>
604+
<div className="space-y-1">
605+
<Label className="text-xs">OFF label</Label>
606+
<Input
607+
className="h-8 text-xs"
608+
placeholder="Inactive"
609+
value={draftConfig.offLabel || ''}
610+
onChange={(e) => handleConfigChange({ offLabel: e.target.value })}
611+
/>
612+
</div>
613+
</div>
614+
</div>
615+
616+
{/* ON colour */}
617+
<div className="space-y-2 border-t pt-3">
618+
<Label className="flex items-center gap-1.5">
619+
<Palette className="h-3.5 w-3.5" />
620+
ON Colour
621+
</Label>
622+
<div className="flex items-center gap-3">
623+
<Input
624+
type="color"
625+
className="h-8 w-12 p-0.5 cursor-pointer"
626+
value={draftConfig.onColor || '#22c55e'}
627+
onChange={(e) => handleConfigChange({ onColor: e.target.value })}
628+
/>
629+
<span className="text-xs text-muted-foreground">
630+
Used for the pill, status label, and glow effect when ON.
631+
</span>
632+
</div>
633+
</div>
634+
635+
</div>
636+
)}
637+
533638
</div>
534639
</div>
535640
);

src/components/dashboard-builder/widgetConfig.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const WIDGET_SIZE_CONSTRAINTS: Record<string, WidgetSizeConfig> = {
1717
ValueDisplayWidget: { minW: 1, minH: 1, maxW: 12, maxH: 24, defaultW: 3, defaultH: 1.5 },
1818
ValueStoreWidget: { minW: 3, minH: 1, maxW: 12, maxH: 12, defaultW: 4, defaultH: 1 },
1919
DonutChartWidget: { minW: 2, minH: 2, maxW: 8, maxH: 8, defaultW: 3, defaultH: 3 },
20+
ToggleSwitchWidget: { minW: 2, minH: 1, maxW: 8, maxH: 4, defaultW: 3, defaultH: 1 },
2021
};
2122

2223
/** Returns constraints for a widget type, with safe fallback */

0 commit comments

Comments
 (0)