Skip to content

Commit c74b26d

Browse files
feat/refactor: settings cleanup (#381)
Co-authored-by: Tarik Alani <tarik.nzl@gmail.com>
1 parent be3c79c commit c74b26d

24 files changed

+3729
-2903
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Meta, StoryObj } from '@storybook/react-vite';
2+
import { useState } from 'react';
3+
import { TabButton } from './TabButton';
4+
import type { SettingsTabType } from '../types';
5+
6+
const meta = {
7+
component: TabButton,
8+
title: 'components/TabButton',
9+
} satisfies Meta<typeof TabButton>;
10+
11+
export default meta;
12+
type Story = StoryObj<typeof TabButton>;
13+
14+
export const Interactive: Story = {
15+
render: () => {
16+
const [activeTab, setActiveTab] =
17+
useState<SettingsTabType>('layout');
18+
19+
return (
20+
<div className="bg-slate-900 p-6 flex gap-4">
21+
<TabButton
22+
id="layout"
23+
activeTab={activeTab}
24+
setActiveTab={setActiveTab}
25+
>
26+
Layout
27+
</TabButton>
28+
29+
<TabButton
30+
id="options"
31+
activeTab={activeTab}
32+
setActiveTab={setActiveTab}
33+
>
34+
Options
35+
</TabButton>
36+
37+
<TabButton
38+
id="visibility"
39+
activeTab={activeTab}
40+
setActiveTab={setActiveTab}
41+
>
42+
Visibility
43+
</TabButton>
44+
</div>
45+
);
46+
},
47+
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { SettingsTabType } from '../types';
2+
3+
interface TabButtonProps {
4+
id: SettingsTabType;
5+
activeTab: SettingsTabType;
6+
setActiveTab: (tab: SettingsTabType) => void;
7+
children: React.ReactNode;
8+
};
9+
10+
export const TabButton = ({ id, activeTab, setActiveTab, children }: TabButtonProps) => (
11+
<button
12+
onClick={() => setActiveTab(id)}
13+
className={`px-4 py-2 text-sm border-b-2 transition-colors ${
14+
activeTab === id
15+
? 'text-white border-blue-500'
16+
: 'text-slate-400 border-transparent hover:text-slate-200'
17+
}`}
18+
>
19+
{children}
20+
</button>
21+
);
Lines changed: 164 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect } from 'react';
22
import { BaseSettingsSection } from '../components/BaseSettingsSection';
33
import { useDashboard } from '@irdashies/context';
44
import {
55
BlindSpotMonitorWidgetSettings,
66
SessionVisibilitySettings,
7+
SettingsTabType,
78
} from '../types';
89
import { SessionVisibility } from '../components/SessionVisibility';
910
import { ToggleSwitch } from '../components/ToggleSwitch';
11+
import { TabButton } from '../components/TabButton';
1012

1113
const SETTING_ID = 'blindspotmonitor';
1214

@@ -61,6 +63,15 @@ export const BlindSpotMonitorSettings = () => {
6163
config: migrateConfig(savedSettings?.config),
6264
});
6365

