feat: updates

This commit is contained in:
lohxt1 2024-10-07 22:32:53 +05:30
parent 5afafb5944
commit 8ab8af6b3f
18 changed files with 111 additions and 62 deletions

View File

@ -53,6 +53,7 @@ const EnvironmentSelector = ({ collection }) => {
<StyledWrapper>
<div className="flex items-center cursor-pointer environment-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div className="label-item font-medium">Collection Environments</div>
{environments && environments.length
? environments.map((e) => (
<div

View File

@ -6,7 +6,7 @@ import EnvironmentSettings from '../EnvironmentSettings';
import toast from 'react-hot-toast';
import { useDispatch, useSelector } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import { selectGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { selectGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
const EnvironmentSelector = () => {
const dispatch = useDispatch();
@ -53,6 +53,7 @@ const EnvironmentSelector = () => {
<StyledWrapper>
<div className="flex items-center cursor-pointer environment-selector mr-3">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end" transparent={true}>
<div className="label-item font-medium">Global Environments</div>
{globalEnvironments && globalEnvironments.length
? globalEnvironments.map((e) => (
<div

View File

@ -1,7 +1,7 @@
import Modal from 'components/Modal/index';
import Portal from 'components/Portal/index';
import { useFormik } from 'formik';
import { copyGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { copyGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
import { useEffect, useRef } from 'react';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';

View File

@ -5,7 +5,7 @@ import * as Yup from 'yup';
import { useDispatch } from 'react-redux';
import Portal from 'components/Portal';
import Modal from 'components/Modal';
import { addGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { addGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
const CreateEnvironment = ({ onClose }) => {
const dispatch = useDispatch();

View File

@ -4,14 +4,14 @@ import toast from 'react-hot-toast';
import Modal from 'components/Modal/index';
import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import { deleteGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { deleteGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
const DeleteEnvironment = ({ onClose, environment }) => {
const dispatch = useDispatch();
const onConfirm = () => {
dispatch(deleteGlobalEnvironment({ environmentUid: environment.uid }))
.then(() => {
toast.success('Environment deleted successfully');
toast.success('Global Environment deleted successfully');
onClose();
})
.catch(() => toast.error('An error occurred while deleting the environment'));

View File

@ -10,7 +10,7 @@ import { useFormik } from 'formik';
import * as Yup from 'yup';
import { variableNameRegex } from 'utils/common/regex';
import toast from 'react-hot-toast';
import { saveGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { saveGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
const EnvironmentVariables = ({ environment, setIsModified, originalEnvironmentVariables }) => {
const dispatch = useDispatch();

View File

@ -3,9 +3,10 @@ import usePrevious from 'hooks/usePrevious';
import EnvironmentDetails from './EnvironmentDetails';
import CreateEnvironment from '../CreateEnvironment';
import { IconDownload, IconShieldLock } from '@tabler/icons';
import ManageSecrets from '../ManageSecrets';
import StyledWrapper from './StyledWrapper';
import ConfirmSwitchEnv from './ConfirmSwitchEnv';
import ManageSecrets from 'components/Environments/EnvironmentSettings/ManageSecrets/index';
import ImportEnvironment from '../ImportEnvironment';
const EnvironmentList = ({ environments, activeEnvironmentUid, selectedEnvironment, setSelectedEnvironment, isModified, setIsModified }) => {
const [openCreateModal, setOpenCreateModal] = useState(false);
@ -86,6 +87,7 @@ const EnvironmentList = ({ environments, activeEnvironmentUid, selectedEnvironme
return (
<StyledWrapper>
{openCreateModal && <CreateEnvironment onClose={() => setOpenCreateModal(false)} />}
{openImportModal && <ImportEnvironment onClose={() => setOpenImportModal(false)} />}
{openManageSecretsModal && <ManageSecrets onClose={() => setOpenManageSecretsModal(false)} />}
<div className="flex">

View File

@ -0,0 +1,62 @@
import React from 'react';
import Portal from 'components/Portal';
import Modal from 'components/Modal';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import importPostmanEnvironment from 'utils/importers/postman-environment';
import { toastError } from 'utils/common/error';
import { IconDatabaseImport } from '@tabler/icons';
import { addGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
import { uuid } from 'utils/common/index';
const ImportEnvironment = ({ onClose }) => {
const dispatch = useDispatch();
const handleImportPostmanEnvironment = () => {
importPostmanEnvironment()
.then((environments) => {
environments
.filter((env) =>
env.name && env.name !== 'undefined'
? true
: () => {
toast.error('Failed to import environment: env has no name');
return false;
}
)
.map((environment) => {
let variables = environment?.variables?.map(v => ({
...v,
uid: uuid(),
type: 'text'
}));
dispatch(addGlobalEnvironment({ name: environment.name, variables }))
.then(() => {
toast.success('Global Environment imported successfully');
})
.catch(() => toast.error('An error occurred while importing the environment'));
});
})
.then(() => {
onClose();
})
.catch((err) => toastError(err, 'Postman Import environment failed'));
};
return (
<Portal>
<Modal size="sm" title="Import Global Environment" hideFooter={true} handleConfirm={onClose} handleCancel={onClose}>
<button
type="button"
onClick={handleImportPostmanEnvironment}
className="flex justify-center flex-col items-center w-full dark:bg-zinc-700 rounded-lg border-2 border-dashed border-zinc-300 dark:border-zinc-400 p-12 text-center hover:border-zinc-400 focus:outline-none focus:ring-2 focus:ring-amber-500 focus:ring-offset-2"
>
<IconDatabaseImport size={64} />
<span className="mt-2 block text-sm font-semibold">Import your Postman environments</span>
</button>
</Modal>
</Portal>
);
};
export default ImportEnvironment;

View File

@ -1,31 +0,0 @@
import React from 'react';
import Portal from 'components/Portal';
import Modal from 'components/Modal';
const ManageSecrets = ({ onClose }) => {
return (
<Portal>
<Modal size="sm" title="Manage Secrets" hideFooter={true} handleConfirm={onClose} handleCancel={onClose}>
<div>
<p>In any collection, there are secrets that need to be managed.</p>
<p className="mt-2">These secrets can be anything such as API keys, passwords, or tokens.</p>
<p className="mt-4">Bruno offers two approaches to manage secrets in collections.</p>
<p className="mt-2">
Read more about it in our{' '}
<a
href="https://docs.usebruno.com/secrets-management/overview"
target="_blank"
rel="noreferrer"
className="text-link hover:underline"
>
docs
</a>
.
</p>
</div>
</Modal>
</Portal>
);
};
export default ManageSecrets;

View File

@ -6,7 +6,7 @@ import { useFormik } from 'formik';
import { renameEnvironment } from 'providers/ReduxStore/slices/collections/actions';
import * as Yup from 'yup';
import { useDispatch } from 'react-redux';
import { renameGlobalEnvironment } from 'providers/ReduxStore/slices/globalEnvironments';
import { renameGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
const RenameEnvironment = ({ onClose, environment }) => {
const dispatch = useDispatch();

View File

@ -4,6 +4,7 @@ import CreateEnvironment from './CreateEnvironment';
import EnvironmentList from './EnvironmentList';
import StyledWrapper from './StyledWrapper';
import { IconFileAlert } from '@tabler/icons';
import ImportEnvironment from './ImportEnvironment/index';
export const SharedButton = ({ children, className, onClick }) => {
return (
@ -27,6 +28,12 @@ const DefaultTab = ({ setTab }) => {
<SharedButton onClick={() => setTab('create')}>
<span>Create Global Environment</span>
</SharedButton>
<span className="mx-4">Or</span>
<SharedButton onClick={() => setTab('import')}>
<span>Import Environment</span>
</SharedButton>
</div>
</div>
);
@ -40,9 +47,11 @@ const EnvironmentSettings = ({ globalEnvironments, activeGlobalEnvironmentUid, o
if (!environments || !environments.length) {
return (
<StyledWrapper>
<Modal size="md" title="Environments" handleCancel={onClose} hideCancel={true} hideFooter={true}>
<Modal size="md" title="Global Environments" handleCancel={onClose} hideCancel={true} hideFooter={true}>
{tab === 'create' ? (
<CreateEnvironment onClose={() => setTab('default')} />
) : tab === 'import' ? (
<ImportEnvironment onClose={() => setTab('default')} />
) : (
<></>
)}

View File

@ -23,7 +23,7 @@ import { collectionAddEnvFileEvent, openCollectionEvent } from 'providers/ReduxS
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import { isElectron } from 'utils/common/platform';
import { globalEnvironmentsUpdateEvent, updateGlobalEnvironments } from 'providers/ReduxStore/slices/globalEnvironments';
import { globalEnvironmentsUpdateEvent, updateGlobalEnvironments } from 'providers/ReduxStore/slices/global-environments';
const useIpcEvents = () => {
const dispatch = useDispatch();

View File

@ -6,7 +6,7 @@ import appReducer from './slices/app';
import collectionsReducer from './slices/collections';
import tabsReducer from './slices/tabs';
import notificationsReducer from './slices/notifications';
import globalEnvironmentsReducer from './slices/globalEnvironments';
import globalEnvironmentsReducer from './slices/global-environments';
const { publicRuntimeConfig } = getConfig();
const isDevEnv = () => {

View File

@ -1,5 +1,5 @@
import { createSlice } from '@reduxjs/toolkit';
import { generateUidBasedOnHash, stringifyIfNot, uuid } from 'utils/common/index';
import { stringifyIfNot, uuid } from 'utils/common/index';
import { environmentSchema } from '@usebruno/schema';
import { cloneDeep } from 'lodash';
@ -17,12 +17,12 @@ export const globalEnvironmentsSlice = createSlice({
state.activeGlobalEnvironmentUid = action.payload?.activeGlobalEnvironmentUid;
},
_addGlobalEnvironment: (state, action) => {
const { name, uid } = action.payload;
const { name, uid, variables = [] } = action.payload;
if (name?.length) {
state.globalEnvironments.push({
uid,
name,
variables: []
variables
});
}
},
@ -87,13 +87,13 @@ export const {
_deleteGlobalEnvironment
} = globalEnvironmentsSlice.actions;
export const addGlobalEnvironment = ({ name }) => (dispatch, getState) => {
export const addGlobalEnvironment = ({ name, variables = [] }) => (dispatch, getState) => {
return new Promise((resolve, reject) => {
const uid = generateUidBasedOnHash(name);
const uid = uuid();
ipcRenderer
.invoke('renderer:create-global-environment', { name, uid })
.invoke('renderer:create-global-environment', { name, uid, variables })
.then(
dispatch(_addGlobalEnvironment({ name, uid }))
dispatch(_addGlobalEnvironment({ name, uid, variables }))
)
.then(resolve)
.catch(reject);
@ -105,7 +105,7 @@ export const copyGlobalEnvironment = ({ name, environmentUid: baseEnvUid }) => (
const state = getState();
const globalEnvironments = state.globalEnvironments.globalEnvironments;
const baseEnv = globalEnvironments?.find(env => env?.uid == baseEnvUid)
const uid = generateUidBasedOnHash(name);
const uid = uuid();
ipcRenderer
.invoke('renderer:create-global-environment', { name, variables: baseEnv.variables })
.then(() => {
@ -195,7 +195,8 @@ export const globalEnvironmentsUpdateEvent = ({ globalEnvironmentVariables }) =>
const environment = globalEnvironments?.find(env => env?.uid == environmentUid);
if (!environment || !environmentUid) {
return reject(new Error('Environment not found'));
console.error('Global Environment not found');
return resolve();
}
let variables = cloneDeep(environment?.variables);

View File

@ -23,7 +23,7 @@ const registerPreferencesIpc = require('./ipc/preferences');
const Watcher = require('./app/watcher');
const { loadWindowState, saveBounds, saveMaximized } = require('./utils/window');
const registerNotificationsIpc = require('./ipc/notifications');
const registerGlobalEnvironmentsIpc = require('./ipc/globalEnvironments');
const registerGlobalEnvironmentsIpc = require('./ipc/global-environments');
const lastOpenedCollections = new LastOpenedCollections();

View File

@ -6,9 +6,9 @@ const registerGlobalEnvironmentsIpc = (mainWindow) => {
// GLOBAL ENVIRONMENTS
ipcMain.handle('renderer:create-global-environment', async (event, { uid, name }) => {
ipcMain.handle('renderer:create-global-environment', async (event, { uid, name, variables }) => {
try {
globalEnvironmentsStore.addGlobalEnvironment({ uid, name });
globalEnvironmentsStore.addGlobalEnvironment({ uid, name, variables });
} catch (error) {
return Promise.reject(error);
}
@ -30,9 +30,9 @@ const registerGlobalEnvironmentsIpc = (mainWindow) => {
}
});
ipcMain.handle('renderer:delete-global-environment', async (event, { uid }) => {
ipcMain.handle('renderer:delete-global-environment', async (event, { environmentUid }) => {
try {
globalEnvironmentsStore.deleteGlobalEnvironment({ uid });
globalEnvironmentsStore.deleteGlobalEnvironment({ environmentUid });
} catch (error) {
return Promise.reject(error);
}

View File

@ -17,7 +17,8 @@ const registerPreferencesIpc = (mainWindow, watcher, lastOpenedCollections) => {
// load global environments
const globalEnvironments = globalEnvironmentsStore.getGlobalEnvironments();
const activeGlobalEnvironmentUid = globalEnvironmentsStore.getActiveGlobalEnvironmentUid();
let activeGlobalEnvironmentUid = globalEnvironmentsStore.getActiveGlobalEnvironmentUid();
activeGlobalEnvironmentUid = globalEnvironments?.find(env => env?.uid == activeGlobalEnvironmentUid) ? activeGlobalEnvironmentUid : null;
mainWindow.webContents.send('main:load-global-environments', { globalEnvironments, activeGlobalEnvironmentUid });
// reload last opened collections

View File

@ -42,7 +42,6 @@ class GlobalEnvironmentsStore {
});
}
getGlobalEnvironments() {
let globalEnvironments = this.store.get('environments', []);
globalEnvironments = this.decryptGlobalEnvironmentVariables({ globalEnvironments });
@ -62,12 +61,12 @@ class GlobalEnvironmentsStore {
return this.store.set('activeGlobalEnvironmentUid', uid);
}
addGlobalEnvironment({ uid, name }) {
addGlobalEnvironment({ uid, name, variables = [] }) {
let globalEnvironments = this.getGlobalEnvironments();
globalEnvironments.push({
uid,
name,
variables: []
variables
});
this.setGlobalEnvironments(globalEnvironments);
}
@ -115,9 +114,13 @@ class GlobalEnvironmentsStore {
}
}
deleteGlobalEnvironment({ uid }) {
deleteGlobalEnvironment({ environmentUid }) {
let globalEnvironments = this.getGlobalEnvironments();
globalEnvironments = globalEnvironments.filter(env => env?.uid !== uid);
let activeGlobalEnvironmentUid = this.getActiveGlobalEnvironmentUid();
globalEnvironments = globalEnvironments.filter(env => env?.uid !== environmentUid);
if (environmentUid == activeGlobalEnvironmentUid) {
this.setActiveGlobalEnvironmentUid(null);
}
this.setGlobalEnvironments(globalEnvironments);
}
}