mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-02 08:58:32 +00:00
feat: local collections displayed separately (resolves #22)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import Modal from 'components/Modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { removeLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
|
||||
|
||||
const RemoveLocalCollection = ({onClose, collection}) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const onConfirm = () =>{
|
||||
dispatch(removeLocalCollection(collection.uid))
|
||||
.then(() => {
|
||||
toast.success("Collection removed");
|
||||
onClose();
|
||||
})
|
||||
.catch(() => toast.error("An error occured while removing the collection"));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="sm"
|
||||
title="Remove Collection"
|
||||
confirmText="Remove"
|
||||
handleConfirm={onConfirm}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
Are you sure you want to remove this collection?
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default RemoveLocalCollection;
|
||||
@@ -10,6 +10,7 @@ import NewRequest from 'components/Sidebar/NewRequest';
|
||||
import NewFolder from 'components/Sidebar/NewFolder';
|
||||
import CollectionItem from './CollectionItem';
|
||||
import RemoveCollectionFromWorkspace from './RemoveCollectionFromWorkspace';
|
||||
import RemoveLocalCollection from './RemoveLocalCollection';
|
||||
import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search';
|
||||
import { isItemAFolder, isItemARequest, transformCollectionToSaveToIdb, isLocalCollection } from 'utils/collections';
|
||||
import exportCollection from 'utils/collections/export';
|
||||
@@ -23,6 +24,7 @@ const Collection = ({collection, searchText}) => {
|
||||
const [showNewRequestModal, setShowNewRequestModal] = useState(false);
|
||||
const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false);
|
||||
const [showRemoveCollectionFromWSModal, setShowRemoveCollectionFromWSModal] = useState(false);
|
||||
const [showRemoveLocalCollectionModal, setShowRemoveLocalCollectionModal] = useState(false);
|
||||
const [showDeleteCollectionModal, setShowDeleteCollectionModal] = useState(false);
|
||||
const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed);
|
||||
const dispatch = useDispatch();
|
||||
@@ -76,6 +78,7 @@ const Collection = ({collection, searchText}) => {
|
||||
{showRenameCollectionModal && <RenameCollection collection={collection} onClose={() => setShowRenameCollectionModal(false)}/>}
|
||||
{showRemoveCollectionFromWSModal && <RemoveCollectionFromWorkspace collection={collection} onClose={() => setShowRemoveCollectionFromWSModal(false)}/>}
|
||||
{showDeleteCollectionModal && <DeleteCollection collection={collection} onClose={() => setShowDeleteCollectionModal(false)}/>}
|
||||
{showRemoveLocalCollectionModal && <RemoveLocalCollection collection={collection} onClose={() => setShowRemoveLocalCollectionModal(false)}/>}
|
||||
<div className="flex py-1 collection-name items-center">
|
||||
<div className="flex flex-grow items-center" onClick={handleClick}>
|
||||
<IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{width:16, color: 'rgb(160 160 160)'}}/>
|
||||
@@ -111,12 +114,21 @@ const Collection = ({collection, searchText}) => {
|
||||
}}>
|
||||
Export
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowRemoveCollectionFromWSModal(true);
|
||||
}}>
|
||||
Remove from Workspace
|
||||
</div>
|
||||
{!isLocal ? (
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowRemoveCollectionFromWSModal(true);
|
||||
}}>
|
||||
Remove from Workspace
|
||||
</div>
|
||||
) : (
|
||||
<div className="dropdown-item" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
setShowRemoveLocalCollectionModal(true);
|
||||
}}>
|
||||
Remove
|
||||
</div>
|
||||
)}
|
||||
{!isLocal ? (
|
||||
<div className="dropdown-item delete-collection" onClick={(e) => {
|
||||
menuDropdownTippyRef.current.hide();
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import React from "react";
|
||||
import filter from "lodash/filter";
|
||||
import Modal from "components/Modal/index";
|
||||
import { IconFiles } from '@tabler/icons';
|
||||
import { useSelector } from "react-redux";
|
||||
import { isLocalCollection } from "utils/collections";
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const SelectCollection = ({onClose, onSelect, title}) => {
|
||||
const { collections } = useSelector((state) => state.collections);
|
||||
const collectionsToDisplay = filter(collections, (c) => !isLocalCollection(c));
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
@@ -16,7 +19,7 @@ const SelectCollection = ({onClose, onSelect, title}) => {
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<ul className="mb-2" >
|
||||
{(collections && collections.length) ? collections.map((c) => (
|
||||
{(collectionsToDisplay && collectionsToDisplay.length) ? collectionsToDisplay.map((c) => (
|
||||
<div className="collection" key={c.uid} onClick={() => onSelect(c.uid)}>
|
||||
<IconFiles size={18} strokeWidth={1.5}/> <span className="ml-2">{c.name}</span>
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import filter from 'lodash/filter';
|
||||
import Collection from './Collection';
|
||||
import CreateOrAddCollection from './CreateOrAddCollection';
|
||||
import { findCollectionInWorkspace } from 'utils/workspaces';
|
||||
import { isLocalCollection } from 'utils/collections';
|
||||
|
||||
const Collections = ({searchText}) => {
|
||||
const { collections } = useSelector((state) => state.collections);
|
||||
@@ -15,7 +16,7 @@ const Collections = ({searchText}) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const collectionToDisplay = filter(collections, (c) => findCollectionInWorkspace(activeWorkspace, c.uid));
|
||||
const collectionToDisplay = filter(collections, (c) => findCollectionInWorkspace(activeWorkspace, c.uid) && !isLocalCollection(c));
|
||||
|
||||
if(!collectionToDisplay || !collectionToDisplay.length) {
|
||||
return <CreateOrAddCollection />;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
.current-workspace {
|
||||
margin-inline: .5rem;
|
||||
background: #e1e1e1;
|
||||
border-radius: 5px;
|
||||
|
||||
.caret {
|
||||
margin-left: 0.25rem;
|
||||
color: rgb(140, 140, 140);
|
||||
fill: rgb(140, 140, 140);
|
||||
}
|
||||
}
|
||||
|
||||
div[data-tippy-root] {
|
||||
width: calc(100% - 1rem);
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@@ -0,0 +1,73 @@
|
||||
import React, { useRef, forwardRef } from 'react';
|
||||
import filter from 'lodash/filter';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import { IconArrowForwardUp, IconCaretDown, IconFolders, IconPlus } from '@tabler/icons';
|
||||
import Collection from '../Collections/Collection';
|
||||
import { isLocalCollection } from 'utils/collections';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const LocalCollections = ({searchText}) => {
|
||||
const dropdownTippyRef = useRef();
|
||||
const { collections } = useSelector((state) => state.collections);
|
||||
|
||||
const collectionToDisplay = filter(collections, (c) => isLocalCollection(c));
|
||||
|
||||
if(!collectionToDisplay || !collectionToDisplay.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const Icon = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="current-workspace flex justify-between items-center pl-2 pr-2 py-1 select-none">
|
||||
<div className='flex items-center'>
|
||||
<span className='mr-2'>
|
||||
<IconFolders size={18} strokeWidth={1.5}/>
|
||||
</span>
|
||||
<span>
|
||||
Local Collections
|
||||
</span>
|
||||
</div>
|
||||
<IconCaretDown className="caret" size={14} strokeWidth={2}/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="items-center cursor-pointer mt-6 relative">
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
|
||||
<div className="dropdown-item" onClick={() => {}}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconPlus size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
<span>Create Collection</span>
|
||||
</div>
|
||||
<div className="dropdown-item" onClick={() => {}}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconArrowForwardUp size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
<span>Open Collection</span>
|
||||
</div>
|
||||
|
||||
<div className='px-2 pt-2 text-gray-600' style={{fontSize: 10, borderTop: 'solid 1px #e7e7e7'}}>
|
||||
Note: Local collections are not tied to a workspace
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-col">
|
||||
{collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => {
|
||||
return <Collection
|
||||
searchText={searchText}
|
||||
collection={c}
|
||||
key={c.uid}
|
||||
/>
|
||||
}) : null}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default LocalCollections;
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect} from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import Collections from './Collections';
|
||||
import LocalCollections from './LocalCollections';
|
||||
import TitleBar from './TitleBar';
|
||||
import MenuBar from './MenuBar';
|
||||
import { IconSearch, IconChevronsRight } from '@tabler/icons';
|
||||
@@ -94,6 +95,7 @@ const Sidebar = () => {
|
||||
</div>
|
||||
|
||||
<Collections searchText={searchText}/>
|
||||
<LocalCollections searchText={searchText}/>
|
||||
</div>
|
||||
<div className="flex px-1 py-2 items-center cursor-pointer text-gray-500 select-none">
|
||||
<div className="flex items-center ml-1 text-xs ">
|
||||
|
||||
@@ -3,7 +3,7 @@ import styled from 'styled-components';
|
||||
const Wrapper = styled.div`
|
||||
.current-workspace {
|
||||
margin-inline: .5rem;
|
||||
background: #fff;
|
||||
background: #e1e1e1;
|
||||
border-radius: 5px;
|
||||
|
||||
.caret {
|
||||
@@ -12,6 +12,10 @@ const Wrapper = styled.div`
|
||||
fill: rgb(140, 140, 140);
|
||||
}
|
||||
}
|
||||
|
||||
div[data-tippy-root] {
|
||||
width: calc(100% - 1rem);
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useRef, forwardRef, useState, useEffect } from 'react';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import { IconAdjustmentsHorizontal, IconCaretDown, IconBox } from '@tabler/icons';
|
||||
import { IconCaretDown, IconBox, IconSwitch3, IconSettings, IconFolders } from '@tabler/icons';
|
||||
import WorkspaceConfigurer from "../WorkspaceConfigurer";
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { selectWorkspace } from 'providers/ReduxStore/slices/workspaces';
|
||||
@@ -42,21 +42,25 @@ const WorkspaceSelector = () => {
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="items-center cursor-pointer">
|
||||
<div className="items-center cursor-pointer relative">
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
|
||||
{workspaces && workspaces.length && workspaces.map((workspace) => (
|
||||
{/* {workspaces && workspaces.length && workspaces.map((workspace) => (
|
||||
<div className="dropdown-item" onClick={() => handleSelectWorkspace(workspace)} key={workspace.uid}>
|
||||
<span>{workspace.name}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="dropdown-item" style={{borderTop: 'solid 1px #e7e7e7'}} onClick={() => {
|
||||
setOpenWorkspacesModal(true);
|
||||
}}>
|
||||
))} */}
|
||||
<div className="dropdown-item" onClick={() => handleSelectWorkspace(workspace)}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconAdjustmentsHorizontal size={18} strokeWidth={1.5}/>
|
||||
<IconSwitch3 size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
<span>Configure</span>
|
||||
<span>Switch Workspace</span>
|
||||
</div>
|
||||
|
||||
<div className="dropdown-item" onClick={() => handleSelectWorkspace(workspace)}>
|
||||
<div className="pr-2 text-gray-600">
|
||||
<IconSettings size={18} strokeWidth={1.5}/>
|
||||
</div>
|
||||
<span>Configure Workspaces</span>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user