feat(#304) Environments color 🎨 (#1053)

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* move dependency to appropriate package.json

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* use border instead of background color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* simplify onColorChange

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* add black, keep backgound on unselected color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* fix conflicts

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

* associate environment to a color

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use StyledWrapper

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

don't save anything for color if it is not set

Signed-off-by: mathieu <mathieu.dreano@gmail.com>

use redux store instead of local state

remove logs

fix selectedEnvironment

cleanup

add bottom border on active tab

# Conflicts:
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/StyledWrapper.js
#	packages/bruno-app/src/components/Environments/EnvironmentSelector/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/index.js
#	packages/bruno-app/src/components/Environments/EnvironmentSettings/index.js
#	packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js

* Update packages/bruno-app/src/components/Environments/EnvironmentSettings/EnvironmentList/EnvironmentDetails/EnvironmentColor/index.js

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* unused selectedEnvironment prop in EnvironmentList

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* RequestTab, avoid unnecessary call if undefined activeCollection

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

* use @uiw/reac-color instead of react-color

Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>

---------

Signed-off-by: mathieu <mathieu.dreano@gmail.com>
Signed-off-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Mathieu D <mathieu.dreano@decathlon.com>
Co-authored-by: Anoop M D <anoop@usebruno.com>
Co-authored-by: Mathieu DREANO <122891400+mdreano@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Mathieu DREANO
2026-01-29 11:51:39 +01:00
committed by Sid
parent 214e1434e5
commit 5a6714f085
15 changed files with 612 additions and 24 deletions

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { useCallback } from 'react';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import { saveEnvironmentColor } from 'providers/ReduxStore/slices/collections/actions';
import { Circle } from '@uiw/react-color';
const EnvironmentColor = ({ environment, collectionUid }) => {
const dispatch = useDispatch();
const onColorChange = useCallback(
(color) => {
if (color == environment.color) return;
dispatch(saveEnvironmentColor(color, environment.uid, collectionUid))
.then(() => toast.success('Environment color changed successfully'))
.catch(() => toast.error('An error occurred while changing the environment color'));
},
[dispatch, environment.uid, environment.color, collectionUid]
);
return (
<Circle
id="environment-color"
style={{ gap: 3 }}
pointProps={{ style: { width: 14, height: 14, borderRadius: 10 } }}
colors={['#000000','#9c27b0','#3f51b5','#03a9f4','#009688','#8bc34a','#ffeb3b','#ff9800','#ff5722','#795548','#607d8b']}
color={environment.color}
onChange={(color) => onColorChange(color.hex)}
/>
);
};
export default EnvironmentColor;

View File

@@ -7,6 +7,8 @@ import toast from 'react-hot-toast';
import CopyEnvironment from 'components/Environments/EnvironmentSettings/CopyEnvironment';
import DeleteEnvironment from 'components/Environments/EnvironmentSettings/DeleteEnvironment';
import EnvironmentVariables from './EnvironmentVariables';
import EnvironmentColor from '../EnvironmentDetails/EnvironmentColor';
import ToolHint from 'components/ToolHint/index';
import StyledWrapper from './StyledWrapper';
const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
@@ -119,7 +121,6 @@ const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
{openCopyModal && (
<CopyEnvironment onClose={() => setOpenCopyModal(false)} environment={environment} collection={collection} />
)}
<div className="header">
<div className={`title-container ${isRenaming ? 'renaming' : ''}`}>
{isRenaming ? (
@@ -174,9 +175,8 @@ const EnvironmentDetails = ({ environment, setIsModified, collection }) => {
</div>
</div>
<div className="content">
<EnvironmentVariables environment={environment} setIsModified={setIsModified} collection={collection} />
</div>
<EnvironmentColor environment={environment} collectionUid={collection.uid} />
<EnvironmentVariables environment={environment} collection={collection} setIsModified={setIsModified} onClose={onClose} />
</StyledWrapper>
);
};

View File

@@ -1,4 +1,5 @@
import React, { useEffect, useState, useRef } from 'react';
import { findEnvironmentInCollection, findItem } from 'utils/collections';
import usePrevious from 'hooks/usePrevious';
import EnvironmentDetails from './EnvironmentDetails';
import CreateEnvironment from 'components/Environments/EnvironmentSettings/CreateEnvironment';
@@ -24,6 +25,10 @@ const EnvironmentList = ({
}) => {
const dispatch = useDispatch();
const EnvironmentList = ({ collection, isModified, setIsModified, onClose, setShowExportModal }) => {
const { environments, activeEnvironmentUid } = collection;
const [selectedEnvironment, setSelectedEnvironment] = useState(null);
const [openCreateModal, setOpenCreateModal] = useState(false);
const [openImportModal, setOpenImportModal] = useState(false);
const [searchText, setSearchText] = useState('');
@@ -38,7 +43,7 @@ const EnvironmentList = ({
const [switchEnvConfirmClose, setSwitchEnvConfirmClose] = useState(false);
const [originalEnvironmentVariables, setOriginalEnvironmentVariables] = useState([]);
const envUids = environments ? environments.map((env) => env.uid) : [];
const envUids = environments?.map((env) => env.uid) ?? [];
const prevEnvUids = usePrevious(envUids);
useEffect(() => {
@@ -63,10 +68,12 @@ const EnvironmentList = ({
if (hasSelectedEnvironmentChanged || selectedEnvironment.uid !== _selectedEnvironment?.uid) {
setSelectedEnvironment(_selectedEnvironment);
}
setOriginalEnvironmentVariables(_selectedEnvironment?.variables || []);
setOriginalEnvironmentVariables(selectedEnvironment?.variables||[]);
setSelectedEnvironment(findItem(environments, selectedEnvironment.uid));
return;
}
const environment = environments?.find((env) => env.uid === activeEnvironmentUid) || environments?.[0];
setSelectedEnvironment(environment);
@@ -74,15 +81,21 @@ const EnvironmentList = ({
}, [environments, activeEnvironmentUid, selectedEnvironment]);
useEffect(() => {
if (prevEnvUids && prevEnvUids.length && envUids.length > prevEnvUids.length) {
if (selectedEnvironment) {
setSelectedEnvironment(findEnvironmentInCollection(collection, selectedEnvironment.uid));
}
}, [environments]);
useEffect(() => {
if (prevEnvUids?.length && envUids.length > prevEnvUids.length) {
const newEnv = environments.find((env) => !prevEnvUids.includes(env.uid));
if (newEnv) {
setSelectedEnvironment(newEnv);
}
}
if (prevEnvUids && prevEnvUids.length && envUids.length < prevEnvUids.length) {
setSelectedEnvironment(environments && environments.length ? environments[0] : null);
if (prevEnvUids?.length && envUids.length < prevEnvUids.length) {
setSelectedEnvironment(environments?.length ? environments[0] : null);
}
}, [envUids, environments, prevEnvUids]);