add: presets in collection setting (#6389)

* add: presets in collection setting

* fix

* add: websocket in preset

* fix: htmlFor
This commit is contained in:
Pooja
2025-12-16 17:16:41 +05:30
committed by GitHub
parent fdff792476
commit dc111ecce2
6 changed files with 227 additions and 2 deletions

View File

@@ -0,0 +1,29 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
max-width: 800px;
.settings-label {
width: 110px;
}
.textbox {
border: 1px solid #ccc;
padding: 0.15rem 0.45rem;
box-shadow: none;
border-radius: 0px;
outline: none;
box-shadow: none;
transition: border-color ease-in-out 0.1s;
border-radius: 3px;
background-color: ${(props) => props.theme.modal.input.bg};
border: 1px solid ${(props) => props.theme.modal.input.border};
&:focus {
border: solid 1px ${(props) => props.theme.modal.input.focusBorder} !important;
outline: none !important;
}
}
`;
export default StyledWrapper;

View File

@@ -0,0 +1,134 @@
import React from 'react';
import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
import { updateCollectionPresets } from 'providers/ReduxStore/slices/collections';
import { saveCollectionSettings } from 'providers/ReduxStore/slices/collections/actions';
import { get } from 'lodash';
const PresetsSettings = ({ collection }) => {
const dispatch = useDispatch();
const initialPresets = { requestType: 'http', requestUrl: '' };
// Get presets from draft.brunoConfig if it exists, otherwise from brunoConfig
const currentPresets = collection.draft?.brunoConfig
? get(collection, 'draft.brunoConfig.presets', initialPresets)
: get(collection, 'brunoConfig.presets', initialPresets);
// Helper to update presets config
const updatePresets = (updates) => {
const updatedPresets = { ...currentPresets, ...updates };
dispatch(updateCollectionPresets({
collectionUid: collection.uid,
presets: updatedPresets
}));
};
const handleSave = () => dispatch(saveCollectionSettings(collection.uid));
const handleRequestTypeChange = (e) => {
updatePresets({ requestType: e.target.value });
};
const handleRequestUrlChange = (e) => {
updatePresets({ requestUrl: e.target.value });
};
return (
<StyledWrapper className="h-full w-full">
<div className="text-xs mb-4 mt-4 text-muted">
These presets will be used as the default values for new requests in this collection.
</div>
<div className="bruno-form">
<div className="mb-3 flex items-center">
<label className="settings-label flex items-center" htmlFor="http">
Request Type
</label>
<div className="flex items-center">
<input
id="http"
className="cursor-pointer"
type="radio"
name="requestType"
onChange={handleRequestTypeChange}
value="http"
checked={(currentPresets.requestType || 'http') === 'http'}
/>
<label htmlFor="http" className="ml-1 cursor-pointer select-none">
HTTP
</label>
<input
id="graphql"
className="ml-4 cursor-pointer"
type="radio"
name="requestType"
onChange={handleRequestTypeChange}
value="graphql"
checked={(currentPresets.requestType || 'http') === 'graphql'}
/>
<label htmlFor="graphql" className="ml-1 cursor-pointer select-none">
GraphQL
</label>
<input
id="grpc"
className="ml-4 cursor-pointer"
type="radio"
name="requestType"
onChange={handleRequestTypeChange}
value="grpc"
checked={(currentPresets.requestType || 'http') === 'grpc'}
/>
<label htmlFor="grpc" className="ml-1 cursor-pointer select-none">
gRPC
</label>
<input
id="ws"
className="ml-4 cursor-pointer"
type="radio"
name="requestType"
onChange={handleRequestTypeChange}
value="ws"
checked={(currentPresets.requestType || 'http') === 'ws'}
/>
<label htmlFor="ws" className="ml-1 cursor-pointer select-none">
WebSocket
</label>
</div>
</div>
<div className="mb-3 flex items-center">
<label className="settings-label" htmlFor="request-url">
Base URL
</label>
<div className="flex items-center w-full">
<div className="flex items-center flex-grow input-container h-full">
<input
id="request-url"
type="text"
name="requestUrl"
placeholder="Request URL"
className="block textbox"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={handleRequestUrlChange}
value={currentPresets.requestUrl || ''}
style={{ width: '100%' }}
/>
</div>
</div>
</div>
<div className="mt-6">
<button type="button" className="submit btn btn-sm btn-secondary" onClick={handleSave}>
Save
</button>
</div>
</div>
</StyledWrapper>
);
};
export default PresetsSettings;

View File

@@ -9,6 +9,7 @@ import Headers from './Headers';
import Auth from './Auth';
import Script from './Script';
import Test from './Tests';
import Presets from './Presets';
import Protobuf from './Protobuf';
import StyledWrapper from './StyledWrapper';
import Vars from './Vars/index';
@@ -58,6 +59,8 @@ const CollectionSettings = ({ collection }) => {
const protobufConfig = collection.draft?.brunoConfig
? get(collection, 'draft.brunoConfig.protobuf', {})
: get(collection, 'brunoConfig.protobuf', {});
const presets = collection.draft?.brunoConfig ? get(collection, 'draft.brunoConfig.presets', {}) : get(collection, 'brunoConfig.presets', {});
const hasPresets = presets && presets.requestUrl !== '';
const getTabPanel = (tab) => {
switch (tab) {
@@ -79,6 +82,9 @@ const CollectionSettings = ({ collection }) => {
case 'tests': {
return <Test collection={collection} />;
}
case 'presets': {
return <Presets collection={collection} />;
}
case 'proxy': {
return <ProxySettings collection={collection} />;
}
@@ -123,6 +129,10 @@ const CollectionSettings = ({ collection }) => {
Tests
{hasTests && <StatusDot />}
</div>
<div className={getTabClassname('presets')} role="tab" onClick={() => setTab('presets')}>
Presets
{hasPresets && <StatusDot />}
</div>
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
Proxy
{Object.keys(proxyConfig).length > 0 && proxyEnabled && <StatusDot />}

View File

@@ -1,4 +1,5 @@
import React, { useRef, useEffect, useCallback, forwardRef, useState } from 'react';
import get from 'lodash/get';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import toast from 'react-hot-toast';
@@ -29,6 +30,11 @@ const NewRequest = ({ collectionUid, item, isEphemeral, onClose }) => {
const storedTheme = useTheme();
const collection = useSelector((state) => state.collections.collections?.find((c) => c.uid === collectionUid));
const collectionPresets = get(
collection,
collection?.draft?.brunoConfig ? 'draft.brunoConfig.presets' : 'brunoConfig.presets',
{}
);
const [curlRequestTypeDetected, setCurlRequestTypeDetected] = useState(null);
const [showFilesystemName, toggleShowFilesystemName] = useState(false);
@@ -69,13 +75,41 @@ const NewRequest = ({ collectionUid, item, isEphemeral, onClose }) => {
const [isEditing, toggleEditing] = useState(false);
const getRequestType = (collectionPresets) => {
if (!collectionPresets || !collectionPresets.requestType) {
return 'http-request';
}
// Note: Why different labels for the same thing?
// http-request and graphql-request are used inside the app's json representation of a request
// http and graphql are used in Bru DSL as well as collection exports
// We need to eventually standardize the app's DSL to use the same labels as bru DSL
if (collectionPresets.requestType === 'http') {
return 'http-request';
}
if (collectionPresets.requestType === 'graphql') {
return 'graphql-request';
}
if (collectionPresets.requestType === 'grpc') {
return 'grpc-request';
}
if (collectionPresets.requestType === 'ws') {
return 'ws-request';
}
return 'http-request';
};
const formik = useFormik({
enableReinitialize: true,
initialValues: {
requestName: '',
filename: '',
requestType: 'http-request',
requestUrl: '',
requestType: getRequestType(collectionPresets),
requestUrl: collectionPresets.requestUrl || '',
requestMethod: 'GET',
curlCommand: ''
},

View File

@@ -41,6 +41,7 @@ const actionsToIntercept = [
'collections/moveVar',
'collections/updateRequestDocs',
'collections/runRequestEvent',
'collections/updateCollectionPresets',
// Folder-level actions
'collections/addFolderHeader',

View File

@@ -2080,6 +2080,22 @@ export const collectionsSlice = createSlice({
set(collection, 'draft.brunoConfig.clientCertificates', action.payload.clientCertificates);
}
},
updateCollectionPresets: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
if (collection) {
if (!collection.draft) {
collection.draft = {
root: cloneDeep(collection.root),
brunoConfig: cloneDeep(collection.brunoConfig)
};
}
if (!collection.draft.brunoConfig) {
collection.draft.brunoConfig = cloneDeep(collection.brunoConfig);
}
set(collection, 'draft.brunoConfig.presets', action.payload.presets);
}
},
updateCollectionProtobuf: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
@@ -3450,6 +3466,7 @@ export const {
updateCollectionDocs,
updateCollectionProxy,
updateCollectionClientCertificates,
updateCollectionPresets,
updateCollectionProtobuf,
collectionAddFileEvent,
collectionAddDirectoryEvent,