Skip to content

Commit 55e62aa

Browse files
committed
ability to clone flow
1 parent 057e354 commit 55e62aa

10 files changed

Lines changed: 131 additions & 71 deletions

File tree

packages/flowtest-electron/preload.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ contextBridge.exposeInMainWorld('ipcRenderer', {
66
on: (channel, handler) => ipcRenderer.on(channel, (event, ...args) => handler(...args)),
77
join: (...args) => path.join(...args),
88
relative: (...args) => path.relative(...args),
9+
dirname: (...args) => path.dirname(...args),
910
});

packages/flowtest-electron/src/ipc/collection.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,16 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
237237
}
238238
});
239239

240+
ipcMain.handle('renderer:clone-flowtest', async (event, name, flowtestPath) => {
241+
try {
242+
const content = readFile(flowtestPath);
243+
createFile(`${name}.flow`, path.dirname(flowtestPath), content);
244+
console.log(`Cloned file: ${name}.flow`);
245+
} catch (error) {
246+
return Promise.reject(error);
247+
}
248+
});
249+
240250
ipcMain.handle('renderer:read-flowtest', async (event, pathname, collectionId) => {
241251
try {
242252
const content = readFile(pathname);

src/components/atoms/sidebar/collections/OptionsMenu.js

Lines changed: 57 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,44 +36,67 @@ const OptionsMenu = ({ collectionId, directory, itemType }) => {
3636
data-click-from='options-menu'
3737
data-item-type={itemType}
3838
>
39-
<div className='px-1 py-1' data-click-from='options-menu' data-item-type={itemType}>
40-
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
41-
<button
42-
className={menuItemsStyles}
43-
data-click-from='options-menu'
44-
data-options-menu-item={DirectoryOptionsActions.addNewFolder.value}
45-
data-path-name={directory.pathname}
46-
data-item-type={itemType}
47-
data-collection-id={collectionId}
48-
>
49-
<FolderPlusIcon
50-
className='w-4 h-4 mr-2'
51-
aria-hidden='true'
39+
{itemType === OBJ_TYPES.collection || itemType === OBJ_TYPES.folder ? (
40+
<div className='px-1 py-1' data-click-from='options-menu' data-item-type={itemType}>
41+
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
42+
<button
43+
className={menuItemsStyles}
5244
data-click-from='options-menu'
45+
data-options-menu-item={DirectoryOptionsActions.addNewFolder.value}
46+
data-path-name={directory.pathname}
5347
data-item-type={itemType}
54-
/>
55-
{DirectoryOptionsActions.addNewFolder.displayValue}
56-
</button>
57-
</Menu.Item>
58-
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
59-
<button
60-
className={menuItemsStyles}
61-
data-click-from='options-menu'
62-
data-options-menu-item={DirectoryOptionsActions.addNewFlow.value}
63-
data-path-name={directory.pathname}
64-
data-item-type={itemType}
65-
data-collection-id={collectionId}
66-
>
67-
<PencilSquareIcon
68-
className='w-4 h-4 mr-2'
69-
aria-hidden='true'
48+
data-collection-id={collectionId}
49+
>
50+
<FolderPlusIcon
51+
className='w-4 h-4 mr-2'
52+
aria-hidden='true'
53+
data-click-from='options-menu'
54+
data-item-type={itemType}
55+
/>
56+
{DirectoryOptionsActions.addNewFolder.displayValue}
57+
</button>
58+
</Menu.Item>
59+
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
60+
<button
61+
className={menuItemsStyles}
7062
data-click-from='options-menu'
63+
data-options-menu-item={DirectoryOptionsActions.addNewFlow.value}
64+
data-path-name={directory.pathname}
7165
data-item-type={itemType}
72-
/>
73-
{DirectoryOptionsActions.addNewFlow.displayValue}
74-
</button>
75-
</Menu.Item>
76-
</div>
66+
data-collection-id={collectionId}
67+
>
68+
<PencilSquareIcon
69+
className='w-4 h-4 mr-2'
70+
aria-hidden='true'
71+
data-click-from='options-menu'
72+
data-item-type={itemType}
73+
/>
74+
{DirectoryOptionsActions.addNewFlow.displayValue}
75+
</button>
76+
</Menu.Item>
77+
</div>
78+
) : (
79+
<div className='px-1 py-1' data-click-from='options-menu' data-item-type={itemType}>
80+
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
81+
<button
82+
className={menuItemsStyles}
83+
data-click-from='options-menu'
84+
data-options-menu-item={DirectoryOptionsActions.cloneFlow.value}
85+
data-path-name={directory.pathname}
86+
data-item-type={itemType}
87+
data-collection-id={collectionId}
88+
>
89+
<PencilSquareIcon
90+
className='w-4 h-4 mr-2'
91+
aria-hidden='true'
92+
data-click-from='options-menu'
93+
data-item-type={itemType}
94+
/>
95+
{DirectoryOptionsActions.cloneFlow.displayValue}
96+
</button>
97+
</Menu.Item>
98+
</div>
99+
)}
77100
<div className='px-1 py-1' data-click-from='options-menu' data-item-type={itemType}>
78101
<Menu.Item data-click-from='options-menu' data-item-type={itemType}>
79102
<button

src/components/molecules/flow/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ const Flow = ({ tab, collectionId }) => {
226226
onMoveEnd={(event, data) => {
227227
setViewport(data);
228228
}}
229+
minZoom={0}
230+
maxZoom={2}
229231
isValidConnection={isValidConnection}
230232
>
231233
<Background variant='dots' gap={12} size={1} />

src/components/molecules/flow/nodes/RequestNode.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ const RequestNode = ({ id, data }) => {
108108
handleRight={true}
109109
handleRightData={{ type: 'source' }}
110110
>
111-
<div className='min-w-60'>
111+
<div className='min-w-80'>
112112
<div className='pb-4'>
113113
<TextInput
114114
placeHolder={`Enter URL for a ${data.requestType} request`}

src/components/molecules/modals/sidebar/NewLabelModal.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { Fragment, useState } from 'react';
22
import { PropTypes } from 'prop-types';
33
import { Dialog, Transition } from '@headlessui/react';
4-
import { createFolder, createFlowTest, createEnvironmentFile } from 'service/collection';
4+
import { createFolder, createFlowTest, createEnvironmentFile, cloneFlowTest } from 'service/collection';
55
import { DirectoryOptionsActions } from 'constants/WorkspaceDirectory';
66
import { toast } from 'react-toastify';
77
import Button from 'components/atoms/common/Button';
@@ -95,6 +95,21 @@ const NewLabelModal = ({ closeFn = () => null, open = false, pathName, collectio
9595
toast.error(`Error creating new flowtest`);
9696
closeFn();
9797
});
98+
} else if (menuOption === 'clone-flow') {
99+
console.log(labelValue);
100+
console.log(pathName);
101+
cloneFlowTest(labelValue, pathName, collectionId)
102+
.then((result) => {
103+
console.log(
104+
`Cloned a new flowtest: name = ${labelValue}, path = ${pathName}, collectionId = ${collectionId}, result: ${result} \n`,
105+
);
106+
toast.success(`Cloned the flowtest: ${labelValue}`);
107+
})
108+
.catch((error) => {
109+
console.log(`Error cloning flowtest: ${error}`);
110+
toast.error(`Error cloning flowtest`);
111+
closeFn();
112+
});
98113
} else if (menuOption === 'collection') {
99114
// createCollection();
100115
// wont be needing it here but just putting it for testing

src/components/molecules/sidebar/content/Collection.js

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,11 @@ import OptionsMenu from 'components/atoms/sidebar/collections/OptionsMenu';
77
import { toast } from 'react-toastify';
88
import Tippy from '@tippyjs/react';
99
import 'tippy.js/dist/tippy.css';
10-
import { DirectoryOptionsActions } from 'constants/WorkspaceDirectory';
11-
import ConfirmActionModal from 'components/molecules/modals/ConfirmActionModal';
12-
import { deleteFlowTest } from 'service/collection';
1310
import useCollectionStore from 'stores/CollectionStore';
1411

1512
const Collection = ({ collectionId, item, depth }) => {
1613
//const [isExpanded, setIsExpanded] = useState(false);
1714
const clickItem = useCollectionStore((state) => state.clickItem);
18-
const [confirmActionModalOpen, setConfirmActionModalOpen] = useState(false);
19-
const [flowTestPathToDelete, setFlowTestPathToDelete] = useState('');
20-
21-
const messageForConfirmActionModal =
22-
'Do you wish to delete this flowtest? This action deletes it from disk and cannot be undone';
2315

2416
const getListDisplayTitle = () => {
2517
if (item.type === OBJ_TYPES.collection) {
@@ -75,15 +67,13 @@ const Collection = ({ collectionId, item, depth }) => {
7567
<DocumentIcon className='w-4 h-4' />
7668
<span>{item.name}</span>
7769
</div>
78-
<div
79-
className='relative inline-block p-2 text-left transition duration-200 ease-out rounded rounded-l-none hover:bg-slate-200'
80-
onClick={() => {
81-
setFlowTestPathToDelete(item.pathname);
82-
setConfirmActionModalOpen(true);
83-
}}
84-
>
85-
<TrashIcon className='w-4 h-4' aria-hidden='true' />
86-
</div>
70+
<OptionsMenu
71+
data-click-from='options-menu'
72+
directory={item}
73+
data-item-type={OBJ_TYPES.flowtest}
74+
itemType={OBJ_TYPES.flowtest}
75+
collectionId={collectionId}
76+
/>
8777
</div>
8878
);
8979
}
@@ -132,24 +122,6 @@ const Collection = ({ collectionId, item, depth }) => {
132122
</>
133123
)}
134124
</li>
135-
<ConfirmActionModal
136-
closeFn={() => setConfirmActionModalOpen(false)}
137-
open={confirmActionModalOpen}
138-
message={messageForConfirmActionModal}
139-
actionFn={() => {
140-
deleteFlowTest(flowTestPathToDelete, collectionId)
141-
.then((result) => {
142-
console.log(
143-
`Deleted flowtest: path = ${flowTestPathToDelete}, collectionId = ${collectionId}, result: ${result}`,
144-
);
145-
})
146-
.catch((error) => {
147-
console.log(`Error deleting flowtest = ${flowTestPathToDelete}: ${error}`);
148-
toast.error(`Error deleting flowtest`);
149-
});
150-
setConfirmActionModalOpen(false);
151-
}}
152-
/>
153125
</>
154126
);
155127
};

src/components/molecules/sidebar/content/Collections.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ const Collections = ({ collections }) => {
8181
case DirectoryOptionsActions.addNewFlow.value:
8282
setNewLabelModal(true);
8383
break;
84+
case DirectoryOptionsActions.cloneFlow.value:
85+
setNewLabelModal(true);
86+
break;
8487
case DirectoryOptionsActions.delete.value:
8588
setSelectedItemType(itemType);
8689
setConfirmActionModalOpen(true);

src/constants/WorkspaceDirectory.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ export const DirectoryOptionsActions = {
1919
value: 'new-flow',
2020
dataSetValue: 'new-flow',
2121
},
22+
cloneFlow: {
23+
displayValue: 'Clone Flow',
24+
value: 'clone-flow',
25+
dataSetValue: 'clone-flow',
26+
},
2227
addNewEnvironment: {
2328
displayValue: 'New Environment',
2429
value: 'new-environment',

src/service/collection.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,35 @@ export const createFlowTest = (name, folderPath, collectionId) => {
269269
}
270270
};
271271

272+
export const cloneFlowTest = (name, flowTestPath, collectionId) => {
273+
try {
274+
const { ipcRenderer } = window;
275+
276+
const collection = useCollectionStore.getState().collections.find((c) => c.id === collectionId);
277+
if (collection) {
278+
const flowtest = findItemInCollectionByPathname(collection, flowTestPath);
279+
if (flowtest) {
280+
return new Promise((resolve, reject) => {
281+
ipcRenderer.invoke('renderer:clone-flowtest', name, flowTestPath).then(resolve).catch(reject);
282+
useEventStore.getState().addEvent({
283+
id: uuidv4(),
284+
type: 'OPEN_NEW_FLOWTEST',
285+
collectionId,
286+
name: `${name}.flow`,
287+
path: ipcRenderer.dirname(flowTestPath),
288+
});
289+
});
290+
}
291+
} else {
292+
return Promise.reject(new Error('Collection not found'));
293+
}
294+
} catch (error) {
295+
console.log(`Error cloning flowtest: ${error}`);
296+
//toast.error('Error creating new flowtest');
297+
return Promise.reject(new Error('Error cloning flowtest'));
298+
}
299+
};
300+
272301
export const readFlowTest = (pathname, collectionId) => {
273302
try {
274303
const { ipcRenderer } = window;

0 commit comments

Comments
 (0)