Skip to content

Commit a182d92

Browse files
committed
Convert advanced settings tabs to vertical dropdown layout
1 parent 0444b7e commit a182d92

1 file changed

Lines changed: 186 additions & 19 deletions

File tree

src/components/settings/SettingsPage.tsx

Lines changed: 186 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1-
import React from 'react';
2-
import { Routes, Route, Navigate } from 'react-router-dom';
1+
import React, { useState, useEffect, useMemo } from 'react';
2+
import { useLocation, useNavigate } from 'react-router-dom';
33
import { PageTitle } from '@app/components/common/PageTitle/PageTitle';
4-
import SettingsNavigation from './SettingsNavigation';
4+
import styled from 'styled-components';
5+
import {
6+
FilterOutlined,
7+
PictureOutlined,
8+
ApiOutlined,
9+
RobotOutlined,
10+
TwitterOutlined,
11+
InfoCircleOutlined,
12+
WalletOutlined,
13+
GlobalOutlined,
14+
DatabaseOutlined,
15+
DownOutlined,
16+
RightOutlined
17+
} from '@ant-design/icons';
518
import ImageModerationSettings from './ImageModerationSettings';
619
import ContentFilterSettings from './ContentFilterSettings';
720
import NestFeederSettings from './NestFeederSettings';
@@ -12,27 +25,181 @@ import RelayInfoSettings from './RelayInfoSettings';
1225
import QueryCacheSettings from './QueryCacheSettings';
1326
import XNostrSettings from './XNostrSettings';
1427

