Skip to content

Commit 379ccc3

Browse files
feat: enhance EnvironmentDetails with tabbed interface and improved search functionality
1 parent 3b502fd commit 379ccc3

3 files changed

Lines changed: 88 additions & 69 deletions

File tree

  • packages/bruno-app/src

packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/StyledWrapper.js

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@ const StyledWrapper = styled.div`
9696
display: flex;
9797
align-items: center;
9898
gap: 2px;
99+
}
100+
}
101+
102+
.tabs-container {
103+
padding: 0 20px;
104+
flex-shrink: 0;
105+
106+
.env-search-container {
107+
display: flex;
108+
align-items: center;
99109
100110
.search-input-wrapper {
101111
position: relative;
@@ -150,30 +160,6 @@ const StyledWrapper = styled.div`
150160
}
151161
}
152162
}
153-
154-
button {
155-
display: inline-flex;
156-
align-items: center;
157-
justify-content: center;
158-
width: 28px;
159-
height: 28px;
160-
padding: 0;
161-
color: ${(props) => props.theme.colors.text.muted};
162-
background: transparent;
163-
border: none;
164-
border-radius: 5px;
165-
cursor: pointer;
166-
transition: all 0.15s ease;
167-
168-
&:hover {
169-
background: ${(props) => props.theme.sidebar.collection.item.hoverBg};
170-
color: ${(props) => props.theme.text};
171-
}
172-
173-
&:last-child:hover {
174-
color: ${(props) => props.theme.colors.text.danger};
175-
}
176-
}
177163
}
178164
}
179165
@@ -183,6 +169,7 @@ const StyledWrapper = styled.div`
183169
display: flex;
184170
flex-direction: column;
185171
padding: 0 20px 20px 20px;
172+
margin-top: 12px;
186173
}
187174
`;
188175

packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js

Lines changed: 69 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
import { IconCopy, IconEdit, IconTrash, IconCheck, IconX, IconSearch } from '@tabler/icons';
22
import { useState, useRef } from 'react';
3-
import { useDispatch } from 'react-redux';
3+
import { useDispatch, useSelector } from 'react-redux';
44
import { renameEnvironment, updateEnvironmentColor } from 'providers/ReduxStore/slices/collections/actions';
55
import { validateName, validateNameError } from 'utils/common/regex';
66
import toast from 'react-hot-toast';
77
import CopyEnvironment from 'components/Environments/EnvironmentSettings/CopyEnvironment';
88
import DeleteEnvironment from 'components/Environments/EnvironmentSettings/DeleteEnvironment';
99
import EnvironmentVariables from './EnvironmentVariables';
1010
import ColorPicker from 'components/ColorPicker';
11+
import ActionIcon from 'ui/ActionIcon';
12+
import ResponsiveTabs from 'ui/ResponsiveTabs';
13+
import { updateTabState } from 'providers/ReduxStore/slices/tabs';
1114
import StyledWrapper from './StyledWrapper';
1215

