Added properties to collections

This commit is contained in:
Amr osama 2023-10-21 20:51:13 +03:00
parent f8f38802a9
commit 36d6d115d4
7 changed files with 214 additions and 24 deletions

View File

@ -0,0 +1,40 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
div.method-selector-container {
border: solid 1px ${(props) => props.theme.modal.input.border};
border-right: none;
background-color: ${(props) => props.theme.modal.input.bg};
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
.method-selector {
min-width: 80px;
}
}
div.method-selector-container,
div.input-container {
background-color: ${(props) => props.theme.modal.input.bg};
height: 2.3rem;
}
div.input-container {
border: solid 1px ${(props) => props.theme.modal.input.border};
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
input {
background-color: ${(props) => props.theme.modal.input.bg};
outline: none;
box-shadow: none;
&:focus {
outline: none !important;
box-shadow: none !important;
}
}
}
`;
export default StyledWrapper;

View File

@ -1,5 +1,12 @@
import React from 'react'; import React from 'react';
import Modal from 'components/Modal'; import Modal from 'components/Modal';
import { useState } from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import StyledWrapper from './StyledWrapper';
import { useDispatch } from 'react-redux';
import toast from 'react-hot-toast';
import { updateCollectionProperties } from 'providers/ReduxStore/slices/collections/actions';
function countRequests(items) { function countRequests(items) {
let count = 0; let count = 0;
@ -21,29 +28,116 @@ function countRequests(items) {
} }
const CollectionProperties = ({ collection, onClose }) => { const CollectionProperties = ({ collection, onClose }) => {
const dispatch = useDispatch();
const {
brunoConfig: { properties: defaultProperties = {} }
} = collection;
const formik = useFormik({
enableReinitialize: true,
initialValues: {
defaultType: defaultProperties.defaultType || 'http-request',
defaultUrl: defaultProperties.defaultUrl || ''
},
onSubmit: (newProperties) => {
dispatch(updateCollectionProperties(newProperties, collection.uid));
toast.success('Collection properties updated');
onClose();
}
});
const onSubmit = () => formik.handleSubmit();
return ( return (
<Modal size="sm" title="Collection Properties" hideFooter={true} handleCancel={onClose}> <StyledWrapper>
<table className="w-full border-collapse"> <Modal
<tbody> size="sm"
<tr className=""> title="Collection Properties"
<td className="py-2 px-2 text-right">Name&nbsp;:</td> confirmText="Update"
<td className="py-2 px-2">{collection.name}</td> handleConfirm={onSubmit}
</tr> handleCancel={onClose}
<tr className=""> >
<td className="py-2 px-2 text-right">Location&nbsp;:</td> <form className="bruno-form" onSubmit={formik.handleSubmit}>
<td className="py-2 px-2 break-all">{collection.pathname}</td> <table className="w-full border-collapse">
</tr> <tbody>
<tr className=""> <tr className="">
<td className="py-2 px-2 text-right">Environments&nbsp;:</td> <td className="py-2 px-2 text-right">Name&nbsp;:</td>
<td className="py-2 px-2">{collection.environments?.length || 0}</td> <td className="py-2 px-2">{collection.name}</td>
</tr> </tr>
<tr className=""> <tr className="">
<td className="py-2 px-2 text-right">Requests&nbsp;:</td> <td className="py-2 px-2 text-right">Location&nbsp;:</td>
<td className="py-2 px-2">{countRequests(collection.items)}</td> <td className="py-2 px-2 break-all">{collection.pathname}</td>
</tr> </tr>
</tbody> <tr className="">
</table> <td className="py-2 px-2 text-right">Environments&nbsp;:</td>
</Modal> <td className="py-2 px-2">{collection.environments?.length || 0}</td>
</tr>
<tr className="">
<td className="py-2 px-2 text-right">Requests&nbsp;:</td>
<td className="py-2 px-2">{countRequests(collection.items)}</td>
</tr>
<tr className="">
<td className="py-2 px-2 text-right">Default Request Type&nbsp;:</td>
<td className="py-2 px-2">
<div className="flex items-center mt-2">
<input
id="http-request"
className="cursor-pointer"
type="radio"
name="defaultType"
onChange={formik.handleChange}
value="http-request"
checked={formik.values.defaultType === 'http-request'}
/>
<label htmlFor="http-request" className="ml-1 cursor-pointer select-none">
HTTP
</label>
<input
id="graphql-request"
className="ml-4 cursor-pointer"
type="radio"
name="defaultType"
onChange={formik.handleChange}
value="graphql-request"
checked={formik.values.defaultType === 'graphql-request'}
/>
<label htmlFor="graphql-request" className="ml-1 cursor-pointer select-none">
GraphQL
</label>
</div>
</td>
</tr>
<tr className="">
<td className="py-2 px-2 text-right">Default Base URL&nbsp;:</td>
<td className="py-2 px-2">
<div className="flex items-center mt-2 ">
<div className="flex items-center flex-grow input-container h-full">
<input
id="request-url"
type="text"
name="defaultUrl"
className="px-3 w-full "
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.defaultUrl || ''}
/>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div className="mt-4">
{formik.touched.defaultUrl && formik.errors.defaultUrl ? (
<div className="text-red-500">{formik.errors.defaultUrl}</div>
) : null}
</div>
</form>
</Modal>
</StyledWrapper>
); );
}; };

View File

@ -15,12 +15,15 @@ import StyledWrapper from './StyledWrapper';
const NewRequest = ({ collection, item, isEphemeral, onClose }) => { const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const inputRef = useRef(); const inputRef = useRef();
const {
brunoConfig: { properties: collectionProperties = {} }
} = collection;
const formik = useFormik({ const formik = useFormik({
enableReinitialize: true, enableReinitialize: true,
initialValues: { initialValues: {
requestName: '', requestName: '',
requestType: 'http-request', requestType: collectionProperties.defaultType || 'http-request',
requestUrl: '', requestUrl: collectionProperties.defaultUrl || '',
requestMethod: 'GET' requestMethod: 'GET'
}, },
validationSchema: Yup.object({ validationSchema: Yup.object({

View File

@ -18,6 +18,7 @@ import { updatePreferences } from 'providers/ReduxStore/slices/app';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions'; import { openCollectionEvent, collectionAddEnvFileEvent } from 'providers/ReduxStore/slices/collections/actions';
import { isElectron } from 'utils/common/platform'; import { isElectron } from 'utils/common/platform';
import { collectionPropertiesUpdatedEvent } from 'providers/ReduxStore/slices/collections/index';
const useIpcEvents = () => { const useIpcEvents = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -107,6 +108,10 @@ const useIpcEvents = () => {
dispatch(collectionRenamedEvent(val)); dispatch(collectionRenamedEvent(val));
}); });
const removeCollectionPropertiesUpdatedListener = ipcRenderer.on('main:collection-properties-updated', (val) => {
dispatch(collectionPropertiesUpdatedEvent(val));
});
const removeRunFolderEventListener = ipcRenderer.on('main:run-folder-event', (val) => { const removeRunFolderEventListener = ipcRenderer.on('main:run-folder-event', (val) => {
dispatch(runFolderEvent(val)); dispatch(runFolderEvent(val));
}); });
@ -138,6 +143,7 @@ const useIpcEvents = () => {
removeDisplayErrorListener(); removeDisplayErrorListener();
removeScriptEnvUpdateListener(); removeScriptEnvUpdateListener();
removeCollectionRenamedListener(); removeCollectionRenamedListener();
removeCollectionPropertiesUpdatedListener();
removeRunFolderEventListener(); removeRunFolderEventListener();
removeRunRequestEventListener(); removeRunRequestEventListener();
removeProcessEnvUpdatesListener(); removeProcessEnvUpdatesListener();

View File

@ -47,6 +47,22 @@ import { resolveRequestFilename } from 'utils/common/platform';
import { parseQueryParams, splitOnFirst } from 'utils/url/index'; import { parseQueryParams, splitOnFirst } from 'utils/url/index';
import { each } from 'lodash'; import { each } from 'lodash';
export const updateCollectionProperties = (newProperties, collectionUid) => (dispatch, getState) => {
const state = getState();
const collection = findCollectionByUid(state.collections.collections, collectionUid);
return new Promise((resolve, reject) => {
if (!collection) {
return reject(new Error('Collection not found'));
}
ipcRenderer
.invoke('renderer:update-collection-properties', newProperties, collection.pathname)
.then(resolve)
.catch(reject);
});
};
export const renameCollection = (newName, collectionUid) => (dispatch, getState) => { export const renameCollection = (newName, collectionUid) => (dispatch, getState) => {
const state = getState(); const state = getState();
const collection = findCollectionByUid(state.collections.collections, collectionUid); const collection = findCollectionByUid(state.collections.collections, collectionUid);

View File

@ -1224,6 +1224,14 @@ export const collectionsSlice = createSlice({
collection.name = newName; collection.name = newName;
} }
}, },
collectionPropertiesUpdatedEvent: (state, action) => {
const { collectionPathname, newProperties } = action.payload;
const collection = findCollectionByPathname(state.collections, collectionPathname);
if (collection) {
collection.properties = newProperties;
}
},
resetRunResults: (state, action) => { resetRunResults: (state, action) => {
const { collectionUid } = action.payload; const { collectionUid } = action.payload;
const collection = findCollectionByUid(state.collections, collectionUid); const collection = findCollectionByUid(state.collections, collectionUid);
@ -1427,6 +1435,7 @@ export const {
collectionUnlinkDirectoryEvent, collectionUnlinkDirectoryEvent,
collectionAddEnvFileEvent, collectionAddEnvFileEvent,
collectionRenamedEvent, collectionRenamedEvent,
collectionPropertiesUpdatedEvent,
resetRunResults, resetRunResults,
runRequestEvent, runRequestEvent,
runFolderEvent, runFolderEvent,

View File

@ -94,6 +94,28 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
} }
}); });
// update collection properties
ipcMain.handle('renderer:update-collection-properties', async (event, newProperties, collectionPathname) => {
try {
const brunoJsonFilePath = path.join(collectionPathname, 'bruno.json');
const content = fs.readFileSync(brunoJsonFilePath, 'utf8');
const json = JSON.parse(content);
json.properties = newProperties;
const newContent = await stringifyJson(json);
await writeFile(brunoJsonFilePath, newContent);
// fire an event in renderer to change the collection properties
mainWindow.webContents.send('main:collection-properties-updated', {
collectionPathname,
newProperties
});
} catch (error) {
return Promise.reject(error);
}
});
ipcMain.handle('renderer:save-collection-root', async (event, collectionPathname, collectionRoot) => { ipcMain.handle('renderer:save-collection-root', async (event, collectionPathname, collectionRoot) => {
try { try {
const collectionBruFilePath = path.join(collectionPathname, 'collection.bru'); const collectionBruFilePath = path.join(collectionPathname, 'collection.bru');