diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/RenameCollection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/RenameCollection/index.js new file mode 100644 index 00000000..aa752125 --- /dev/null +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/RenameCollection/index.js @@ -0,0 +1,64 @@ +import React, { useRef, useEffect } from 'react'; +import { useFormik } from 'formik'; +import * as Yup from 'yup'; +import Modal from 'components/Modal'; +import { useDispatch } from 'react-redux'; +import { renameCollection } from 'providers/ReduxStore/slices/collections/actions'; + +const RenameCollection = ({collection, onClose}) => { + const dispatch = useDispatch(); + const inputRef = useRef(); + const formik = useFormik({ + enableReinitialize: true, + initialValues: { + name: collection.name + }, + validationSchema: Yup.object({ + name: Yup.string() + .min(1, 'must be atleast 1 characters') + .max(50, 'must be 50 characters or less') + .required('name is required') + }), + onSubmit: (values) => { + dispatch(renameCollection(values.name, collection.uid)); + onClose(); + } + }); + + useEffect(() => { + if(inputRef && inputRef.current) { + inputRef.current.focus(); + } + }, [inputRef]); + + const onSubmit = () => formik.handleSubmit(); + + return ( + +
+
+ + + {formik.touched.name && formik.errors.name ? ( +
{formik.errors.name}
+ ) : null} +
+
+
+ ); +}; + +export default RenameCollection; diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js index b549b086..7f617355 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/index.js @@ -11,11 +11,13 @@ import CollectionItem from './CollectionItem'; import { doesCollectionHaveItemsMatchingSearchText } from 'utils/collections/search'; import { isItemAFolder, isItemARequest } from 'utils/collections'; +import RenameCollection from './RenameCollection'; import StyledWrapper from './StyledWrapper'; const Collection = ({collection, searchText}) => { const [showNewFolderModal, setShowNewFolderModal] = useState(false); const [showNewRequestModal, setShowNewRequestModal] = useState(false); + const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false); const [collectionIsCollapsed, setCollectionIsCollapsed] = useState(collection.collapsed); const dispatch = useDispatch(); @@ -58,6 +60,7 @@ const Collection = ({collection, searchText}) => { {showNewRequestModal && setShowNewRequestModal(false)}/>} {showNewFolderModal && setShowNewFolderModal(false)}/>} + {showRenameCollectionModal && setShowRenameCollectionModal(false)}/>}
@@ -71,16 +74,23 @@ const Collection = ({collection, searchText}) => { >
{ menuDropdownTippyRef.current.hide(); - setShowNewRequestModal(true) + setShowNewRequestModal(true); }}> New Request
{ menuDropdownTippyRef.current.hide(); - setShowNewFolderModal(true) + setShowNewFolderModal(true); }}> New Folder
+
{ + dispatch(removeCollection(collection.uid)); + menuDropdownTippyRef.current.hide(); + setShowRenameCollectionModal(true); + }}> + Rename +
{ dispatch(removeCollection(collection.uid)); menuDropdownTippyRef.current.hide(); diff --git a/packages/bruno-app/src/pageComponents/Collections/CollectionItem/index.js b/packages/bruno-app/src/pageComponents/Collections/CollectionItem/index.js new file mode 100644 index 00000000..906a4e2f --- /dev/null +++ b/packages/bruno-app/src/pageComponents/Collections/CollectionItem/index.js @@ -0,0 +1,20 @@ +import React, { useState } from 'react'; +import { IconEdit, IconTrash } from "@tabler/icons"; +import RenameCollection from 'components/Sidebar/Collections/Collection/RenameCollection'; + +export default function CollectionItem({collection}) { + const [showRenameCollectionModal, setShowRenameCollectionModal] = useState(false); + + return ( + <> + {showRenameCollectionModal && setShowRenameCollectionModal(false)}/>} +
+
  • {collection.name}
  • +
    + setShowRenameCollectionModal(true)}/> + +
    +
    + + ); +}; diff --git a/packages/bruno-app/src/pageComponents/Collections/index.js b/packages/bruno-app/src/pageComponents/Collections/index.js index a3ca1e19..84f2558a 100644 --- a/packages/bruno-app/src/pageComponents/Collections/index.js +++ b/packages/bruno-app/src/pageComponents/Collections/index.js @@ -1,9 +1,8 @@ import React from 'react'; -import { IconEdit, IconTrash } from "@tabler/icons"; import { useSelector } from 'react-redux'; +import CollectionItem from './CollectionItem'; import StyledWrapper from './StyledWrapper'; - export default function Collections() { const collections = useSelector((state) => state.collections.collections); @@ -12,15 +11,9 @@ export default function Collections() {

    Collections

    - {collections && collections.length ? collections.map((collection) => -
    -
  • {collection.name}
  • -
    - - -
    -
    - ): null} + {collections && collections.length ? collections.map((collection) => { + return ; + }): null}
    ); diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js new file mode 100644 index 00000000..6c1f88e3 --- /dev/null +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -0,0 +1,33 @@ +import cloneDeep from 'lodash/cloneDeep'; +import { + findCollectionByUid, + findItemInCollection, + transformCollectionToSaveToIdb +} from 'utils/collections'; +import { saveCollectionToIdb } from 'utils/idb'; + +import { + _renameCollection +} from './index'; + +export const renameCollection = (newName, collectionUid) => (dispatch, getState) => { + const state = getState(); + const collection = findCollectionByUid(state.collections.collections, collectionUid); + + if(collection) { + const collectionCopy = cloneDeep(collection); + collectionCopy.name = newName; + const collectionToSave = transformCollectionToSaveToIdb(collectionCopy, { + ignoreDraft: true + }); + + saveCollectionToIdb(window.__idb, collectionToSave) + .then(() => { + dispatch(_renameCollection({ + newName: newName, + collectionUid: collectionUid + })); + }) + .catch((err) => console.log(err)); + } +}; \ No newline at end of file diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js similarity index 99% rename from packages/bruno-app/src/providers/ReduxStore/slices/collections.js rename to packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index fa52ad19..f8db4bd5 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -28,8 +28,6 @@ const initialState = { collections: [] }; -const PATH_SEPARATOR = path.sep; - export const collectionsSlice = createSlice({ name: 'collections', initialState, @@ -42,6 +40,13 @@ export const collectionsSlice = createSlice({ _createCollection: (state, action) => { state.collections.push(action.payload); }, + _renameCollection: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + + if(collection) { + collection.name = action.payload.newName; + } + }, _newItem: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -521,6 +526,7 @@ export const collectionsSlice = createSlice({ export const { _createCollection, + _renameCollection, _loadCollections, _newItem, _deleteItem, diff --git a/packages/bruno-app/src/utils/collections/index.js b/packages/bruno-app/src/utils/collections/index.js index c17d8b6f..e6843221 100644 --- a/packages/bruno-app/src/utils/collections/index.js +++ b/packages/bruno-app/src/utils/collections/index.js @@ -162,7 +162,10 @@ export const transformCollectionToSaveToIdb = (collection, options = {}) => { di.name = si.name; // if items is draft, then take data from draft to save - if(!options.ignoreDraft && si.draft) { + // The condition "!options.ignoreDraft" may appear confusing + // When saving a collection, this option allows the caller to specify to ignore any draft changes while still saving rest of the collection. + // This is useful for performing rename request/collections while still leaving changes in draft not making its way into the indexeddb + if(si.draft && !options.ignoreDraft) { if(si.draft.request) { di.request = { url: si.draft.request.url,