forked from extern/bruno
feat: delete collection item
This commit is contained in:
parent
7fc624d1a8
commit
4ded884501
@ -38,7 +38,7 @@ const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ConfirmModal = ({
|
const Modal = ({
|
||||||
size,
|
size,
|
||||||
title,
|
title,
|
||||||
confirmText,
|
confirmText,
|
||||||
@ -91,4 +91,4 @@ const ConfirmModal = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ConfirmModal;
|
export default Modal;
|
||||||
|
@ -83,6 +83,13 @@ const RequestTabPanel = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const item = findItemInCollection(collection, activeTabUid);
|
const item = findItemInCollection(collection, activeTabUid);
|
||||||
|
if(!item || !item.uid) {
|
||||||
|
return (
|
||||||
|
<StyledWrapper>
|
||||||
|
Request not found!
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const onUrlChange = (value) => {
|
const onUrlChange = (value) => {
|
||||||
dispatch(requestChanged({
|
dispatch(requestChanged({
|
||||||
|
@ -47,7 +47,9 @@ const RequestTabs = () => {
|
|||||||
const handleCloseClick = (event, tab) => {
|
const handleCloseClick = (event, tab) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
dispatch(closeTab(tab.uid))
|
dispatch(closeTab({
|
||||||
|
tabUid: tab.uid
|
||||||
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
const createNewTab = () => {
|
const createNewTab = () => {
|
||||||
@ -71,14 +73,22 @@ const RequestTabs = () => {
|
|||||||
const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab.collectionUid);
|
const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab.collectionUid);
|
||||||
const item = findItemInCollection(activeCollection, activeTab.uid);
|
const item = findItemInCollection(activeCollection, activeTab.uid);
|
||||||
|
|
||||||
|
if(!item || !item.uid) {
|
||||||
|
return (
|
||||||
|
<StyledWrapper>
|
||||||
|
Request not found!
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const getRequestName = (tab) => {
|
const getRequestName = (tab) => {
|
||||||
const item = findItemInCollection(activeCollection, tab.uid);
|
const item = findItemInCollection(activeCollection, tab.uid);
|
||||||
return item.name;
|
return item ? item.name : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRequestMethod = (tab) => {
|
const getRequestMethod = (tab) => {
|
||||||
const item = findItemInCollection(activeCollection, tab.uid);
|
const item = findItemInCollection(activeCollection, tab.uid);
|
||||||
return item.request.name;
|
return item ? item.request.method : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
button.submit {
|
||||||
|
color: white;
|
||||||
|
background-color: var(--color-background-danger) !important;
|
||||||
|
border: inherit !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: inherit !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Modal from 'components/Modal';
|
||||||
|
import { isItemAFolder } from 'utils/tabs';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { closeTab } from 'providers/ReduxStore/slices/tabs';
|
||||||
|
import { deleteItem } from 'providers/ReduxStore/slices/collections';
|
||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
|
const DeleteCollectionItem = ({onClose, item, collection}) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const isFolder = isItemAFolder(item);
|
||||||
|
const onConfirm = () =>{
|
||||||
|
dispatch(closeTab({
|
||||||
|
tabUid: item.uid
|
||||||
|
}));
|
||||||
|
dispatch(deleteItem(item.uid, collection.uid));
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper>
|
||||||
|
<Modal
|
||||||
|
size="sm"
|
||||||
|
title={`Delete ${isFolder ? 'Folder' : 'Request'}`}
|
||||||
|
confirmText="Delete"
|
||||||
|
handleConfirm={onConfirm}
|
||||||
|
handleCancel={onClose}
|
||||||
|
>
|
||||||
|
Are you sure you want to delete <span className="font-semibold">{item.name}</span> ?
|
||||||
|
</Modal>
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteCollectionItem;
|
@ -47,6 +47,14 @@ const Wrapper = styled.div`
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: -0.625rem;
|
top: -0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.dropdown-item.delete-item {
|
||||||
|
color: var(--color-text-danger);
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-background-danger);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useRef, forwardRef } from 'react';
|
import React, { useState, useRef, forwardRef } from 'react';
|
||||||
import range from 'lodash/range';
|
import range from 'lodash/range';
|
||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import { IconChevronRight, IconDots } from '@tabler/icons';
|
import { IconChevronRight, IconDots } from '@tabler/icons';
|
||||||
@ -8,6 +8,7 @@ import { addTab, focusTab } from 'providers/ReduxStore/slices/tabs';
|
|||||||
import { isItemARequest, isItemAFolder, itemIsOpenedInTabs } from 'utils/tabs';
|
import { isItemARequest, isItemAFolder, itemIsOpenedInTabs } from 'utils/tabs';
|
||||||
import Dropdown from 'components/Dropdown';
|
import Dropdown from 'components/Dropdown';
|
||||||
import RequestMethod from './RequestMethod';
|
import RequestMethod from './RequestMethod';
|
||||||
|
import DeleteCollectionItem from './DeleteCollectionItem';
|
||||||
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
|
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
const [deleteItemModalOpen, setDeleteItemModalOpen] = useState(false);
|
||||||
|
|
||||||
const dropdownTippyRef = useRef();
|
const dropdownTippyRef = useRef();
|
||||||
const MenuIcon = forwardRef((props, ref) => {
|
const MenuIcon = forwardRef((props, ref) => {
|
||||||
return (
|
return (
|
||||||
@ -62,6 +65,7 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className="flex flex-col">
|
<StyledWrapper className="flex flex-col">
|
||||||
|
{deleteItemModalOpen && <DeleteCollectionItem item={item} collection={collection} onClose={() => setDeleteItemModalOpen(false)}/>}
|
||||||
<div
|
<div
|
||||||
className={itemRowClassName}
|
className={itemRowClassName}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
@ -119,8 +123,9 @@ const CollectionItem = ({item, collection}) => {
|
|||||||
}}>
|
}}>
|
||||||
Rename
|
Rename
|
||||||
</div>
|
</div>
|
||||||
<div className="dropdown-item" onClick={(e) => {
|
<div className="dropdown-item delete-item" onClick={(e) => {
|
||||||
dropdownTippyRef.current.hide();
|
dropdownTippyRef.current.hide();
|
||||||
|
setDeleteItemModalOpen(true);
|
||||||
}}>
|
}}>
|
||||||
Delete
|
Delete
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useRef, useEffect } from 'react';
|
import React, { useRef, useEffect } from 'react';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import Modal from '../../Modal';
|
import Modal from 'components/Modal';
|
||||||
|
|
||||||
const CreateCollection = ({handleConfirm, handleCancel}) => {
|
const CreateCollection = ({handleConfirm, handleCancel}) => {
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
|
@ -4,7 +4,14 @@ import cloneDeep from 'lodash/cloneDeep';
|
|||||||
import { createSlice } from '@reduxjs/toolkit'
|
import { createSlice } from '@reduxjs/toolkit'
|
||||||
import { getCollectionsFromIdb, saveCollectionToIdb } from 'utils/idb';
|
import { getCollectionsFromIdb, saveCollectionToIdb } from 'utils/idb';
|
||||||
import { sendNetworkRequest } from 'utils/network';
|
import { sendNetworkRequest } from 'utils/network';
|
||||||
import { findCollectionByUid, findItemInCollection, cloneItem, transformCollectionToSaveToIdb, addDepth } from 'utils/collections';
|
import {
|
||||||
|
findCollectionByUid,
|
||||||
|
findItemInCollection,
|
||||||
|
cloneItem,
|
||||||
|
transformCollectionToSaveToIdb,
|
||||||
|
addDepth,
|
||||||
|
deleteItemInCollection
|
||||||
|
} from 'utils/collections';
|
||||||
|
|
||||||
// todo: errors should be tracked in each slice and displayed as toasts
|
// todo: errors should be tracked in each slice and displayed as toasts
|
||||||
|
|
||||||
@ -78,6 +85,13 @@ export const collectionsSlice = createSlice({
|
|||||||
addDepth(collection.items);
|
addDepth(collection.items);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
_deleteItem: (state, action) => {
|
||||||
|
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||||
|
|
||||||
|
if(collection) {
|
||||||
|
deleteItemInCollection(action.payload.itemUid, collection);
|
||||||
|
}
|
||||||
|
},
|
||||||
collectionClicked: (state, action) => {
|
collectionClicked: (state, action) => {
|
||||||
const collection = findCollectionByUid(state.collections, action.payload);
|
const collection = findCollectionByUid(state.collections, action.payload);
|
||||||
|
|
||||||
@ -110,6 +124,7 @@ export const {
|
|||||||
_saveRequest,
|
_saveRequest,
|
||||||
_newFolder,
|
_newFolder,
|
||||||
_newRequest,
|
_newRequest,
|
||||||
|
_deleteItem,
|
||||||
collectionClicked,
|
collectionClicked,
|
||||||
requestUrlChanged,
|
requestUrlChanged,
|
||||||
} = collectionsSlice.actions;
|
} = collectionsSlice.actions;
|
||||||
@ -233,4 +248,24 @@ export const newHttpRequest = (requestName, collectionUid) => (dispatch, getStat
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const deleteItem = (itemUid, collectionUid) => (dispatch, getState) => {
|
||||||
|
const state = getState();
|
||||||
|
const collection = findCollectionByUid(state.collections.collections, collectionUid);
|
||||||
|
|
||||||
|
if(collection) {
|
||||||
|
const collectionCopy = cloneDeep(collection);
|
||||||
|
deleteItemInCollection(itemUid, collectionCopy);
|
||||||
|
const collectionToSave = transformCollectionToSaveToIdb(collectionCopy);
|
||||||
|
|
||||||
|
saveCollectionToIdb(window.__idb, collectionToSave)
|
||||||
|
.then(() => {
|
||||||
|
dispatch(_deleteItem({
|
||||||
|
itemUid: itemUid,
|
||||||
|
collectionUid: collectionUid
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
.catch((err) => console.log(err));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default collectionsSlice.reducer;
|
export default collectionsSlice.reducer;
|
||||||
|
@ -26,7 +26,7 @@ export const tabsSlice = createSlice({
|
|||||||
state.activeTabUid = action.payload.uid;
|
state.activeTabUid = action.payload.uid;
|
||||||
},
|
},
|
||||||
closeTab: (state, action) => {
|
closeTab: (state, action) => {
|
||||||
state.tabs = filter(state.tabs, (t) => t.uid !== action.payload);
|
state.tabs = filter(state.tabs, (t) => t.uid !== action.payload.tabUid);
|
||||||
|
|
||||||
if(state.tabs && state.tabs.length) {
|
if(state.tabs && state.tabs.length) {
|
||||||
// todo: closing tab needs to focus on the right adjacent tab
|
// todo: closing tab needs to focus on the right adjacent tab
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
--color-codemirror-border: #efefef;
|
--color-codemirror-border: #efefef;
|
||||||
--color-codemirror-background: rgb(243, 243, 243);
|
--color-codemirror-background: rgb(243, 243, 243);
|
||||||
--color-text-link: #1663bb;
|
--color-text-link: #1663bb;
|
||||||
|
--color-text-danger: rgb(185, 28, 28);
|
||||||
|
--color-background-danger: #dc3545;
|
||||||
--color-method-get: rgb(5, 150, 105);
|
--color-method-get: rgb(5, 150, 105);
|
||||||
--color-method-post: #8e44ad;
|
--color-method-post: #8e44ad;
|
||||||
--color-method-put: #8e44ad;
|
--color-method-put: #8e44ad;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import each from 'lodash/each';
|
import each from 'lodash/each';
|
||||||
import find from 'lodash/find';
|
import find from 'lodash/find';
|
||||||
|
import filter from 'lodash/filter';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
|
||||||
export const addDepth = (items = []) => {
|
export const addDepth = (items = []) => {
|
||||||
@ -105,3 +106,16 @@ export const transformCollectionToSaveToIdb = (collection) => {
|
|||||||
|
|
||||||
return collectionToSave;
|
return collectionToSave;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// todo: optimize this
|
||||||
|
export const deleteItemInCollection = (itemUid, collection) => {
|
||||||
|
collection.items = filter(collection.items, (i) => i.uid !== itemUid);
|
||||||
|
|
||||||
|
let flattenedItems = flattenItems(collection.items);
|
||||||
|
|
||||||
|
each(flattenedItems, (i) => {
|
||||||
|
if(i.items && i.items.length) {
|
||||||
|
i.items = filter(i.items, (i) => i.uid !== itemUid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user