mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-09 06:55:03 +00:00
* refactor: remove size prop from Button components for consistency across modals and improve styling * style: update confirm button colors in modal components for consistency
179 lines
5.6 KiB
JavaScript
179 lines
5.6 KiB
JavaScript
import React, { useEffect, useMemo } from 'react';
|
|
import each from 'lodash/each';
|
|
import filter from 'lodash/filter';
|
|
import groupBy from 'lodash/groupBy';
|
|
import { useSelector } from 'react-redux';
|
|
import { useDispatch } from 'react-redux';
|
|
import { findCollectionByUid, flattenItems, isItemARequest, hasRequestChanges } from 'utils/collections';
|
|
import { pluralizeWord } from 'utils/common';
|
|
import { completeQuitFlow } from 'providers/ReduxStore/slices/app';
|
|
import { saveMultipleRequests, saveMultipleCollections, saveMultipleFolders } from 'providers/ReduxStore/slices/collections/actions';
|
|
import { IconAlertTriangle } from '@tabler/icons';
|
|
import Modal from 'components/Modal';
|
|
import Button from 'ui/Button';
|
|
|
|
const SaveRequestsModal = ({ onClose }) => {
|
|
const MAX_UNSAVED_ITEMS_TO_SHOW = 5;
|
|
const collections = useSelector((state) => state.collections.collections);
|
|
const tabs = useSelector((state) => state.tabs.tabs);
|
|
const dispatch = useDispatch();
|
|
|
|
const allDrafts = useMemo(() => {
|
|
const requestDrafts = [];
|
|
const collectionDrafts = [];
|
|
const folderDrafts = [];
|
|
const tabsByCollection = groupBy(tabs, (t) => t.collectionUid);
|
|
|
|
Object.keys(tabsByCollection).forEach((collectionUid) => {
|
|
const collection = findCollectionByUid(collections, collectionUid);
|
|
if (collection) {
|
|
// Check for collection draft
|
|
if (collection.draft) {
|
|
collectionDrafts.push({
|
|
type: 'collection',
|
|
name: collection.name,
|
|
collectionUid: collectionUid
|
|
});
|
|
}
|
|
|
|
// Check for request and folder drafts
|
|
const items = flattenItems(collection.items);
|
|
|
|
// Request drafts
|
|
const requests = filter(items, (item) => isItemARequest(item) && hasRequestChanges(item));
|
|
each(requests, (draft) => {
|
|
requestDrafts.push({
|
|
type: 'request',
|
|
...draft,
|
|
collectionUid: collectionUid
|
|
});
|
|
});
|
|
|
|
// Folder drafts
|
|
const folders = filter(items, (item) => item.type === 'folder' && item.draft);
|
|
each(folders, (folder) => {
|
|
folderDrafts.push({
|
|
type: 'folder',
|
|
name: folder.name,
|
|
folderUid: folder.uid,
|
|
collectionUid: collectionUid
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
return [...collectionDrafts, ...folderDrafts, ...requestDrafts];
|
|
}, [collections, tabs]);
|
|
|
|
const totalDraftsCount = allDrafts.length;
|
|
|
|
useEffect(() => {
|
|
if (totalDraftsCount === 0) {
|
|
return dispatch(completeQuitFlow());
|
|
}
|
|
}, [totalDraftsCount, dispatch]);
|
|
|
|
const closeWithoutSave = () => {
|
|
dispatch(completeQuitFlow());
|
|
onClose();
|
|
};
|
|
|
|
const closeWithSave = async () => {
|
|
try {
|
|
// Separate drafts by type
|
|
const collectionDrafts = allDrafts.filter((d) => d.type === 'collection');
|
|
const folderDrafts = allDrafts.filter((d) => d.type === 'folder');
|
|
const requestDrafts = allDrafts.filter((d) => d.type === 'request');
|
|
|
|
// Save all collection drafts
|
|
if (collectionDrafts.length > 0) {
|
|
await dispatch(saveMultipleCollections(collectionDrafts));
|
|
}
|
|
|
|
// Save all folder drafts
|
|
if (folderDrafts.length > 0) {
|
|
await dispatch(saveMultipleFolders(folderDrafts));
|
|
}
|
|
|
|
// Save all request drafts
|
|
if (requestDrafts.length > 0) {
|
|
await dispatch(saveMultipleRequests(requestDrafts));
|
|
}
|
|
|
|
dispatch(completeQuitFlow());
|
|
onClose();
|
|
} catch (error) {
|
|
console.error('Error saving drafts:', error);
|
|
}
|
|
};
|
|
|
|
if (totalDraftsCount === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<Modal
|
|
size="md"
|
|
title="Unsaved changes"
|
|
confirmText="Save and Close"
|
|
cancelText="Close without saving"
|
|
handleCancel={onClose}
|
|
disableEscapeKey={true}
|
|
disableCloseOnOutsideClick={true}
|
|
closeModalFadeTimeout={150}
|
|
hideFooter={true}
|
|
>
|
|
<div className="flex items-center">
|
|
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
|
|
<h1 className="ml-2 text-lg font-medium">Hold on..</h1>
|
|
</div>
|
|
<p className="mt-4">
|
|
Do you want to save the changes you made to the following{' '}
|
|
<span className="font-medium">{totalDraftsCount}</span> {pluralizeWord('item', totalDraftsCount)}?
|
|
</p>
|
|
|
|
<ul className="mt-4">
|
|
{allDrafts.slice(0, MAX_UNSAVED_ITEMS_TO_SHOW).map((item, index) => {
|
|
const prefix
|
|
= item.type === 'collection'
|
|
? 'Collection: '
|
|
: item.type === 'folder'
|
|
? 'Folder: '
|
|
: 'Request: ';
|
|
return (
|
|
<li key={`${item.type}-${item.collectionUid || item.uid}-${index}`} className="mt-1 text-xs">
|
|
{prefix}
|
|
{item.name || item.filename}
|
|
</li>
|
|
);
|
|
})}
|
|
</ul>
|
|
|
|
{totalDraftsCount > MAX_UNSAVED_ITEMS_TO_SHOW && (
|
|
<p className="mt-1 text-xs">
|
|
...{totalDraftsCount - MAX_UNSAVED_ITEMS_TO_SHOW} additional{' '}
|
|
{pluralizeWord('item', totalDraftsCount - MAX_UNSAVED_ITEMS_TO_SHOW)} not shown
|
|
</p>
|
|
)}
|
|
|
|
<div className="flex justify-between mt-6">
|
|
<div>
|
|
<Button color="danger" onClick={closeWithoutSave}>
|
|
Don't Save
|
|
</Button>
|
|
</div>
|
|
<div>
|
|
<Button className="mr-2" color="secondary" variant="ghost" onClick={onClose}>
|
|
Cancel
|
|
</Button>
|
|
<Button onClick={closeWithSave}>
|
|
{totalDraftsCount > 1 ? 'Save All' : 'Save'}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
);
|
|
};
|
|
|
|
export default SaveRequestsModal;
|