Skip to content

Commit 2be602d

Browse files
Feature/prompt save before collection close (usebruno#6062)
* added confirmation dialog before collection close for items in draft state * chore: lint fix --------- Co-authored-by: Sid <siddharth@usebruno.com>
1 parent 8ec1925 commit 2be602d

File tree

2 files changed

+139
-2
lines changed

2 files changed

+139
-2
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import React from 'react';
2+
import filter from 'lodash/filter';
3+
import { useDispatch } from 'react-redux';
4+
import { flattenItems, isItemARequest, hasRequestChanges } from 'utils/collections';
5+
import { pluralizeWord } from 'utils/common';
6+
import { saveMultipleRequests } from 'providers/ReduxStore/slices/collections/actions';
7+
import { deleteRequestDraft } from 'providers/ReduxStore/slices/collections';
8+
import { removeCollection } from 'providers/ReduxStore/slices/collections/actions';
9+
import { IconAlertTriangle } from '@tabler/icons';
10+
import Modal from 'components/Modal';
11+
import toast from 'react-hot-toast';
12+
13+
const ConfirmCollectionCloseDrafts = ({ onClose, collection, collectionUid }) => {
14+
const MAX_UNSAVED_REQUESTS_TO_SHOW = 5;
15+
const dispatch = useDispatch();
16+
17+
// Get all draft items in the collection
18+
const currentDrafts = React.useMemo(() => {
19+
if (!collection) return [];
20+
const items = flattenItems(collection.items);
21+
const collectionDrafts = filter(items, (item) => isItemARequest(item) && hasRequestChanges(item));
22+
return collectionDrafts.map((draft) => ({
23+
...draft,
24+
collectionUid: collectionUid
25+
}));
26+
}, [collection, collectionUid]);
27+
28+
const handleSaveAll = () => {
29+
dispatch(saveMultipleRequests(currentDrafts))
30+
.then(() => {
31+
dispatch(removeCollection(collectionUid))
32+
.then(() => {
33+
toast.success('Collection closed');
34+
onClose();
35+
})
36+
.catch(() => toast.error('An error occurred while closing the collection'));
37+
})
38+
.catch(() => {
39+
toast.error('Failed to save requests!');
40+
});
41+
};
42+
43+
const handleDiscardAll = () => {
44+
// Discard all drafts
45+
currentDrafts.forEach((draft) => {
46+
dispatch(deleteRequestDraft({
47+
collectionUid: collectionUid,
48+
itemUid: draft.uid
49+
}));
50+
});
51+
52+
// Then close the collection
53+
dispatch(removeCollection(collectionUid))
54+
.then(() => {
55+
toast.success('Collection closed');
56+
onClose();
57+
})
58+
.catch(() => toast.error('An error occurred while closing the collection'));
59+
};
60+
61+
if (!currentDrafts.length) {
62+
return null;
63+
}
64+
65+
return (
66+
<Modal
67+
size="md"
68+
title="Close Collection"
69+
confirmText="Save and Close"
70+
cancelText="Close without saving"
71+
handleCancel={onClose}
72+
disableEscapeKey={true}
73+
disableCloseOnOutsideClick={true}
74+
closeModalFadeTimeout={150}
75+
hideFooter={true}
76+
>
77+
<div className="flex items-center">
78+
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
79+
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
80+
</div>
81+
<p className="mt-4">
82+
Do you want to save the changes you made to the following{' '}
83+
<span className="font-medium">{currentDrafts.length}</span> {pluralizeWord('request', currentDrafts.length)}?
84+
</p>
85+
86+
<ul className="mt-4">
87+
{currentDrafts.slice(0, MAX_UNSAVED_REQUESTS_TO_SHOW).map((item) => {
88+
return (
89+
<li key={item.uid} className="mt-1 text-xs">
90+
{item.filename}
91+
</li>
92+
);
93+
})}
94+
</ul>
95+
96+
{currentDrafts.length > MAX_UNSAVED_REQUESTS_TO_SHOW && (
97+
<p className="mt-1 text-xs">
98+
...{currentDrafts.length - MAX_UNSAVED_REQUESTS_TO_SHOW} additional{' '}
99+
{pluralizeWord('request', currentDrafts.length - MAX_UNSAVED_REQUESTS_TO_SHOW)} not shown
100+
</p>
101+
)}
102+
103+
<div className="flex justify-between mt-6">
104+
<div>
105+
<button className="btn btn-sm btn-danger" onClick={handleDiscardAll}>
106+
Discard and Close
107+
</button>
108+
</div>
109+
<div>
110+
<button className="btn btn-close btn-sm mr-2" onClick={onClose}>
111+
Cancel
112+
</button>
113+
<button className="btn btn-secondary btn-sm" onClick={handleSaveAll}>
114+
{currentDrafts.length > 1 ? 'Save All and Close' : 'Save and Close'}
115+
</button>
116+
</div>
117+
</div>
118+
</Modal>
119+
);
120+
};
121+
122+
export default ConfirmCollectionCloseDrafts;

packages/bruno-app/src/components/Sidebar/Collections/Collection/RemoveCollection/index.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
1-
import React from 'react';
1+
import React, { useMemo } from 'react';
22
import toast from 'react-hot-toast';
33
import Modal from 'components/Modal';
44
import { useDispatch, useSelector } from 'react-redux';
55
import { IconFiles } from '@tabler/icons';
66
import { removeCollection } from 'providers/ReduxStore/slices/collections/actions';
7-
import { findCollectionByUid } from 'utils/collections/index';
7+
import { findCollectionByUid, flattenItems, isItemARequest, hasRequestChanges } from 'utils/collections/index';
8+
import filter from 'lodash/filter';
9+
import ConfirmCollectionCloseDrafts from './ConfirmCollectionCloseDrafts';
810

911
const RemoveCollection = ({ onClose, collectionUid }) => {
1012
const dispatch = useDispatch();
1113
const collection = useSelector(state => findCollectionByUid(state.collections.collections, collectionUid));
1214

15+
// Detect drafts in the collection
16+
const drafts = useMemo(() => {
17+
if (!collection) return [];
18+
const items = flattenItems(collection.items);
19+
return filter(items, (item) => isItemARequest(item) && hasRequestChanges(item));
20+
}, [collection]);
21+
1322
const onConfirm = () => {
1423
dispatch(removeCollection(collection.uid))
1524
.then(() => {
@@ -19,6 +28,12 @@ const RemoveCollection = ({ onClose, collectionUid }) => {
1928
.catch(() => toast.error('An error occurred while closing the collection'));
2029
};
2130

31+
// If there are drafts, show the draft confirmation modal
32+
if (drafts.length > 0) {
33+
return <ConfirmCollectionCloseDrafts onClose={onClose} collection={collection} collectionUid={collectionUid} />;
34+
}
35+
36+
// Otherwise, show the standard close confirmation modal
2237
return (
2338
<Modal size="sm" title="Close Collection" confirmText="Close" handleConfirm={onConfirm} handleCancel={onClose}>
2439
<div className="flex items-center">

0 commit comments

Comments
 (0)