Skip to content

Commit 097dffd

Browse files
authored
Merge pull request #1342 from Stijnus/ACT_FEAT_BoltDYI_UI_BUGFIX
fix: bolt dyi UI bugfix
2 parents 0e2e183 + ca7f5ad commit 097dffd

File tree

12 files changed

+500
-390
lines changed

12 files changed

+500
-390
lines changed

app/components/@settings/core/ControlPanel.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,27 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
263263
},
264264
};
265265

266+
// Reset to default view when modal opens/closes
267+
useEffect(() => {
268+
if (!open) {
269+
// Reset when closing
270+
setActiveTab(null);
271+
setLoadingTab(null);
272+
setShowTabManagement(false);
273+
} else {
274+
// When opening, set to null to show the main view
275+
setActiveTab(null);
276+
}
277+
}, [open]);
278+
279+
// Handle closing
280+
const handleClose = () => {
281+
setActiveTab(null);
282+
setLoadingTab(null);
283+
setShowTabManagement(false);
284+
onClose();
285+
};
286+
266287
// Handlers
267288
const handleBack = () => {
268289
if (showTabManagement) {
@@ -405,8 +426,8 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
405426

406427
<RadixDialog.Content
407428
aria-describedby={undefined}
408-
onEscapeKeyDown={onClose}
409-
onPointerDownOutside={onClose}
429+
onEscapeKeyDown={handleClose}
430+
onPointerDownOutside={handleClose}
410431
className="relative z-[101]"
411432
>
412433
<motion.div
@@ -461,7 +482,7 @@ export const ControlPanel = ({ open, onClose }: ControlPanelProps) => {
461482

462483
{/* Close Button */}
463484
<button
464-
onClick={onClose}
485+
onClick={handleClose}
465486
className="flex items-center justify-center w-8 h-8 rounded-full bg-transparent hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200"
466487
>
467488
<div className="i-ph:x w-4 h-4 text-gray-500 dark:text-gray-400 group-hover:text-purple-500 transition-colors" />

app/components/@settings/shared/components/TabManagement.tsx

Lines changed: 187 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { useState } from 'react';
1+
import { useState, useEffect } from 'react';
22
import { motion } from 'framer-motion';
33
import { useStore } from '@nanostores/react';
4-
import { Switch } from '@radix-ui/react-switch';
4+
import { Switch } from '~/components/ui/Switch';
55
import { classNames } from '~/utils/classNames';
66
import { tabConfigurationStore } from '~/lib/stores/settings';
77
import { TAB_LABELS } from '~/components/@settings/core/constants';
88
import type { TabType } from '~/components/@settings/core/types';
99
import { toast } from 'react-toastify';
1010
import { TbLayoutGrid } from 'react-icons/tb';
11+
import { useSettingsStore } from '~/lib/stores/settings';
1112

1213
// Define tab icons mapping
1314
const TAB_ICONS: Record<TabType, string> = {
@@ -55,6 +56,7 @@ const BetaLabel = () => (
5556
export const TabManagement = () => {
5657
const [searchQuery, setSearchQuery] = useState('');
5758
const tabConfiguration = useStore(tabConfigurationStore);
59+
const { setSelectedTab } = useSettingsStore();
5860

5961
const handleTabVisibilityChange = (tabId: TabType, checked: boolean) => {
6062
// Get current tab configuration
@@ -126,6 +128,13 @@ export const TabManagement = () => {
126128
// Filter tabs based on search query
127129
const filteredTabs = allTabs.filter((tab) => TAB_LABELS[tab.id].toLowerCase().includes(searchQuery.toLowerCase()));
128130

131+
useEffect(() => {
132+
// Reset to first tab when component unmounts
133+
return () => {
134+
setSelectedTab('user'); // Reset to user tab when unmounting
135+
};
136+
}, [setSelectedTab]);
137+
129138
return (
130139
<div className="space-y-6">
131140
<motion.div
@@ -177,92 +186,193 @@ export const TabManagement = () => {
177186

178187
{/* Tab Grid */}
179188
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
180-
{filteredTabs.map((tab, index) => (
181-
<motion.div
182-
key={tab.id}
183-
className={classNames(
184-
'rounded-lg border bg-bolt-elements-background text-bolt-elements-textPrimary',
185-
'bg-bolt-elements-background-depth-2',
186-
'hover:bg-bolt-elements-background-depth-3',
187-
'transition-all duration-200',
188-
'relative overflow-hidden group',
189-
)}
190-
initial={{ opacity: 0, y: 20 }}
191-
animate={{ opacity: 1, y: 0 }}
192-
transition={{ delay: index * 0.1 }}
193-
whileHover={{ scale: 1.02 }}
194-
>
195-
{/* Status Badges */}
196-
<div className="absolute top-2 right-2 flex gap-1">
197-
{DEFAULT_USER_TABS.includes(tab.id) && (
198-
<span className="px-2 py-0.5 text-xs rounded-full bg-purple-500/10 text-purple-500 font-medium">
189+
{/* Default Section Header */}
190+
{filteredTabs.some((tab) => DEFAULT_USER_TABS.includes(tab.id)) && (
191+
<div className="col-span-full flex items-center gap-2 mt-4 mb-2">
192+
<div className="i-ph:star-fill w-4 h-4 text-purple-500" />
193+
<span className="text-sm font-medium text-bolt-elements-textPrimary">Default Tabs</span>
194+
</div>
195+
)}
196+
197+
{/* Default Tabs */}
198+
{filteredTabs
199+
.filter((tab) => DEFAULT_USER_TABS.includes(tab.id))
200+
.map((tab, index) => (
201+
<motion.div
202+
key={tab.id}
203+
className={classNames(
204+
'rounded-lg border bg-bolt-elements-background text-bolt-elements-textPrimary',
205+
'bg-bolt-elements-background-depth-2',
206+
'hover:bg-bolt-elements-background-depth-3',
207+
'transition-all duration-200',
208+
'relative overflow-hidden group',
209+
)}
210+
initial={{ opacity: 0, y: 20 }}
211+
animate={{ opacity: 1, y: 0 }}
212+
transition={{ delay: index * 0.1 }}
213+
whileHover={{ scale: 1.02 }}
214+
>
215+
{/* Status Badges */}
216+
<div className="absolute top-1 right-1.5 flex gap-1">
217+
<span className="px-1.5 py-0.25 text-xs rounded-full bg-purple-500/10 text-purple-500 font-medium mr-2">
199218
Default
200219
</span>
220+
</div>
221+
222+
<div className="flex items-start gap-4 p-4">
223+
<motion.div
224+
className={classNames(
225+
'w-10 h-10 flex items-center justify-center rounded-xl',
226+
'bg-bolt-elements-background-depth-3 group-hover:bg-bolt-elements-background-depth-4',
227+
'transition-all duration-200',
228+
tab.visible ? 'text-purple-500' : 'text-bolt-elements-textSecondary',
229+
)}
230+
whileHover={{ scale: 1.1 }}
231+
whileTap={{ scale: 0.9 }}
232+
>
233+
<div
234+
className={classNames('w-6 h-6', 'transition-transform duration-200', 'group-hover:rotate-12')}
235+
>
236+
<div className={classNames(TAB_ICONS[tab.id], 'w-full h-full')} />
237+
</div>
238+
</motion.div>
239+
240+
<div className="flex-1 min-w-0">
241+
<div className="flex items-center justify-between gap-4">
242+
<div>
243+
<div className="flex items-center gap-2">
244+
<h4 className="text-sm font-medium text-bolt-elements-textPrimary group-hover:text-purple-500 transition-colors">
245+
{TAB_LABELS[tab.id]}
246+
</h4>
247+
{BETA_TABS.has(tab.id) && <BetaLabel />}
248+
</div>
249+
<p className="text-xs text-bolt-elements-textSecondary mt-0.5">
250+
{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}
251+
</p>
252+
</div>
253+
<Switch
254+
checked={tab.visible}
255+
onCheckedChange={(checked) => {
256+
const isDisabled =
257+
!DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id);
258+
259+
if (!isDisabled) {
260+
handleTabVisibilityChange(tab.id, checked);
261+
}
262+
}}
263+
className={classNames('data-[state=checked]:bg-purple-500 ml-4', {
264+
'opacity-50 pointer-events-none':
265+
!DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id),
266+
})}
267+
/>
268+
</div>
269+
</div>
270+
</div>
271+
272+
<motion.div
273+
className="absolute inset-0 border-2 border-purple-500/0 rounded-lg pointer-events-none"
274+
animate={{
275+
borderColor: tab.visible ? 'rgba(168, 85, 247, 0.2)' : 'rgba(168, 85, 247, 0)',
276+
scale: tab.visible ? 1 : 0.98,
277+
}}
278+
transition={{ duration: 0.2 }}
279+
/>
280+
</motion.div>
281+
))}
282+
283+
{/* Optional Section Header */}
284+
{filteredTabs.some((tab) => OPTIONAL_USER_TABS.includes(tab.id)) && (
285+
<div className="col-span-full flex items-center gap-2 mt-8 mb-2">
286+
<div className="i-ph:plus-circle-fill w-4 h-4 text-blue-500" />
287+
<span className="text-sm font-medium text-bolt-elements-textPrimary">Optional Tabs</span>
288+
</div>
289+
)}
290+
291+
{/* Optional Tabs */}
292+
{filteredTabs
293+
.filter((tab) => OPTIONAL_USER_TABS.includes(tab.id))
294+
.map((tab, index) => (
295+
<motion.div
296+
key={tab.id}
297+
className={classNames(
298+
'rounded-lg border bg-bolt-elements-background text-bolt-elements-textPrimary',
299+
'bg-bolt-elements-background-depth-2',
300+
'hover:bg-bolt-elements-background-depth-3',
301+
'transition-all duration-200',
302+
'relative overflow-hidden group',
201303
)}
202-
{OPTIONAL_USER_TABS.includes(tab.id) && (
203-
<span className="px-2 py-0.5 text-xs rounded-full bg-blue-500/10 text-blue-500 font-medium">
304+
initial={{ opacity: 0, y: 20 }}
305+
animate={{ opacity: 1, y: 0 }}
306+
transition={{ delay: index * 0.1 }}
307+
whileHover={{ scale: 1.02 }}
308+
>
309+
{/* Status Badges */}
310+
<div className="absolute top-1 right-1.5 flex gap-1">
311+
<span className="px-1.5 py-0.25 text-xs rounded-full bg-blue-500/10 text-blue-500 font-medium mr-2">
204312
Optional
205313
</span>
206-
)}
207-
</div>
314+
</div>
208315

209-
<div className="flex items-start gap-4 p-4">
210-
<motion.div
211-
className={classNames(
212-
'w-10 h-10 flex items-center justify-center rounded-xl',
213-
'bg-bolt-elements-background-depth-3 group-hover:bg-bolt-elements-background-depth-4',
214-
'transition-all duration-200',
215-
tab.visible ? 'text-purple-500' : 'text-bolt-elements-textSecondary',
216-
)}
217-
whileHover={{ scale: 1.1 }}
218-
whileTap={{ scale: 0.9 }}
219-
>
220-
<div className={classNames('w-6 h-6', 'transition-transform duration-200', 'group-hover:rotate-12')}>
221-
<div className={classNames(TAB_ICONS[tab.id], 'w-full h-full')} />
222-
</div>
223-
</motion.div>
224-
225-
<div className="flex-1 min-w-0">
226-
<div className="flex items-center justify-between gap-4">
227-
<div>
228-
<div className="flex items-center gap-2">
229-
<h4 className="text-sm font-medium text-bolt-elements-textPrimary group-hover:text-purple-500 transition-colors">
230-
{TAB_LABELS[tab.id]}
231-
</h4>
232-
{BETA_TABS.has(tab.id) && <BetaLabel />}
233-
</div>
234-
<p className="text-xs text-bolt-elements-textSecondary mt-0.5">
235-
{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}
236-
</p>
316+
<div className="flex items-start gap-4 p-4">
317+
<motion.div
318+
className={classNames(
319+
'w-10 h-10 flex items-center justify-center rounded-xl',
320+
'bg-bolt-elements-background-depth-3 group-hover:bg-bolt-elements-background-depth-4',
321+
'transition-all duration-200',
322+
tab.visible ? 'text-purple-500' : 'text-bolt-elements-textSecondary',
323+
)}
324+
whileHover={{ scale: 1.1 }}
325+
whileTap={{ scale: 0.9 }}
326+
>
327+
<div
328+
className={classNames('w-6 h-6', 'transition-transform duration-200', 'group-hover:rotate-12')}
329+
>
330+
<div className={classNames(TAB_ICONS[tab.id], 'w-full h-full')} />
237331
</div>
238-
<Switch
239-
checked={tab.visible}
240-
onCheckedChange={(checked) => handleTabVisibilityChange(tab.id, checked)}
241-
disabled={!DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id)}
242-
className={classNames(
243-
'relative inline-flex h-5 w-9 items-center rounded-full',
244-
'transition-colors duration-200',
245-
tab.visible ? 'bg-purple-500' : 'bg-bolt-elements-background-depth-4',
246-
{
247-
'opacity-50 cursor-not-allowed':
332+
</motion.div>
333+
334+
<div className="flex-1 min-w-0">
335+
<div className="flex items-center justify-between gap-4">
336+
<div>
337+
<div className="flex items-center gap-2">
338+
<h4 className="text-sm font-medium text-bolt-elements-textPrimary group-hover:text-purple-500 transition-colors">
339+
{TAB_LABELS[tab.id]}
340+
</h4>
341+
{BETA_TABS.has(tab.id) && <BetaLabel />}
342+
</div>
343+
<p className="text-xs text-bolt-elements-textSecondary mt-0.5">
344+
{tab.visible ? 'Visible in user mode' : 'Hidden in user mode'}
345+
</p>
346+
</div>
347+
<Switch
348+
checked={tab.visible}
349+
onCheckedChange={(checked) => {
350+
const isDisabled =
351+
!DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id);
352+
353+
if (!isDisabled) {
354+
handleTabVisibilityChange(tab.id, checked);
355+
}
356+
}}
357+
className={classNames('data-[state=checked]:bg-purple-500 ml-4', {
358+
'opacity-50 pointer-events-none':
248359
!DEFAULT_USER_TABS.includes(tab.id) && !OPTIONAL_USER_TABS.includes(tab.id),
249-
},
250-
)}
251-
/>
360+
})}
361+
/>
362+
</div>
252363
</div>
253364
</div>
254-
</div>
255365

256-
<motion.div
257-
className="absolute inset-0 border-2 border-purple-500/0 rounded-lg pointer-events-none"
258-
animate={{
259-
borderColor: tab.visible ? 'rgba(168, 85, 247, 0.2)' : 'rgba(168, 85, 247, 0)',
260-
scale: tab.visible ? 1 : 0.98,
261-
}}
262-
transition={{ duration: 0.2 }}
263-
/>
264-
</motion.div>
265-
))}
366+
<motion.div
367+
className="absolute inset-0 border-2 border-purple-500/0 rounded-lg pointer-events-none"
368+
animate={{
369+
borderColor: tab.visible ? 'rgba(168, 85, 247, 0.2)' : 'rgba(168, 85, 247, 0)',
370+
scale: tab.visible ? 1 : 0.98,
371+
}}
372+
transition={{ duration: 0.2 }}
373+
/>
374+
</motion.div>
375+
))}
266376
</div>
267377
</motion.div>
268378
</div>

0 commit comments

Comments
 (0)