Merge pull request #5662 from Kwaadpepper/main

Closes #5460 Close multiple projects at once
This commit is contained in:
Bijin A B
2025-11-10 14:17:42 +05:30
committed by GitHub
3 changed files with 160 additions and 56 deletions

View File

@@ -0,0 +1,81 @@
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
IconArrowsSort,
IconFolders,
IconSortAscendingLetters,
IconSortDescendingLetters,
IconX,
} from '@tabler/icons';
import { sortCollections } from 'providers/ReduxStore/slices/collections/index';
import RemoveCollections from '../RemoveCollections/index';
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 = <IconArrowsSort size={18} strokeWidth={1.5} />;
} else if (collectionSortOrder === 'alphabetical') {
sortIcon = <IconSortAscendingLetters size={18} strokeWidth={1.5} />;
} else {
sortIcon = <IconSortDescendingLetters size={18} strokeWidth={1.5} />;
}
const [collectionsToClose, setCollectionsToClose] = useState([]);
const addAllCollectionsToClose = () => {
setCollectionsToClose(collections.map(c => c.uid));
};
const emptyCollections = () => {
setCollectionsToClose([]);
};
return (
<div className="items-center mt-2 relative">
<div className="collections-badge flex items-center justify-between px-2">
<div className="flex items-center py-1 select-none">
<span className="mr-2">
<IconFolders size={18} strokeWidth={1.5} />
</span>
<span>Collections</span>
</div>
{collections.length >= 1 && (
<div className="flex items-center">
<button onClick={() => sortCollectionOrder()}>
{sortIcon}
</button>
<button className="ml-1" onClick={addAllCollectionsToClose}>
<IconX
size={16}
strokeWidth={1.5}
className="cursor-pointer"
/>
</button>
{collectionsToClose.length > 0 && (
<RemoveCollections collectionUids={collectionsToClose} onClose={emptyCollections} />
)}
</div>
)}
</div>
</div>
);
};
export default CollectionsBadge;

View File

@@ -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 (
<span>
Are you sure you want to close the collection
{' '}
<span className="font-semibold">{collectionsNames}</span>
{' '}
in Bruno?
</span>
);
};
return (
<Modal size="sm" title="Close Collections" confirmText="Close" handleConfirm={onConfirm} handleCancel={onClose}>
<div className="flex items-center">
<IconFiles size={18} strokeWidth={1.5} />
<span className="ml-2 mr-4 font-semibold">{collectionsNames}</span>
</div>
<div className="break-words text-xs mt-1">{collectionsPathnames}</div>
<div className="mt-4">{getConfirmationText()}</div>
<div className="mt-4">
It will still be available in the file system at the above locations and can be re-opened later.
</div>
</Modal>
);
};
RemoveCollections.propTypes = {
collectionUids: PropTypes.arrayOf(PropTypes.string).isRequired,
onClose: PropTypes.func,
};
export default RemoveCollections;

View File

@@ -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 (
<div className="items-center mt-2 relative">
<div className="collections-badge flex items-center justify-between px-2">
<div className="flex items-center py-1 select-none">
<span className="mr-2">
<IconFolders size={18} strokeWidth={1.5} />
</span>
<span>Collections</span>
</div>
{collections.length >= 1 && (
<button onClick={() => sortCollectionOrder()}>
{collectionSortOrder == 'default' ? (
<IconArrowsSort size={18} strokeWidth={1.5} />
) : collectionSortOrder == 'alphabetical' ? (
<IconSortAscendingLetters size={18} strokeWidth={1.5} />
) : (
<IconSortDescendingLetters size={18} strokeWidth={1.5} />
)}
</button>
)}
</div>
</div>
);
};
import StyledWrapper from './StyledWrapper';
const Collections = () => {
const [searchText, setSearchText] = useState('');