Skip to content

Commit 8695ff1

Browse files
authored
Merge pull request #51 from FlowTestAI/ui-environments
Adding functionality for environments directory rendering
2 parents 86cc859 + f428b6f commit 8695ff1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1826
-618
lines changed

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

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,44 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
104104
}
105105
});
106106

107+
ipcMain.handle('renderer:open-collection', async (event, openAPISpecFilePath, collectionFolderPath) => {
108+
try {
109+
if (isDirectory(collectionFolderPath)) {
110+
// async/await syntax
111+
const api = await SwaggerParser.validate(openAPISpecFilePath);
112+
// console.log("API name: %s, Version: %s", api.info.title, api.info.version);
113+
114+
// resolve references in openapi spec
115+
const resolvedSpec = await JsonRefs.resolveRefs(api);
116+
const parsedNodes = parseOpenAPISpec(resolvedSpec.resolved);
117+
118+
const id = uuidv4();
119+
const collectionName = api.info.title;
120+
121+
const newCollection = {
122+
id: id,
123+
name: collectionName,
124+
pathname: collectionFolderPath,
125+
openapi_spec: resolvedSpec.resolved,
126+
nodes: parsedNodes,
127+
};
128+
129+
mainWindow.webContents.send('main:collection-created', id, collectionName, collectionFolderPath, parsedNodes);
130+
131+
watcher.addWatcher(mainWindow, collectionFolderPath, id);
132+
collectionStore.add(newCollection);
133+
} else {
134+
return Promise.reject(new Error(`Directory: ${collectionFolderPath} does not exist`));
135+
}
136+
} catch (error) {
137+
return Promise.reject(error);
138+
}
139+
});
140+
107141
ipcMain.handle('renderer:delete-collection', async (event, collection) => {
108142
try {
109-
deleteDirectory(collection.pathname);
110-
console.log(`Deleted directory: ${collection.pathname}`);
143+
// deleteDirectory(collection.pathname);
144+
// console.log(`Deleted directory: ${collection.pathname}`);
111145

112146
mainWindow.webContents.send('main:collection-deleted', collection.id);
113147

@@ -161,8 +195,8 @@ const registerRendererEventHandlers = (mainWindow, watcher) => {
161195
ipcMain.handle('renderer:delete-environment', async (event, collectionPath, name) => {
162196
try {
163197
const envDir = path.join(collectionPath, 'environments');
164-
deleteFile(path.join(envDir, `${name}.env`));
165-
console.log(`Delete file: ${name}.env`);
198+
deleteFile(path.join(envDir, name));
199+
console.log(`Delete file: ${name}`);
166200
} catch (error) {
167201
return Promise.reject(error);
168202
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class Collections {
2020
remove(collection) {
2121
const collections = this.store.get('collections') || [];
2222

23-
if (!isDirectory(collection.pathname)) {
23+
if (collections.find((c) => c.id === collection.id)) {
2424
this.store.set(
2525
'collections',
2626
collections.filter((c) => c.pathname !== collection.pathname),

packages/flowtest-electron/tests/store/collection-store.test.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ describe('collection-store', () => {
1111
id: '1234',
1212
name: 'test',
1313
pathname: `${__dirname}/test`,
14-
collection: '',
14+
openapi_spec: '',
1515
nodes: '{}',
1616
};
1717

1818
const newestCollection = {
1919
id: '12345',
2020
name: 'test1',
2121
pathname: `${__dirname}/test1`,
22-
collection: '',
22+
openapi_spec: '',
2323
nodes: '{}',
2424
};
2525

@@ -38,17 +38,14 @@ describe('collection-store', () => {
3838
store.add(newestCollection);
3939
expect(store.getAll()).toEqual([newCollection, newestCollection]);
4040

41-
// removing a collection whose directory still exist
42-
store.remove(newCollection);
43-
expect(store.getAll()).toEqual([newCollection, newestCollection]);
44-
45-
deleteDirectory(`${__dirname}/test`);
4641
store.remove(newCollection);
4742
expect(store.getAll()).toEqual([newestCollection]);
4843

49-
deleteDirectory(`${__dirname}/test1`);
5044
store.remove(newestCollection);
5145
expect(store.getAll()).toEqual([]);
46+
47+
deleteDirectory(`${__dirname}/test`);
48+
deleteDirectory(`${__dirname}/test1`);
5249
});
5350

5451
it('collection set should be unique by pathname', async () => {
@@ -57,15 +54,15 @@ describe('collection-store', () => {
5754
id: '1234',
5855
name: 'test',
5956
pathname: `${__dirname}/test`,
60-
collection: '',
57+
openapi_spec: '',
6158
nodes: '{}',
6259
};
6360

6461
const newestCollection = {
6562
id: '12345',
6663
name: 'test',
6764
pathname: `${__dirname}/test`,
68-
collection: '',
65+
openapi_spec: '',
6966
nodes: '{}',
7067
};
7168

@@ -80,8 +77,9 @@ describe('collection-store', () => {
8077
store.add(newestCollection);
8178
expect(store.getAll()).toEqual([newCollection]);
8279

83-
deleteDirectory(`${__dirname}/test`);
8480
store.remove(newCollection);
8581
expect(store.getAll()).toEqual([]);
82+
83+
deleteDirectory(`${__dirname}/test`);
8684
});
8785
});

src/App.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react';
22
import './App.css';
3-
import FlowRoutes from './routes';
3+
import Routes from './routes';
44
import { HashRouter } from 'react-router-dom';
55

66
function App() {
77
return (
88
<HashRouter>
9-
<FlowRoutes />
9+
<Routes />
1010
</HashRouter>
1111
);
1212
}

src/components/atoms/SelectEnvironment.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { Fragment, useState } from 'react';
2+
import { PropTypes } from 'prop-types';
23
import { Listbox, Transition } from '@headlessui/react';
34
import { CheckIcon, ChevronUpDownIcon } from '@heroicons/react/20/solid';
45
import { Square3Stack3DIcon } from '@heroicons/react/24/outline';
@@ -53,4 +54,8 @@ const SelectEnvironment = ({ environments }) => {
5354
);
5455
};
5556

57+
SelectEnvironment.propTypes = {
58+
environments: PropTypes.Array.isRequired,
59+
};
60+
5661
export default SelectEnvironment;

src/components/atoms/Tabs.js

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,78 @@
1-
import React from 'react';
1+
import React, { useState } from 'react';
22
import { XMarkIcon } from '@heroicons/react/24/outline';
33
import { useTabStore } from 'stores/TabStore';
4+
import ConfirmActionModal from 'components/molecules/modals/ConfirmActionModal';
45

56
const Tabs = () => {
67
const tabs = useTabStore((state) => state.tabs);
78
const setFocusTab = useTabStore((state) => state.setFocusTab);
89
const focusTabId = useTabStore((state) => state.focusTabId);
910
const focusTab = tabs.find((t) => t.id === focusTabId);
11+
const [confirmActionModalOpen, setConfirmActionModalOpen] = useState(false);
1012
const closeTab = useTabStore((state) => state.closeTab);
13+
const [closingTabId, setClosingTabId] = useState('');
14+
const [closingCollectionId, setClosingCollectionId] = useState('');
1115
const activeTabStyles =
1216
'before:absolute before:h-[0.25rem] before:w-full before:bg-slate-300 before:content-[""] before:bottom-0 before:left-0';
1317
const tabCommonStyles =
1418
'tab flex items-center gap-x-2 border-r border-neutral-300 bg-transparent pr-0 tracking-[0.15em] transition duration-500 ease-in text-sm';
19+
const messageForConfirmActionModal = 'You have unsaved changes in the flowtest, are you sure you want to close it?';
20+
const handleCloseTab = (event) => {
21+
event.stopPropagation();
22+
event.preventDefault();
23+
const tabId = event.currentTarget.dataset.tabId;
24+
const { isDirty, collectionId } = tabs.find((tab) => {
25+
if (tab.id === tabId) return tab;
26+
});
27+
setClosingTabId(tabId);
28+
setClosingCollectionId(collectionId);
29+
30+
if (isDirty) {
31+
console.log(`\n \n BHUT HI SAHIIII tabId: ${tabId} : collectionId: ${collectionId}\n \n`);
32+
setConfirmActionModalOpen(true);
33+
return;
34+
}
35+
closeTab(tabId, collectionId);
36+
};
1537
return (
1638
<div role='tablist' className='tabs tabs-lg'>
1739
{tabs
1840
// tabs belonging to one collection will be shown at a time
1941
.filter((t) => t.collectionId === focusTab.collectionId)
2042
.map((tab, index) => {
2143
return (
22-
<>
23-
<a
24-
role='tab'
25-
className={`${tabCommonStyles} ${focusTabId === tab.id ? activeTabStyles : ''}`}
26-
key={index}
27-
data-id={tab.id}
28-
data-collection-id={tab.collectionId}
29-
onClick={() => {
30-
setFocusTab(tab.id);
31-
console.log(`CLICKED THE ${tab.id}`);
32-
}}
33-
>
34-
{tab.name}
35-
</a>
44+
<div
45+
key={index}
46+
className={`${tabCommonStyles} ${focusTabId === tab.id ? activeTabStyles : ''}`}
47+
role='tab'
48+
onClick={() => {
49+
setFocusTab(tab.id);
50+
console.log(`CLICKED THE ${tab.id}`);
51+
}}
52+
data-id={tab.id}
53+
data-collection-id={tab.collectionId}
54+
>
55+
<a>{tab.name}</a>
3656
{/* close needs to be a separate clickable component other wise it gets confused with above */}
3757
<div
38-
className='flex h-full items-center px-2 hover:rounded hover:rounded-l-none hover:bg-slate-200'
39-
onClick={() => {
40-
closeTab(tab.id, tab.collectionId);
41-
}}
58+
className='flex items-center h-full px-2 hover:rounded hover:rounded-l-none hover:bg-slate-200'
59+
data-tab-id={tab.id}
60+
onClick={handleCloseTab}
4261
>
43-
<XMarkIcon className='h-4 w-4' />
62+
<XMarkIcon className='w-4 h-4' />
4463
</div>
45-
</>
64+
</div>
4665
);
4766
})}
67+
<ConfirmActionModal
68+
closeFn={() => setConfirmActionModalOpen(false)}
69+
open={confirmActionModalOpen}
70+
message={messageForConfirmActionModal}
71+
actionFn={() => {
72+
closeTab(closingTabId, closingCollectionId);
73+
setConfirmActionModalOpen(false);
74+
}}
75+
/>
4876
</div>
4977
);
5078
};
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react';
2+
import Tippy from '@tippyjs/react';
3+
import 'tippy.js/dist/tippy.css';
4+
5+
const ThemeController = () => {
6+
return (
7+
<Tippy content='Coming Soon!' placement='bottom'>
8+
<label className='flex items-center gap-2 cursor-pointer'>
9+
<svg
10+
xmlns='http://www.w3.org/2000/svg'
11+
width='16'
12+
height='16'
13+
viewBox='0 0 24 24'
14+
fill='none'
15+
stroke='currentColor'
16+
strokeWidth='2'
17+
strokeLinecap='round'
18+
strokeLinejoin='round'
19+
>
20+
<circle cx='12' cy='12' r='5' />
21+
<path d='M12 1v2M12 21v2M4.2 4.2l1.4 1.4M18.4 18.4l1.4 1.4M1 12h2M21 12h2M4.2 19.8l1.4-1.4M18.4 5.6l1.4-1.4' />
22+
</svg>
23+
<input type='checkbox' value='synthwave' className='theme-controller toggle toggle-xs' disabled />
24+
<svg
25+
xmlns='http://www.w3.org/2000/svg'
26+
width='16'
27+
height='16'
28+
viewBox='0 0 24 24'
29+
fill='none'
30+
stroke='currentColor'
31+
strokeWidth='2'
32+
strokeLinecap='round'
33+
strokeLinejoin='round'
34+
>
35+
<path d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'></path>
36+
</svg>
37+
</label>
38+
</Tippy>
39+
);
40+
};
41+
42+
export default ThemeController;

src/components/atoms/flow/FlowNode.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import { PropTypes } from 'prop-types';
23
import { Handle, Position } from 'reactflow';
34

45
const FlowNode = ({ children, title, handleLeft, handleLeftData, handleRight, handleRightData }) => {
@@ -27,4 +28,13 @@ const FlowNode = ({ children, title, handleLeft, handleLeftData, handleRight, ha
2728
);
2829
};
2930

31+
FlowNode.propTypes = {
32+
children: PropTypes.node.isRequired,
33+
title: PropTypes.string.isRequired,
34+
handleLeft: PropTypes.node.isRequired,
35+
handleLeftData: PropTypes.object.isRequired,
36+
handleRight: PropTypes.node.isRequired,
37+
handleRightData: PropTypes.object.isRequired,
38+
};
39+
3040
export default FlowNode;

0 commit comments

Comments
 (0)