16+
const TABS = [{ key: 'variables', label: 'Variables' }];
17+
1318
const EnvironmentDetails = ({ environment, setIsModified, collection, searchQuery, setSearchQuery, isSearchExpanded, setIsSearchExpanded, debouncedSearchQuery, searchInputRef }) => {
1419
const dispatch = useDispatch();
1520
const environments = collection?.environments || [];
@@ -19,7 +24,11 @@ const EnvironmentDetails = ({ environment, setIsModified, collection, searchQuer
1924
const [isRenaming, setIsRenaming] = useState(false);
2025
const [newName, setNewName] = useState('');
2126
const [nameError, setNameError] = useState('');
27+
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
28+
const activeTab = useSelector((state) => state.tabs.tabs.find((t) => t.uid === activeTabUid)?.tabState?.envTab) || 'variables';
29+
const setActiveTab = (tab) => dispatch(updateTabState({ uid: activeTabUid, tabState: { envTab: tab } }));
2230
const inputRef = useRef(null);
31+
const rightContentRef = useRef(null);
2332

2433
const validateEnvironmentName = (name) => {
2534
if (!name || name.trim() === '') {
@@ -187,58 +196,73 @@ const EnvironmentDetails = ({ environment, setIsModified, collection, searchQuer
187196
</div>
188197
{nameError && isRenaming && <div className="title-error">{nameError}</div>}
189198
<div className="actions">
190-
{isSearchExpanded ? (
191-
<div className="search-input-wrapper">
192-
<IconSearch size={14} strokeWidth={1.5} className="search-icon" />
193-
<input
194-
ref={searchInputRef}
195-
type="text"
196-
placeholder="Search variables..."
197-
value={searchQuery}
198-
onChange={(e) => setSearchQuery(e.target.value)}
199-
onBlur={handleSearchBlur}
200-
className="search-input"
201-
autoComplete="off"
202-
autoCorrect="off"
203-
autoCapitalize="off"
204-
spellCheck="false"
205-
/>
206-
{searchQuery && (
207-
<button
208-
className="clear-search"
209-
onClick={handleClearSearch}
210-
onMouseDown={(e) => e.preventDefault()}
211-
title="Clear search"
212-
>
213-
<IconX size={14} strokeWidth={1.5} />
214-
</button>
215-
)}
216-
</div>
217-
) : (
218-
<button onClick={handleSearchIconClick} title="Search variables">
219-
<IconSearch size={15} strokeWidth={1.5} />
220-
</button>
221-
)}
222-
<button onClick={handleRenameClick} title="Rename">
199+
<ActionIcon label="Rename" onClick={handleRenameClick}>
223200
<IconEdit size={15} strokeWidth={1.5} />
224-
</button>
225-
<button onClick={() => setOpenCopyModal(true)} title="Copy">
201+
</ActionIcon>
202+
<ActionIcon label="Copy" onClick={() => setOpenCopyModal(true)}>
226203
<IconCopy size={15} strokeWidth={1.5} />
227-
</button>
228-
<button onClick={() => setOpenDeleteModal(true)} title="Delete">
204+
</ActionIcon>
205+
<ActionIcon label="Delete" onClick={() => setOpenDeleteModal(true)} colorOnHover="danger">
229206
<IconTrash size={15} strokeWidth={1.5} />
230-
</button>
207+
</ActionIcon>
231208
</div>
232209
</div>
233210

234-
<div className="content">
235-
<EnvironmentVariables
236-
environment={environment}
237-
setIsModified={setIsModified}
238-
collection={collection}
239-
searchQuery={debouncedSearchQuery}
211+
<div className="tabs-container">
212+
<ResponsiveTabs
213+
tabs={TABS}
214+
activeTab={activeTab}
215+
onTabSelect={setActiveTab}
216+
rightContent={activeTab === 'variables' ? (
217+
<div ref={rightContentRef} className="env-search-container">
218+
{isSearchExpanded ? (
219+
<div className="search-input-wrapper">
220+
<IconSearch size={14} strokeWidth={1.5} className="search-icon" />
221+
<input
222+
ref={searchInputRef}
223+
type="text"
224+
placeholder="Search variables..."
225+
value={searchQuery}
226+
onChange={(e) => setSearchQuery(e.target.value)}
227+
onBlur={handleSearchBlur}
228+
className="search-input"
229+
autoComplete="off"
230+
autoCorrect="off"
231+
autoCapitalize="off"
232+
spellCheck="false"
233+
/>
234+
{searchQuery && (
235+
<button
236+
className="clear-search"
237+
onClick={handleClearSearch}
238+
onMouseDown={(e) => e.preventDefault()}
239+
title="Clear search"
240+
>
241+
<IconX size={14} strokeWidth={1.5} />
242+
</button>
243+
)}
244+
</div>
245+
) : (
246+
<ActionIcon label="Search variables" onClick={handleSearchIconClick}>
247+
<IconSearch size={15} strokeWidth={1.5} />
248+
</ActionIcon>
249+
)}
250+
</div>
251+
) : null}
252+
rightContentRef={activeTab === 'variables' ? rightContentRef : null}
240253
/>
241254
</div>
255+
256+
<div className="content">
257+
{activeTab === 'variables' && (
258+
<EnvironmentVariables
259+
environment={environment}
260+
setIsModified={setIsModified}
261+
collection={collection}
262+
searchQuery={debouncedSearchQuery}
263+
/>
264+
)}
265+
</div>
242266
</StyledWrapper>
243267
);
244268
};

packages/bruno-app/src/providers/ReduxStore/slices/tabs.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ export const tabsSlice = createSlice({
355355

356356
state.tabs = tabs;
357357
},
358+
updateTabState: (state, action) => {
359+
const { uid, tabState } = action.payload;
360+
const tab = find(state.tabs, (t) => t.uid === uid);
361+
if (tab) {
362+
tab.tabState = { ...tab.tabState, ...tabState };
363+
}
364+
},
358365
reopenLastClosedTab: (state, action) => {
359366
const collectionUid = action.payload?.collectionUid;
360367
// Find the last closed tab for this collection (LIFO). If no collectionUid is
@@ -398,6 +405,7 @@ export const {
398405
closeAllCollectionTabs,
399406
makeTabPermanent,
400407
reorderTabs,
408+
updateTabState,
401409
reopenLastClosedTab,
402410
updateQueryBuilderOpen,
403411
updateQueryBuilderWidth,

0 commit comments

Comments
 (0)