diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js new file mode 100644 index 000000000..87b833e40 --- /dev/null +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/CopyEnvironment/index.js @@ -0,0 +1,75 @@ +import Modal from 'components/Modal/index'; +import Portal from 'components/Portal/index'; +import { useFormik } from 'formik'; +import { copyEnvironment } from 'providers/ReduxStore/slices/collections/actions'; +import { useEffect, useRef } from 'react'; +import toast from 'react-hot-toast'; +import { useDispatch } from 'react-redux'; +import * as Yup from 'yup'; + +const CopyEnvironment = ({ collection, environment, onClose }) => { + const dispatch = useDispatch(); + const inputRef = useRef(); + const formik = useFormik({ + enableReinitialize: true, + initialValues: { + name: environment.name + ' - Copy' + }, + validationSchema: Yup.object({ + name: Yup.string() + .min(1, 'must be atleast 1 characters') + .max(50, 'must be 50 characters or less') + .required('name is required') + }), + onSubmit: (values) => { + dispatch(copyEnvironment(values.name, environment.uid, collection.uid)) + .then(() => { + toast.success('Environment created in collection'); + onClose(); + }) + .catch(() => toast.error('An error occurred while created the environment')); + } + }); + + useEffect(() => { + if (inputRef && inputRef.current) { + inputRef.current.focus(); + } + }, [inputRef]); + + const onSubmit = () => { + formik.handleSubmit(); + }; + + return ( + + +
+
+ + + {formik.touched.name && formik.errors.name ? ( +
{formik.errors.name}
+ ) : null} +
+
+
+
+ ); +}; + +export default CopyEnvironment; diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js index 8db0d0418..f8b9e364e 100644 --- a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js +++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js @@ -1,12 +1,14 @@ -import React, { useState } from 'react'; -import { IconEdit, IconTrash, IconDatabase } from '@tabler/icons'; -import EnvironmentVariables from './EnvironmentVariables'; -import RenameEnvironment from '../../RenameEnvironment'; +import { IconCopy, IconDatabase, IconEdit, IconTrash } from '@tabler/icons'; +import { useState } from 'react'; +import CopyEnvironment from '../../CopyEnvironment'; import DeleteEnvironment from '../../DeleteEnvironment'; +import RenameEnvironment from '../../RenameEnvironment'; +import EnvironmentVariables from './EnvironmentVariables'; const EnvironmentDetails = ({ environment, collection }) => { const [openEditModal, setOpenEditModal] = useState(false); const [openDeleteModal, setOpenDeleteModal] = useState(false); + const [openCopyModal, setOpenCopyModal] = useState(false); return (
@@ -20,6 +22,9 @@ const EnvironmentDetails = ({ environment, collection }) => { collection={collection} /> )} + {openCopyModal && ( + setOpenCopyModal(false)} environment={environment} collection={collection} /> + )}
@@ -27,6 +32,7 @@ const EnvironmentDetails = ({ environment, collection }) => {
setOpenEditModal(true)} /> + setOpenCopyModal(true)} /> setOpenDeleteModal(true)} />
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index ccdd7fe1a..d806a3836 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -646,6 +646,37 @@ export const addEnvironment = (name, collectionUid) => (dispatch, getState) => { }); }; +export const copyEnvironment = (name, baseEnvUid, collectionUid) => (dispatch, getState) => { + return new Promise((resolve, reject) => { + const state = getState(); + const collection = findCollectionByUid(state.collections.collections, collectionUid); + if (!collection) { + return reject(new Error('Collection not found')); + } + + const baseEnv = findEnvironmentInCollection(collection, baseEnvUid); + if (!collection) { + return reject(new Error('Environmnent not found')); + } + + ipcRenderer + .invoke('renderer:copy-environment', collection.pathname, name, baseEnv.variables) + .then( + dispatch( + updateLastAction({ + collectionUid, + lastAction: { + type: 'ADD_ENVIRONMENT', + payload: name + } + }) + ) + ) + .then(resolve) + .catch(reject); + }); +}; + export const renameEnvironment = (newName, environmentUid, collectionUid) => (dispatch, getState) => { return new Promise((resolve, reject) => { const state = getState(); diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js index ae85558af..864aff82e 100644 --- a/packages/bruno-electron/src/ipc/collection.js +++ b/packages/bruno-electron/src/ipc/collection.js @@ -151,6 +151,28 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection } }); + // copy environment + ipcMain.handle('renderer:copy-environment', async (event, collectionPathname, name, baseVariables) => { + try { + const envDirPath = path.join(collectionPathname, 'environments'); + if (!fs.existsSync(envDirPath)) { + await createDirectory(envDirPath); + } + + const envFilePath = path.join(envDirPath, `${name}.bru`); + if (fs.existsSync(envFilePath)) { + throw new Error(`environment: ${envFilePath} already exists`); + } + + const content = envJsonToBru({ + variables: baseVariables + }); + await writeFile(envFilePath, content); + } catch (error) { + return Promise.reject(error); + } + }); + // save environment ipcMain.handle('renderer:save-environment', async (event, collectionPathname, environment) => { try {