mirror of
https://github.com/usebruno/bruno.git
synced 2025-06-21 12:33:34 +02:00
Add: Add new button to copy existing environments
This commit is contained in:
parent
90fedc8ec6
commit
3a6dacc1f4
@ -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 (
|
||||||
|
<Portal>
|
||||||
|
<Modal size="sm" title={'Copy Environment'} confirmText="Copy" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||||
|
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="block font-semibold">
|
||||||
|
New Environment Name
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="environment-name"
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
ref={inputRef}
|
||||||
|
className="block textbox mt-2 w-full"
|
||||||
|
autoComplete="off"
|
||||||
|
autoCorrect="off"
|
||||||
|
autoCapitalize="off"
|
||||||
|
spellCheck="false"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.name || ''}
|
||||||
|
/>
|
||||||
|
{formik.touched.name && formik.errors.name ? (
|
||||||
|
<div className="text-red-500">{formik.errors.name}</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CopyEnvironment;
|
@ -1,12 +1,14 @@
|
|||||||
import React, { useState } from 'react';
|
import { IconCopy, IconDatabase, IconEdit, IconTrash } from '@tabler/icons';
|
||||||
import { IconEdit, IconTrash, IconDatabase } from '@tabler/icons';
|
import { useState } from 'react';
|
||||||
import EnvironmentVariables from './EnvironmentVariables';
|
import CopyEnvironment from '../../CopyEnvironment';
|
||||||
import RenameEnvironment from '../../RenameEnvironment';
|
|
||||||
import DeleteEnvironment from '../../DeleteEnvironment';
|
import DeleteEnvironment from '../../DeleteEnvironment';
|
||||||
|
import RenameEnvironment from '../../RenameEnvironment';
|
||||||
|
import EnvironmentVariables from './EnvironmentVariables';
|
||||||
|
|
||||||
const EnvironmentDetails = ({ environment, collection }) => {
|
const EnvironmentDetails = ({ environment, collection }) => {
|
||||||
const [openEditModal, setOpenEditModal] = useState(false);
|
const [openEditModal, setOpenEditModal] = useState(false);
|
||||||
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
const [openDeleteModal, setOpenDeleteModal] = useState(false);
|
||||||
|
const [openCopyModal, setOpenCopyModal] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="px-6 flex-grow flex flex-col pt-6" style={{ maxWidth: '700px' }}>
|
<div className="px-6 flex-grow flex flex-col pt-6" style={{ maxWidth: '700px' }}>
|
||||||
@ -20,6 +22,9 @@ const EnvironmentDetails = ({ environment, collection }) => {
|
|||||||
collection={collection}
|
collection={collection}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{openCopyModal && (
|
||||||
|
<CopyEnvironment onClose={() => setOpenCopyModal(false)} environment={environment} collection={collection} />
|
||||||
|
)}
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<div className="flex flex-grow items-center">
|
<div className="flex flex-grow items-center">
|
||||||
<IconDatabase className="cursor-pointer" size={20} strokeWidth={1.5} />
|
<IconDatabase className="cursor-pointer" size={20} strokeWidth={1.5} />
|
||||||
@ -27,6 +32,7 @@ const EnvironmentDetails = ({ environment, collection }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-4 pl-4">
|
<div className="flex gap-x-4 pl-4">
|
||||||
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenEditModal(true)} />
|
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenEditModal(true)} />
|
||||||
|
<IconCopy className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenCopyModal(true)} />
|
||||||
<IconTrash className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenDeleteModal(true)} />
|
<IconTrash className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenDeleteModal(true)} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -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) => {
|
export const renameEnvironment = (newName, environmentUid, collectionUid) => (dispatch, getState) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
@ -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
|
// save environment
|
||||||
ipcMain.handle('renderer:save-environment', async (event, collectionPathname, environment) => {
|
ipcMain.handle('renderer:save-environment', async (event, collectionPathname, environment) => {
|
||||||
try {
|
try {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user