diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json
index 02083ec2a..6639d6574 100644
--- a/packages/bruno-app/package.json
+++ b/packages/bruno-app/package.json
@@ -26,7 +26,7 @@
"graphql": "^16.2.0",
"graphql-request": "^3.7.0",
"idb": "^7.0.0",
- "immer": "^9.0.12",
+ "immer": "^9.0.15",
"lodash": "^4.17.21",
"mousetrap": "^1.6.5",
"nanoid": "3.3.4",
diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
new file mode 100644
index 000000000..279e3b9c4
--- /dev/null
+++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/StyledWrapper.js
@@ -0,0 +1,45 @@
+import styled from 'styled-components';
+
+const Wrapper = styled.div`
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ font-weight: 600;
+
+ thead, td {
+ border: 1px solid #efefef;
+ }
+
+ thead {
+ color: #616161;
+ font-size: 0.8125rem;
+ user-select: none;
+ }
+ td {
+ padding: 6px 10px;
+ }
+ }
+
+ .btn-add-param {
+ font-size: 0.8125rem;
+ }
+
+ input[type="text"] {
+ width: 100%;
+ border: solid 1px transparent;
+ outline: none !important;
+
+ &:focus{
+ outline: none !important;
+ border: solid 1px transparent;
+ }
+ }
+
+ input[type="checkbox"] {
+ cursor: pointer;
+ position: relative;
+ top: 1px;
+ }
+`;
+
+export default Wrapper;
diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
new file mode 100644
index 000000000..ca50653b6
--- /dev/null
+++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/index.js
@@ -0,0 +1,127 @@
+import React, { useReducer } from 'react';
+import toast from 'react-hot-toast';
+import cloneDeep from 'lodash/cloneDeep';
+import { IconTrash } from '@tabler/icons';
+import { useDispatch } from 'react-redux';
+import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
+import reducer from './reducer';
+import StyledWrapper from './StyledWrapper';
+
+const EnvironmentVariables = ({environment, collection}) => {
+ const dispatch = useDispatch();
+ const [state, reducerDispatch] = useReducer(reducer, {hasChanges: false, variables: environment.variables || []});
+ const {
+ variables,
+ hasChanges
+ } = state;
+
+ const saveChanges = () => {
+ dispatch(saveEnvironment(cloneDeep(variables), environment.uid, collection.uid))
+ .then(() => {
+ toast.success("Changes saved successfully");
+ reducerDispatch({
+ type: 'CHANGES_SAVED'
+ });
+ })
+ .catch(() => toast.error("An error occured while saving the changes"));
+ };
+
+ const addVariable = () => {
+ reducerDispatch({
+ type: 'ADD_VAR'
+ });
+ };
+
+ const handleVarChange = (e, _variable, type) => {
+ const variable = cloneDeep(_variable);
+ switch(type) {
+ case 'name' : {
+ variable.name = e.target.value;
+ break;
+ }
+ case 'value' : {
+ variable.value = e.target.value;
+ break;
+ }
+ case 'enabled' : {
+ variable.enabled = e.target.checked;
+ break;
+ }
+ }
+ reducerDispatch({
+ type: 'UPDATE_VAR',
+ variable
+ });
+ };
+
+ const handleRemoveVars = (variable) => {
+ reducerDispatch({
+ type: 'DELETE_VAR',
+ variable
+ });
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+};
+export default EnvironmentVariables;
\ No newline at end of file
diff --git a/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/reducer.js b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/reducer.js
new file mode 100644
index 000000000..5b2457ea5
--- /dev/null
+++ b/packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentVariables/reducer.js
@@ -0,0 +1,50 @@
+import produce from 'immer';
+import find from 'lodash/find';
+import filter from 'lodash/filter';
+import { uuid } from 'utils/common';
+
+const reducer = (state, action) => {
+ switch (action.type) {
+ case 'ADD_VAR': {
+ return produce(state, (draft) => {
+ draft.variables.push({
+ uid: uuid(),
+ name: '',
+ value: '',
+ type: 'text',
+ enabled: true
+ });
+ draft.hasChanges = true;
+ });
+ }
+
+ case 'UPDATE_VAR': {
+ return produce(state, (draft) => {
+ const variable = find(draft.variables, (v) => v.uid === action.variable.uid);
+ variable.name = action.variable.name;
+ variable.value = action.variable.value;
+ variable.enabled = action.variable.enabled;
+ draft.hasChanges = true;
+ });
+ }
+
+ case 'DELETE_VAR': {
+ return produce(state, (draft) => {
+ draft.variables = filter(draft.variables, (v) => v.uid !== action.variable.uid);
+ draft.hasChanges = true;
+ });
+ }
+
+ case 'CHANGES_SAVED': {
+ return produce(state, (draft) => {
+ draft.hasChanges = false;
+ });
+ }
+
+ default: {
+ return state;
+ }
+ }
+};
+
+export default reducer;
\ No newline at end of file
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 766bba05d..1b2beafeb 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,23 +1,32 @@
import React, {useState } from "react";
-import { IconEdit, IconTrash } from "@tabler/icons";
+import { IconEdit, IconTrash, IconDatabase } from "@tabler/icons";
+import EnvironmentVariables from './EnvironmentVariables';
import RenameEnvironment from "../../RenameEnvironment";
import DeleteEnvironment from "../../DeleteEnvironment";
const EnvironmentDetails = ({environment, collection}) => {
const [ openEditModal, setOpenEditModal] = useState(false);
const [ openDeleteModal, setOpenDeleteModal] = useState(false);
+ console.log(environment);
return (
-
+
{openEditModal &&
setOpenEditModal(false)} environment={environment} collection={collection}/>}
{openDeleteModal && setOpenDeleteModal(false)} environment={environment} collection={collection}/>}
-
-
{environment.name}
-
+
+
+
+ {environment.name}
+
+
setOpenEditModal(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 4b286bf66..4c9c90ece 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
@@ -35,6 +35,7 @@ import {
addEnvironment as _addEnvironment,
renameEnvironment as _renameEnvironment,
deleteEnvironment as _deleteEnvironment,
+ saveEnvironment as _saveEnvironment,
createCollection as _createCollection,
renameCollection as _renameCollection,
deleteCollection as _deleteCollection,
@@ -691,6 +692,33 @@ export const deleteEnvironment = (environmentUid, collectionUid) => (dispatch,
});
};
+export const saveEnvironment = (variables, environmentUid, 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 collectionCopy = cloneDeep(collection);
+ const environment = findEnvironmentInCollection(collectionCopy, environmentUid);
+ if(!environment) {
+ return reject(new Error('Environment not found'));
+ }
+
+ environment.variables = variables;
+
+ const collectionToSave = transformCollectionToSaveToIdb(collectionCopy);
+
+ collectionSchema
+ .validate(collectionToSave)
+ .then(() => saveCollectionToIdb(window.__idb, collectionToSave))
+ .then(() => dispatch(_saveEnvironment({variables, environmentUid, collectionUid})))
+ .then(resolve)
+ .catch(reject);
+ });
+};
+
export const removeLocalCollection = (collectionUid) => (dispatch, getState) => {
return new Promise((resolve, reject) => {
const state = getState();
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
index a33600e1a..51ff9bbd9 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -103,6 +103,18 @@ export const collectionsSlice = createSlice({
}
}
},
+ saveEnvironment: (state, action) => {
+ const { variables, environmentUid, collectionUid } = action.payload;
+ const collection = findCollectionByUid(state.collections, collectionUid);
+
+ if(collection) {
+ const environment = findEnvironmentInCollection(collection, environmentUid);
+
+ if(environment) {
+ environment.variables = variables;
+ }
+ }
+ },
newItem: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
@@ -727,6 +739,7 @@ export const {
addEnvironment,
renameEnvironment,
deleteEnvironment,
+ saveEnvironment,
newItem,
deleteItem,
renameItem,