28+
const SettingsContainer = styled.div`
29+
width: 100%;
30+
max-height: calc(100vh - 150px);
31+
overflow-y: auto;
32+
padding-right: 10px;
33+
`;
34+
35+
const SettingSection = styled.div`
36+
margin-bottom: 1rem;
37+
border-radius: 8px;
38+
overflow: hidden;
39+
background-color: rgba(0, 0, 0, 0.2);
40+
`;
41+
42+
const SectionHeader = styled.div<{ $isActive: boolean }>`
43+
display: flex;
44+
align-items: center;
45+
padding: 16px;
46+
cursor: pointer;
47+
background-color: rgba(0, 0, 0, 0.3);
48+
transition: background-color 0.3s;
49+
50+
&:hover {
51+
background-color: rgba(0, 0, 0, 0.4);
52+
}
53+
54+
${props => props.$isActive && `
55+
background-color: rgba(0, 0, 0, 0.5);
56+
`}
57+
`;
58+
59+
const SectionIcon = styled.span`
60+
margin-right: 12px;
61+
font-size: 16px;
62+
display: flex;
63+
align-items: center;
64+
`;
65+
66+
const SectionTitle = styled.span`
67+
flex: 1;
68+
font-size: 16px;
69+
`;
70+
71+
const SectionContent = styled.div<{ $isVisible: boolean }>`
72+
display: ${props => props.$isVisible ? 'block' : 'none'};
73+
padding: 0;
74+
transition: all 0.3s;
75+
`;
76+
77+
interface SettingItem {
78+
key: string;
79+
path: string;
80+
label: string;
81+
icon: React.ReactNode;
82+
component: React.ReactNode;
83+
}
84+
1585
const SettingsPage: React.FC = () => {
86+
const location = useLocation();
87+
const navigate = useNavigate();
88+
const [activeKey, setActiveKey] = useState<string | undefined>(undefined);
89+
90+
const settingItems: SettingItem[] = useMemo(() => [
91+
{
92+
key: 'image_moderation',
93+
path: '/settings/image-moderation',
94+
label: 'Image Moderation',
95+
icon: <PictureOutlined />,
96+
component: <ImageModerationSettings />
97+
},
98+
{
99+
key: 'content_filter',
100+
path: '/settings/content-filter',
101+
label: 'Content Filter',
102+
icon: <FilterOutlined />,
103+
component: <ContentFilterSettings />
104+
},
105+
{
106+
key: 'nest_feeder',
107+
path: '/settings/nest-feeder',
108+
label: 'Nest Feeder',
109+
icon: <ApiOutlined />,
110+
component: <NestFeederSettings />
111+
},
112+
{
113+
key: 'ollama',
114+
path: '/settings/ollama',
115+
label: 'Ollama',
116+
icon: <RobotOutlined />,
117+
component: <OllamaSettings />
118+
},
119+
{
120+
key: 'xnostr',
121+
path: '/settings/xnostr',
122+
label: 'XNostr',
123+
icon: <TwitterOutlined />,
124+
component: <XNostrSettings />
125+
},
126+
{
127+
key: 'relay_info',
128+
path: '/settings/relay-info',
129+
label: 'Relay Info',
130+
icon: <InfoCircleOutlined />,
131+
component: <RelayInfoSettings />
132+
},
133+
{
134+
key: 'wallet',
135+
path: '/settings/wallet',
136+
label: 'Wallet',
137+
icon: <WalletOutlined />,
138+
component: <WalletSettings />
139+
},
140+
{
141+
key: 'general',
142+
path: '/settings/general',
143+
label: 'General',
144+
icon: <GlobalOutlined />,
145+
component: <GeneralSettings />
146+
},
147+
{
148+
key: 'query_cache',
149+
path: '/settings/query-cache',
150+
label: 'Query Cache',
151+
icon: <DatabaseOutlined />,
152+
component: <QueryCacheSettings />
153+
}
154+
], []);
155+
156+
// Set active key based on current path
157+
useEffect(() => {
158+
const path = location.pathname;
159+
const item = settingItems.find(item => item.path === path);
160+
161+
if (item) {
162+
setActiveKey(item.key);
163+
} else if (path === '/settings' || path === '/settings/') {
164+
// Don't navigate to any settings by default
165+
setActiveKey(undefined);
166+
}
167+
}, [location.pathname, navigate, settingItems]);
168+
169+
const handleSectionClick = (item: SettingItem) => {
170+
if (activeKey === item.key) {
171+
// If clicking the active section, close it
172+
setActiveKey(undefined);
173+
navigate('/settings');
174+
} else {
175+
// Otherwise, open the clicked section
176+
setActiveKey(item.key);
177+
navigate(item.path);
178+
}
179+
};
180+
16181
return (
17182
<>
18183
<PageTitle>Advanced Settings</PageTitle>
19184

20-
<SettingsNavigation />
21-
22-
<Routes>
23-
<Route path="image-moderation" element={<ImageModerationSettings />} />
24-
<Route path="content-filter" element={<ContentFilterSettings />} />
25-
<Route path="nest-feeder" element={<NestFeederSettings />} />
26-
<Route path="ollama" element={<OllamaSettings />} />
27-
<Route path="xnostr" element={<XNostrSettings />} />
28-
<Route path="relay-info" element={<RelayInfoSettings />} />
29-
<Route path="wallet" element={<WalletSettings />} />
30-
<Route path="general" element={<GeneralSettings />} />
31-
<Route path="query-cache" element={<QueryCacheSettings />} />
32-
33-
{/* Redirect to general settings by default */}
34-
<Route path="/" element={<Navigate to="general" replace />} />
35-
</Routes>
185+
<SettingsContainer>
186+
{settingItems.map(item => (
187+
<SettingSection key={item.key}>
188+
<SectionHeader
189+
$isActive={activeKey === item.key}
190+
onClick={() => handleSectionClick(item)}
191+
>
192+
<SectionIcon>{item.icon}</SectionIcon>
193+
<SectionTitle>{item.label}</SectionTitle>
194+
{activeKey === item.key ? <DownOutlined /> : <RightOutlined />}
195+
</SectionHeader>
196+
197+
<SectionContent $isVisible={activeKey === item.key}>
198+
{item.component}
199+
</SectionContent>
200+
</SettingSection>
201+
))}
202+
</SettingsContainer>
36203
</>
37204
);
38205
};

0 commit comments

Comments
 (0)