66+
// Tab state with persistence
67+
const [activeTab, setActiveTab] = useState<SettingsTabType>(
68+
() => (localStorage.getItem('bsmTab') as SettingsTabType) || 'display'
69+
);
70+
71+
useEffect(() => {
72+
localStorage.setItem('bsmTab', activeTab);
73+
}, [activeTab]);
74+
6475
if (!currentDashboard) {
6576
return <>Loading...</>;
6677
}
@@ -75,128 +86,166 @@ export const BlindSpotMonitorSettings = () => {
7586
>
7687
{(handleConfigChange) => (
7788
<div className="space-y-4">
78-
{/* Background Opacity */}
79-
<div className="space-y-2">
80-
<label className="text-slate-300">
81-
Background Opacity: {settings.config.background?.opacity ?? 30}%
82-
</label>
83-
<input
84-
type="range"
85-
min="0"
86-
max="100"
87-
step="5"
88-
value={settings.config.background?.opacity ?? 30}
89-
onChange={(e) =>
90-
handleConfigChange({
91-
background: { opacity: parseInt(e.target.value) },
92-
})
93-
}
94-
className="w-full"
95-
/>
96-
</div>
9789

98-
{/* Distance Ahead */}
99-
<div className="space-y-2">
100-
<label className="text-slate-300">
101-
Distance Ahead: {settings.config.distAhead}m
102-
</label>
103-
<input
104-
type="range"
105-
min="3"
106-
max="6"
107-
step="0.1"
108-
value={settings.config.distAhead}
109-
onChange={(e) =>
110-
handleConfigChange({ distAhead: parseFloat(e.target.value) })
111-
}
112-
className="w-full"
113-
/>
114-
<p className="text-slate-400 text-sm">
115-
Distance to car ahead in meters. Distance at which point line
116-
starts to appear at the top.
117-
</p>
90+
{/* Tabs */}
91+
<div className="flex border-b border-slate-700/50">
92+
<TabButton id="display" activeTab={activeTab} setActiveTab={setActiveTab}>
93+
Display
94+
</TabButton>
95+
<TabButton id="options" activeTab={activeTab} setActiveTab={setActiveTab}>
96+
Options
97+
</TabButton>
98+
<TabButton id="visibility" activeTab={activeTab} setActiveTab={setActiveTab}>
99+
Visibility
100+
</TabButton>
118101
</div>
119102

120-
{/* Distance Behind */}
121-
<div className="space-y-2">
122-
<label className="text-slate-300">
123-
Distance Behind: {settings.config.distBehind}m
124-
</label>
125-
<input
126-
type="range"
127-
min="3"
128-
max="6"
129-
step="0.1"
130-
value={settings.config.distBehind}
131-
onChange={(e) =>
132-
handleConfigChange({ distBehind: parseFloat(e.target.value) })
133-
}
134-
className="w-full"
135-
/>
136-
<p className="text-slate-400 text-sm">
137-
Distance to car behind in meters. Distance at which point line
138-
starts to appear at the bottom.
139-
</p>
140-
</div>
103+
<div className="pt-4">
141104

142-
{/* Width */}
143-
<div className="space-y-2">
144-
<label className="text-slate-300">
145-
Width: {settings.config.width ?? 20}px
146-
</label>
147-
<input
148-
type="range"
149-
min="5"
150-
max="100"
151-
step="1"
152-
value={settings.config.width ?? 20}
153-
onChange={(e) =>
154-
handleConfigChange({ width: parseInt(e.target.value) })
155-
}
156-
className="w-full"
157-
/>
158-
<p className="text-slate-400 text-sm">
159-
Width of the blind spot indicator in pixels.
160-
</p>
161-
</div>
105+
{/* DISPLAY TAB */}
106+
{activeTab === 'display' && (
107+
<div className="space-y-4">
108+
<h3 className="text-lg font-medium text-slate-200">Display</h3>
109+
<div className="pl-4 space-y-4">
162110

163-
{/* IsOnTrack Section */}
164-
<div className="flex items-center justify-between">
165-
<div>
166-
<h4 className="text-md font-medium text-slate-300">
167-
Show only when on track
168-
</h4>
169-
<span className="block text-xs text-slate-500">
170-
If enabled, blind spotter will only be shown when you are
171-
driving.
172-
</span>
173-
</div>
174-
<ToggleSwitch
175-
enabled={settings.config.showOnlyWhenOnTrack}
176-
onToggle={(newValue) =>
177-
handleConfigChange({
178-
showOnlyWhenOnTrack: newValue,
179-
})
180-
}
181-
/>
182-
</div>
111+
{/* Background Opacity */}
112+
<div className="space-y-2">
113+
<label className="text-slate-300">
114+
Background Opacity: {settings.config.background?.opacity ?? 30}%
115+
</label>
116+
<input
117+
type="range"
118+
min="0"
119+
max="100"
120+
step="5"
121+
value={settings.config.background?.opacity ?? 30}
122+
onChange={(e) =>
123+
handleConfigChange({
124+
background: { opacity: parseInt(e.target.value) },
125+
})
126+
}
127+
className="w-full"
128+
/>
129+
</div>
130+
131+
{/* Width */}
132+
<div className="space-y-2">
133+
<label className="text-slate-300">
134+
Width: {settings.config.width ?? 20}px
135+
</label>
136+
<input
137+
type="range"
138+
min="5"
139+
max="100"
140+
step="1"
141+
value={settings.config.width ?? 20}
142+
onChange={(e) =>
143+
handleConfigChange({ width: parseInt(e.target.value) })
144+
}
145+
className="w-full"
146+
/>
147+
<p className="text-xs text-slate-500">
148+
Width of the blind spot indicator in pixels.
149+
</p>
150+
</div>
151+
152+
</div>
153+
</div>
154+
)}
155+
156+
{/* OPTIONS TAB */}
157+
{activeTab === 'options' && (
158+
<div className="space-y-4">
159+
<h3 className="text-lg font-medium text-slate-200">Options</h3>
160+
<div className="pl-4 space-y-4">
161+
162+
{/* Distance Ahead */}
163+
<div className="space-y-2">
164+
<label className="text-slate-300">
165+
Distance Ahead: {settings.config.distAhead}m
166+
</label>
167+
<input
168+
type="range"
169+
min="3"
170+
max="6"
171+
step="0.1"
172+
value={settings.config.distAhead}
173+
onChange={(e) =>
174+
handleConfigChange({ distAhead: parseFloat(e.target.value) })
175+
}
176+
className="w-full"
177+
/>
178+
<p className="text-xs text-slate-500">
179+
Distance to car ahead in meters.
180+
</p>
181+
</div>
182+
183+
{/* Distance Behind */}
184+
<div className="space-y-2">
185+
<label className="text-slate-300">
186+
Distance Behind: {settings.config.distBehind}m
187+
</label>
188+
<input
189+
type="range"
190+
min="3"
191+
max="6"
192+
step="0.1"
193+
value={settings.config.distBehind}
194+
onChange={(e) =>
195+
handleConfigChange({ distBehind: parseFloat(e.target.value) })
196+
}
197+
className="w-full"
198+
/>
199+
<p className="text-xs text-slate-500">
200+
Distance to car behind in meters.
201+
</p>
202+
</div>
203+
204+
</div>
205+
</div>
206+
)}
207+
208+
{/* VISIBILITY TAB */}
209+
{activeTab === 'visibility' && (
210+
<div className="space-y-4">
211+
212+
<div className="space-y-4">
213+
<h3 className="text-lg font-medium text-slate-200">
214+
Session Visibility
215+
</h3>
216+
<div className="space-y-3 pl-4">
217+
<SessionVisibility
218+
sessionVisibility={settings.config.sessionVisibility}
219+
handleConfigChange={handleConfigChange}
220+
/>
221+
</div>
222+
</div>
223+
224+
<div className="flex items-center justify-between pt-4 border-t border-slate-700/50 pl-4">
225+
<div>
226+
<h4 className="text-md font-medium text-slate-300">
227+
Show only when on track
228+
</h4>
229+
<span className="block text-xs text-slate-500">
230+
If enabled, blind spotter will only be shown when driving.
231+
</span>
232+
</div>
233+
<ToggleSwitch
234+
enabled={settings.config.showOnlyWhenOnTrack}
235+
onToggle={(newValue) =>
236+
handleConfigChange({
237+
showOnlyWhenOnTrack: newValue,
238+
})
239+
}
240+
/>
241+
</div>
242+
243+
</div>
244+
)}
183245

184-
{/* Session Visibility Settings */}
185-
<div className="space-y-4">
186-
<div className="flex items-center justify-between">
187-
<h3 className="text-lg font-medium text-slate-200">
188-
Session Visibility
189-
</h3>
190-
</div>
191-
<div className="space-y-3 pl-4">
192-
<SessionVisibility
193-
sessionVisibility={settings.config.sessionVisibility}
194-
handleConfigChange={handleConfigChange}
195-
/>
196-
</div>
197246
</div>
198247
</div>
199248
)}
200249
</BaseSettingsSection>
201250
);
202-
};
251+
};

0 commit comments

Comments
 (0)