diff --git a/packages/bruno-app/src/components/RequestTabPanel/index.js b/packages/bruno-app/src/components/RequestTabPanel/index.js index c8667eb69..b08e6f100 100644 --- a/packages/bruno-app/src/components/RequestTabPanel/index.js +++ b/packages/bruno-app/src/components/RequestTabPanel/index.js @@ -33,7 +33,7 @@ import WSRequestPane from 'components/RequestPane/WSRequestPane'; import WSResponsePane from 'components/ResponsePane/WsResponsePane'; import { useTabPaneBoundaries } from 'hooks/useTabPaneBoundaries/index'; import ResponseExample from 'components/ResponseExample'; -import WorkspaceOverview from 'components/WorkspaceOverview/index'; +import WorkspaceHome from 'components/WorkspaceHome'; const MIN_LEFT_PANE_WIDTH = 300; const MIN_RIGHT_PANE_WIDTH = 350; @@ -137,7 +137,7 @@ const RequestTabPanel = () => { }, [dragging]); if (!activeTabUid) { - return ; + return ; } if (!focusedTab || !focusedTab.uid || !focusedTab.collectionUid) { diff --git a/packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js b/packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js new file mode 100644 index 000000000..db7499c75 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/CollectionSearch/index.js @@ -0,0 +1,40 @@ +import { IconSearch, IconX } from '@tabler/icons'; + +const CollectionSearch = ({ searchText, setSearchText }) => { + return ( +
+
+ + + +
+ setSearchText(e.target.value.toLowerCase())} + /> + {searchText !== '' && ( +
+ { + setSearchText(''); + }} + > + + +
+ )} +
+ ); +}; + +export default CollectionSearch; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/CreateOrOpenCollection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/CreateOrOpenCollection/index.js index e3cd694fd..fbaefa2f8 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/CreateOrOpenCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/CreateOrOpenCollection/index.js @@ -48,8 +48,6 @@ const CreateOrOpenCollection = () => { {createCollectionModalOpen ? ( setCreateCollectionModalOpen(false)} - workspaceUid={activeWorkspaceUid} - hideLocationInput={activeWorkspace && activeWorkspace.type !== 'default'} /> ) : null} diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js index 52dfc440b..c569d54bb 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js @@ -1,10 +1,10 @@ import React, { useState } from 'react'; import { useSelector } from 'react-redux'; -import { IconSearch, IconX } from '@tabler/icons'; -import Collection from '../Collections/Collection'; +import Collection from './Collection'; import CreateCollection from '../CreateCollection'; import StyledWrapper from './StyledWrapper'; import CreateOrOpenCollection from './CreateOrOpenCollection'; +import CollectionSearch from './CollectionSearch/index'; const Collections = ({ showSearch }) => { const [searchText, setSearchText] = useState(''); @@ -14,27 +14,16 @@ const Collections = ({ showSearch }) => { const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid) || workspaces.find((w) => w.type === 'default'); - let allCollections = []; + let workspaceCollections = []; - if (!activeWorkspace || activeWorkspace.type === 'default') { - if (activeWorkspace && activeWorkspace.collections && activeWorkspace.collections.length > 0) { - allCollections = activeWorkspace.collections.map((wc) => { - const loadedCollection = collections.find((c) => c.pathname === wc.path); - return loadedCollection; - }).filter(Boolean); - } else { - allCollections = []; - } - } else { - if (activeWorkspace.collections && activeWorkspace.collections.length > 0) { - allCollections = activeWorkspace.collections.map((wc) => { - const loadedCollection = collections.find((c) => c.pathname === wc.path); - return loadedCollection; - }).filter(Boolean); - } + if (activeWorkspace?.collections?.length) { + workspaceCollections = activeWorkspace.collections.map((wc) => { + return collections.find((c) => c.pathname === wc.path); + }).filter(Boolean); } - if (!allCollections || !allCollections.length) { + + if (!workspaceCollections || !workspaceCollections.length) { return ( @@ -47,50 +36,16 @@ const Collections = ({ showSearch }) => { {createCollectionModalOpen ? ( setCreateCollectionModalOpen(false)} - workspaceUid={activeWorkspace?.uid} - defaultLocation={activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : ''} - hideLocationInput={!!activeWorkspace?.pathname} /> ) : null} {showSearch && ( -
-
- - - -
- setSearchText(e.target.value.toLowerCase())} - /> - {searchText !== '' && ( -
- { - setSearchText(''); - }} - > - - -
- )} -
+ )}
- {allCollections && allCollections.length - ? allCollections.map((c) => { + {workspaceCollections && workspaceCollections.length + ? workspaceCollections.map((c) => { return ( ); diff --git a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js index 1706550d9..70cc274cb 100644 --- a/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js +++ b/packages/bruno-app/src/components/Sidebar/CreateCollection/index.js @@ -4,7 +4,6 @@ import { useFormik } from 'formik'; import * as Yup from 'yup'; import path from 'path'; import { browseDirectory, createCollection } from 'providers/ReduxStore/slices/collections/actions'; -import { loadWorkspaceCollections } from 'providers/ReduxStore/slices/workspaces/actions'; import toast from 'react-hot-toast'; import Portal from 'components/Portal'; import Modal from 'components/Modal'; @@ -20,21 +19,27 @@ import Dropdown from 'components/Dropdown'; import StyledWrapper from './StyledWrapper'; import get from 'lodash/get'; -const CreateCollection = ({ onClose, workspaceUid, defaultLocation: propDefaultLocation, hideLocationInput = false }) => { +const CreateCollection = ({ onClose, defaultLocation: propDefaultLocation }) => { const inputRef = useRef(); const dispatch = useDispatch(); const workspaces = useSelector((state) => state.workspaces?.workspaces || []); + const workspaceUid = useSelector((state) => state.workspaces?.activeWorkspaceUid); const [isEditing, toggleEditing] = useState(false); const preferences = useSelector((state) => state.app.preferences); const [showExternalLocation, setShowExternalLocation] = useState(false); const [showAdvanced, setShowAdvanced] = useState(false); - const defaultLocation = propDefaultLocation || get(preferences, 'general.defaultCollectionLocation', ''); const dropdownTippyRef = useRef(); const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); const activeWorkspace = workspaces.find((w) => w.uid === workspaceUid); const isDefaultWorkspace = activeWorkspace?.type === 'default'; + const hideLocationInput = activeWorkspace && activeWorkspace.type !== 'default' && !!activeWorkspace?.pathname; + + const defaultLocation = propDefaultLocation + || (activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : '') + || get(preferences, 'general.defaultCollectionLocation', ''); + const shouldShowAccordion = workspaceUid && hideLocationInput && !isDefaultWorkspace; const actuallyHideLocationInput = hideLocationInput && !showExternalLocation && !isDefaultWorkspace; @@ -87,7 +92,6 @@ const CreateCollection = ({ onClose, workspaceUid, defaultLocation: propDefaultL path: collectionPath }; await ipcRenderer.invoke('renderer:add-collection-to-workspace', currentWorkspace.pathname, workspaceCollection); - await dispatch(loadWorkspaceCollections(workspaceUid)); } toast.success('Collection created!'); diff --git a/packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js b/packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js index 191261b89..288a02e98 100644 --- a/packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js +++ b/packages/bruno-app/src/components/Sidebar/TitleBar/StyledWrapper.js @@ -6,32 +6,13 @@ const StyledWrapper = styled.div` align-items: center; } - .workspace-icon-container { - display: flex; - align-items: center; - justify-content: center; - } - - .workspace-letter-logo { - width: 20px; - height: 20px; - border-radius: 4px; - display: flex; - align-items: center; - justify-content: center; - font-size: 12px; - font-weight: 500; - background: white; - color: #5d5d5d; - } - .workspace-name-container { display: flex; align-items: center; gap: 4px; padding: 6px 10px; margin-left: 0px; - border-radius: 6px; + border-radius: ${(props) => props.theme.border.radius.base}; cursor: pointer; transition: all 0.2s ease; min-width: 0; @@ -43,7 +24,7 @@ const StyledWrapper = styled.div` } .workspace-name { - font-size: 13px; + font-size: ${(props) => props.theme.font.size.base}; font-weight: 600; color: ${(props) => props.theme.sidebar.color}; white-space: nowrap; @@ -103,7 +84,7 @@ const StyledWrapper = styled.div` .workspace-name { flex: 1; min-width: 0; - font-size: 13px; + font-size: ${(props) => props.theme.font.size.base}; font-weight: 400; color: ${(props) => props.theme.dropdown.color}; white-space: nowrap; diff --git a/packages/bruno-app/src/components/Sidebar/TitleBar/WorkspaceSelector/index.js b/packages/bruno-app/src/components/Sidebar/TitleBar/WorkspaceSelector/index.js new file mode 100644 index 000000000..86d279bc7 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/TitleBar/WorkspaceSelector/index.js @@ -0,0 +1,136 @@ +import { useState, forwardRef, useRef, useMemo, useCallback } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import toast from 'react-hot-toast'; +import { IconPlus, IconChevronDown, IconCheck, IconFolder, IconPin, IconPinned } from '@tabler/icons'; + +import { savePreferences } from 'providers/ReduxStore/slices/app'; +import { switchWorkspace, openWorkspaceDialog } from 'providers/ReduxStore/slices/workspaces/actions'; +import { sortWorkspaces, toggleWorkspacePin } from 'utils/workspaces'; + +import Dropdown from 'components/Dropdown'; +import CreateWorkspace from 'components/WorkspaceSidebar/CreateWorkspace'; + +const WorkspaceSelector = () => { + const dispatch = useDispatch(); + + const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces); + const preferences = useSelector((state) => state.app.preferences); + const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid); + + const sortedWorkspaces = useMemo(() => { + return sortWorkspaces(workspaces, preferences); + }, [workspaces, preferences]); + + const [showDropdown, setShowDropdown] = useState(false); + const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false); + + const dropdownTippyRef = useRef(); + const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref); + + const toTitleCase = (str) => { + if (!str) return ''; + if (str === 'default') return 'Default'; + return str + .split(/[\s-_]+/) + .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); + }; + + const WorkspaceName = forwardRef((props, ref) => { + return ( +
setShowDropdown(!showDropdown)}> + {toTitleCase(activeWorkspace?.name) || 'Default Workspace'} + +
+ ); + }); + + const handleWorkspaceSwitch = (workspaceUid) => { + dispatch(switchWorkspace(workspaceUid)); + setShowDropdown(false); + toast.success(`Switched to ${workspaces.find((w) => w.uid === workspaceUid)?.name}`); + }; + + const handleOpenWorkspace = async () => { + setShowDropdown(false); + try { + await dispatch(openWorkspaceDialog()); + toast.success('Workspace opened successfully'); + } catch (error) { + toast.error(error.message || 'Failed to open workspace'); + } + }; + + const handleCreateWorkspace = () => { + setShowDropdown(false); + setCreateWorkspaceModalOpen(true); + }; + + const handlePinWorkspace = useCallback((workspaceUid, e) => { + e.preventDefault(); + e.stopPropagation(); + const newPreferences = toggleWorkspacePin(workspaceUid, preferences); + dispatch(savePreferences(newPreferences)); + }, [dispatch, preferences]); + + return ( + <> + {createWorkspaceModalOpen && ( + setCreateWorkspaceModalOpen(false)} /> + )} + + } + placement="bottom-start" + style="new" + visible={showDropdown} + onClickOutside={() => setShowDropdown(false)} + > + {sortedWorkspaces.map((workspace) => { + const isActive = workspace.uid === activeWorkspaceUid; + const isPinned = preferences?.workspaces?.pinnedWorkspaceUids?.includes(workspace.uid); + + return ( +
handleWorkspaceSwitch(workspace.uid)} + > + {toTitleCase(workspace.name)} +
+ {workspace.type !== 'default' && ( + + )} + {isActive && } +
+
+ ); + })} + +
Workspaces
+ +
+ + Create workspace +
+
+ + Open workspace +
+
+ + ); +}; + +export default WorkspaceSelector; diff --git a/packages/bruno-app/src/components/Sidebar/TitleBar/index.js b/packages/bruno-app/src/components/Sidebar/TitleBar/index.js index ec333dab8..f2777eb2b 100644 --- a/packages/bruno-app/src/components/Sidebar/TitleBar/index.js +++ b/packages/bruno-app/src/components/Sidebar/TitleBar/index.js @@ -1,19 +1,18 @@ -import { useState, forwardRef, useRef, useMemo, useCallback } from 'react'; +import { useState, useRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import toast from 'react-hot-toast'; -import { IconPlus, IconChevronDown, IconCheck, IconFolder, IconDownload, IconPin, IconPinned, IconHome, IconSearch, IconDeviceDesktop } from '@tabler/icons'; +import { IconPlus, IconFolder, IconDownload, IconHome, IconSearch, IconDeviceDesktop } from '@tabler/icons'; -import { showHomePage, savePreferences } from 'providers/ReduxStore/slices/app'; +import { showHomePage } from 'providers/ReduxStore/slices/app'; import { openCollection, importCollection } from 'providers/ReduxStore/slices/collections/actions'; -import { switchWorkspace, openWorkspaceDialog, importCollectionInWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; -import { sortWorkspaces, toggleWorkspacePin } from 'utils/workspaces'; +import { importCollectionInWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; import Dropdown from 'components/Dropdown'; import ImportCollection from 'components/Sidebar/ImportCollection'; import ImportCollectionLocation from 'components/Sidebar/ImportCollectionLocation'; -import CreateWorkspace from 'components/WorkspaceSidebar/CreateWorkspace'; import CreateCollection from '../CreateCollection'; +import WorkspaceSelector from './WorkspaceSelector'; import StyledWrapper from './StyledWrapper'; const TitleBar = ({ showSearch, setShowSearch }) => { @@ -21,28 +20,15 @@ const TitleBar = ({ showSearch, setShowSearch }) => { const { ipcRenderer } = window; const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces); - const preferences = useSelector((state) => state.app.preferences); const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid); - // Sort workspaces according to preferences - const sortedWorkspaces = useMemo(() => { - return sortWorkspaces(workspaces, preferences); - }, [workspaces, preferences]); - const [importData, setImportData] = useState(null); const [createCollectionModalOpen, setCreateCollectionModalOpen] = useState(false); const [importCollectionModalOpen, setImportCollectionModalOpen] = useState(false); const [importCollectionLocationModalOpen, setImportCollectionLocationModalOpen] = useState(false); - const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false); - const toTitleCase = (str) => { - if (!str) return ''; - if (str === 'default') return 'Default'; - return str - .split(/[\s-_]+/) - .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(' '); - }; + const actionsDropdownTippyRef = useRef(); + const onActionsDropdownCreate = (ref) => (actionsDropdownTippyRef.current = ref); const handleImportCollection = ({ rawData, type }) => { setImportCollectionModalOpen(false); @@ -71,56 +57,12 @@ const TitleBar = ({ showSearch, setShowSearch }) => { }); }; - const [showWorkspaceDropdown, setShowWorkspaceDropdown] = useState(false); - const workspaceDropdownTippyRef = useRef(); - const onWorkspaceDropdownCreate = (ref) => (workspaceDropdownTippyRef.current = ref); - - const actionsDropdownTippyRef = useRef(); - const onActionsDropdownCreate = (ref) => (actionsDropdownTippyRef.current = ref); - - const WorkspaceName = forwardRef((props, ref) => { - return ( -
setShowWorkspaceDropdown(!showWorkspaceDropdown)}> - {toTitleCase(activeWorkspace?.name) || 'Default Workspace'} - -
- ); - }); - const handleToggleSearch = () => { if (setShowSearch) { setShowSearch((prev) => !prev); } }; - const handleWorkspaceSwitch = (workspaceUid) => { - dispatch(switchWorkspace(workspaceUid)); - setShowWorkspaceDropdown(false); - toast.success(`Switched to ${workspaces.find((w) => w.uid === workspaceUid)?.name}`); - }; - - const handleOpenWorkspace = async () => { - setShowWorkspaceDropdown(false); - try { - await dispatch(openWorkspaceDialog()); - toast.success('Workspace opened successfully'); - } catch (error) { - toast.error(error.message || 'Failed to open workspace'); - } - }; - - const handleCreateWorkspace = () => { - setShowWorkspaceDropdown(false); - setCreateWorkspaceModalOpen(true); - }; - - const handlePinWorkspace = useCallback((workspaceUid, e) => { - e.preventDefault(); - e.stopPropagation(); - const newPreferences = toggleWorkspacePin(workspaceUid, preferences); - dispatch(savePreferences(newPreferences)); - }, [dispatch, preferences]); - const handleOpenCollection = () => { const options = {}; if (activeWorkspace?.pathname) { @@ -141,9 +83,6 @@ const TitleBar = ({ showSearch, setShowSearch }) => { {createCollectionModalOpen && ( setCreateCollectionModalOpen(false)} - workspaceUid={activeWorkspace?.uid} - defaultLocation={activeWorkspace?.type !== 'default' && activeWorkspace?.pathname ? `${activeWorkspace.pathname}/collections` : undefined} - hideLocationInput={activeWorkspace?.type !== 'default' && !!activeWorkspace?.pathname} /> )} {importCollectionModalOpen && ( @@ -160,9 +99,6 @@ const TitleBar = ({ showSearch, setShowSearch }) => { handleSubmit={handleImportCollectionLocation} /> )} - {createWorkspaceModalOpen && ( - setCreateWorkspaceModalOpen(false)} /> - )} ); @@ -170,60 +106,8 @@ const TitleBar = ({ showSearch, setShowSearch }) => { {renderModals()}
+ - {/* Workspace Dropdown */} - } - placement="bottom-start" - style="new" - visible={showWorkspaceDropdown} - onClickOutside={() => setShowWorkspaceDropdown(false)} - > - {sortedWorkspaces.map((workspace) => { - const isActive = workspace.uid === activeWorkspaceUid; - const isPinned = preferences?.workspaces?.pinnedWorkspaceUids?.includes(workspace.uid); - - return ( -
handleWorkspaceSwitch(workspace.uid)} - > - {toTitleCase(workspace.name)} -
- {workspace.type !== 'default' && ( - - )} - {isActive && } -
-
- ); - })} - -
Workspaces
- -
- - Create workspace -
-
- - Open workspace -
-
- - {/* Search and Actions */}