From 25386a229b9080927c9f3676910b82c6903df593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 29 Sep 2025 11:31:53 +0200 Subject: [PATCH 1/2] refactor: move CollectionsBadge to a dedicaced folder --- .../Collections/CollectionsBadge/index.js | 72 +++++++++++++++++++ .../components/Sidebar/Collections/index.js | 62 ++-------------- 2 files changed, 78 insertions(+), 56 deletions(-) create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js diff --git a/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js b/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js new file mode 100644 index 000000000..8790170b3 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js @@ -0,0 +1,72 @@ +import { + IconArrowsSort, + IconFolders, + IconSortAscendingLetters, + IconSortDescendingLetters, + IconX, +} from '@tabler/icons'; +import { sortCollections } from 'providers/ReduxStore/slices/collections/index'; +import { useDispatch, useSelector } from 'react-redux'; + +const CollectionsBadge = () => { + const dispatch = useDispatch(); + const { collections } = useSelector(state => state.collections); + const { collectionSortOrder } = useSelector(state => state.collections); + const sortCollectionOrder = () => { + let order; + switch (collectionSortOrder) { + case 'default': + order = 'alphabetical'; + break; + case 'alphabetical': + order = 'reverseAlphabetical'; + break; + case 'reverseAlphabetical': + order = 'default'; + break; + } + dispatch(sortCollections({ order })); + }; + + let sortIcon; + if (collectionSortOrder === 'default') { + sortIcon = ; + } else if (collectionSortOrder === 'alphabetical') { + sortIcon = ; + } else { + sortIcon = ; + } + + return ( +
+
+
+ + + + Collections +
+ {collections.length >= 1 && ( +
+ + +
+ )} +
+
+ ); +}; + +export default CollectionsBadge; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/index.js index 119318819..cbe4d42e4 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/index.js @@ -1,64 +1,14 @@ -import React, { useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; import { IconSearch, - IconFolders, - IconArrowsSort, - IconSortAscendingLetters, - IconSortDescendingLetters, - IconX + IconX, } from '@tabler/icons'; -import Collection from './Collection'; +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; import CreateCollection from '../CreateCollection'; -import StyledWrapper from './StyledWrapper'; +import Collection from './Collection'; +import CollectionsBadge from './CollectionsBadge/index'; import CreateOrOpenCollection from './CreateOrOpenCollection'; -import { sortCollections } from 'providers/ReduxStore/slices/collections/actions'; - -// todo: move this to a separate folder -// the coding convention is to keep all the components in a folder named after the component -const CollectionsBadge = () => { - const dispatch = useDispatch(); - const { collections } = useSelector((state) => state.collections); - const { collectionSortOrder } = useSelector((state) => state.collections); - const sortCollectionOrder = () => { - let order; - switch (collectionSortOrder) { - case 'default': - order = 'alphabetical'; - break; - case 'alphabetical': - order = 'reverseAlphabetical'; - break; - case 'reverseAlphabetical': - order = 'default'; - break; - } - dispatch(sortCollections({ order })); - }; - return ( -
-
-
- - - - Collections -
- {collections.length >= 1 && ( - - )} -
-
- ); -}; +import StyledWrapper from './StyledWrapper'; const Collections = () => { const [searchText, setSearchText] = useState(''); From ec8dee5935e7432c9b6f36342af076092aa886f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Munsch?= Date: Mon, 29 Sep 2025 12:18:55 +0200 Subject: [PATCH 2/2] feat: add RemoveCollections component in order to close multiple projects at once, Closes #5460 --- .../Collections/CollectionsBadge/index.js | 23 ++++-- .../Collections/RemoveCollections/index.js | 73 +++++++++++++++++++ 2 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 packages/bruno-app/src/components/Sidebar/Collections/RemoveCollections/index.js diff --git a/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js b/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js index 8790170b3..09cb7f9e4 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/CollectionsBadge/index.js @@ -1,3 +1,5 @@ +import { useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import { IconArrowsSort, IconFolders, @@ -6,7 +8,7 @@ import { IconX, } from '@tabler/icons'; import { sortCollections } from 'providers/ReduxStore/slices/collections/index'; -import { useDispatch, useSelector } from 'react-redux'; +import RemoveCollections from '../RemoveCollections/index'; const CollectionsBadge = () => { const dispatch = useDispatch(); @@ -37,6 +39,14 @@ const CollectionsBadge = () => { sortIcon = ; } + const [collectionsToClose, setCollectionsToClose] = useState([]); + const addAllCollectionsToClose = () => { + setCollectionsToClose(collections.map(c => c.uid)); + }; + const emptyCollections = () => { + setCollectionsToClose([]); + }; + return (
@@ -48,20 +58,19 @@ const CollectionsBadge = () => {
{collections.length >= 1 && (
- - + {collectionsToClose.length > 0 && ( + + )}
)}
diff --git a/packages/bruno-app/src/components/Sidebar/Collections/RemoveCollections/index.js b/packages/bruno-app/src/components/Sidebar/Collections/RemoveCollections/index.js new file mode 100644 index 000000000..c659eaeef --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/RemoveCollections/index.js @@ -0,0 +1,73 @@ +import React from 'react'; +import toast from 'react-hot-toast'; +import { useDispatch, useSelector } from 'react-redux'; +import PropTypes from 'prop-types'; +import { IconFiles } from '@tabler/icons'; +import Modal from 'components/Modal'; +import { removeCollection } from 'providers/ReduxStore/slices/collections/actions'; +import { findCollectionByUid } from 'utils/collections/index'; + +const RemoveCollections = ({ collectionUids, onClose }) => { + const dispatch = useDispatch(); + const allCollections = useSelector(state => state.collections.collections || []); + + const selectedCollections = collectionUids + .map(uid => findCollectionByUid(allCollections, uid)) + .filter(Boolean); + + const collectionsNames = selectedCollections.map(c => c.name).join(', '); + const collectionsPathnames = selectedCollections.map(c => c.pathname).join(', '); + + const onConfirm = () => { + const removalPromises = selectedCollections.map(collection => { + return dispatch(removeCollection(collection.uid)); + }); + + Promise.all(removalPromises) + .then(() => { + toast.success('Collections are closed'); + }) + .catch(() => { + toast.error('An error occurred while closing collections'); + }) + .finally(() => { + if (onClose) onClose(); + }); + }; + + const getConfirmationText = () => { + if (collectionUids.length > 1) { + return `Are you sure you want to close all ${collectionUids.length} collections in Bruno?`; + } + return ( + + Are you sure you want to close the collection + {' '} + {collectionsNames} + {' '} + in Bruno? + + ); + }; + + return ( + +
+ + {collectionsNames} +
+
{collectionsPathnames}
+
{getConfirmationText()}
+
+ It will still be available in the file system at the above locations and can be re-opened later. +
+
+ ); +}; + +RemoveCollections.propTypes = { + collectionUids: PropTypes.arrayOf(PropTypes.string).isRequired, + onClose: PropTypes.func, +}; + +export default RemoveCollections;