feat: add bulk edit mode for request headers

Closes #185
This commit is contained in:
Chris Casola 2023-10-20 13:43:11 -04:00
parent f8f38802a9
commit ad9e415eba
5 changed files with 184 additions and 84 deletions

View File

@ -136,7 +136,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
) : null}
</div>
<section
className={`flex w-full ${
className={`flex w-full flex-grow ${
['script', 'vars', 'auth', 'docs'].includes(focusedTab.requestPaneTab) ? '' : 'mt-5'
}`}
>

View File

@ -2,8 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
div.CodeMirror {
/* todo: find a better way */
height: calc(100vh - 220px);
height: 100%;
}
`;

View File

@ -30,10 +30,20 @@ const Wrapper = styled.div`
}
}
.btn-add-header {
.top-controls {
display: flex;
justify-content: right;
font-size: 0.8125rem;
}
.bottom-controls {
font-size: 0.8125rem;
}
div.CodeMirror {
height: 100%;
}
input[type='text'] {
width: 100%;
border: solid 1px transparent;

View File

@ -1,12 +1,18 @@
import React from 'react';
import React, { useState } from 'react';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import { IconTrash } from '@tabler/icons';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { useTheme } from 'providers/Theme';
import { addRequestHeader, updateRequestHeader, deleteRequestHeader } from 'providers/ReduxStore/slices/collections';
import {
addRequestHeader,
updateRequestHeader,
deleteRequestHeader,
setRequestHeaders
} from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import SingleLineEditor from 'components/SingleLineEditor';
import CodeEditor from 'components/CodeEditor';
import StyledWrapper from './StyledWrapper';
import { headers as StandardHTTPHeaders } from 'know-your-http-well';
const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
@ -14,6 +20,7 @@ const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header);
const RequestHeaders = ({ item, collection }) => {
const dispatch = useDispatch();
const { storedTheme } = useTheme();
const preferences = useSelector((state) => state.app.preferences);
const headers = item.draft ? get(item, 'draft.request.headers') : get(item, 'request.headers');
const addHeader = () => {
@ -62,8 +69,66 @@ const RequestHeaders = ({ item, collection }) => {
);
};
const [bulkEdit, setBulkEdit] = useState(false);
const [bulkText, setBulkText] = useState('');
const handleBulkEdit = (value) => {
setBulkText(value);
const keyValPairs = value
.split(/\r?\n/)
.map((pair) => {
const sep = pair.indexOf(':');
if (sep < 0) {
return [];
}
return [pair.slice(0, sep).trim(), pair.slice(sep + 1).trim()];
})
.filter((pair) => pair.length === 2);
dispatch(
setRequestHeaders({
collectionUid: collection.uid,
itemUid: item.uid,
headers: keyValPairs.map(([name, value]) => ({
name,
value
}))
})
);
};
const toggleBulkEdit = () => {
if (!bulkEdit) {
setBulkText(
headers
.filter((header) => header.enabled)
.map((header) => `${header.name}: ${header.value}`)
.join('\n')
);
}
setBulkEdit(!bulkEdit);
};
return (
<StyledWrapper className="w-full">
<StyledWrapper className="w-full h-full flex flex-col flex-grow">
<div className="top-controls mb-3">
<button className="text-link select-none" onClick={toggleBulkEdit}>
{bulkEdit ? 'Key/Value Edit' : 'Bulk Edit'}
</button>
</div>
{bulkEdit && (
<div className="bulk-editor flex-grow">
<CodeEditor
mode="application/text"
theme={storedTheme}
font={get(preferences, 'font.codeFont', 'default')}
value={bulkText}
onEdit={handleBulkEdit}
/>
</div>
)}
{!bulkEdit && (
<table>
<thead>
<tr>
@ -138,9 +203,14 @@ const RequestHeaders = ({ item, collection }) => {
: null}
</tbody>
</table>
<button className="btn-add-header text-link pr-2 py-3 mt-2 select-none" onClick={addHeader}>
)}
<div className="bottom-controls py-3 mt-2">
{!bulkEdit && (
<button className="text-link pr-3 select-none" onClick={addHeader}>
+ Add Header
</button>
)}
</div>
</StyledWrapper>
);
};

View File

@ -532,6 +532,26 @@ export const collectionsSlice = createSlice({
}
}
},
setRequestHeaders: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
if (collection) {
const item = findItemInCollection(collection, action.payload.itemUid);
if (item && isItemARequest(item)) {
if (!item.draft) {
item.draft = cloneDeep(item);
}
item.draft.request.headers = map(action.payload.headers, (header) => ({
uid: uuid(),
name: header.name,
value: header.value,
description: '',
enabled: true
}));
}
}
},
addFormUrlEncodedParam: (state, action) => {
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
@ -1390,6 +1410,7 @@ export const {
addRequestHeader,
updateRequestHeader,
deleteRequestHeader,
setRequestHeaders,
addFormUrlEncodedParam,
updateFormUrlEncodedParam,
deleteFormUrlEncodedParam,