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 ( + + + + + + + + + + + {variables && variables.length ? variables.map((variable, index) => { + return ( + + + + + + ); + }) : null} + +
NameValue
+ handleVarChange(e, variable, 'name')} + /> + + handleVarChange(e, variable, 'value')} + /> + +
+ handleVarChange(e, variable, 'enabled')} + /> + +
+
+ +
+ +
+ +
+ +
+
+ ) +}; +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,