feat: prettier config

This commit is contained in:
Anoop M D 2022-10-20 15:09:30 +05:30
parent 93544f8ae6
commit ba219d66db
160 changed files with 3029 additions and 2830 deletions

View File

@ -0,0 +1,7 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 180
}

View File

@ -5,16 +5,17 @@
"dev": "next dev",
"build": "next build && next export",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\""
},
"dependencies": {
"@usebruno/schema": "0.1.0",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.16",
"@reduxjs/toolkit": "^1.8.0",
"@tabler/icons": "^1.46.0",
"@tippyjs/react": "^4.2.6",
"@usebruno/schema": "0.1.0",
"axios": "^0.26.0",
"classnames": "^2.3.1",
"codemirror": "^5.65.2",
@ -57,6 +58,7 @@
"html-loader": "^3.0.1",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.4.5",
"prettier": "^2.7.1",
"style-loader": "^3.3.1",
"webpack": "^5.64.4",
"webpack-cli": "^4.9.1"

View File

@ -6,12 +6,13 @@ const AuthApi = {
signup: (params) => post('auth/v1/user/signup', params),
login: (params) => {
return new Promise((resolve, reject) => {
const { ipcRenderer } = window.require("electron");
const { ipcRenderer } = window.require('electron');
ipcRenderer.invoke('bruno-account-request', {
ipcRenderer
.invoke('bruno-account-request', {
data: params,
method: 'POST',
url: `${process.env.NEXT_PUBLIC_BRUNO_SERVER_API}/auth/v1/user/login`,
url: `${process.env.NEXT_PUBLIC_BRUNO_SERVER_API}/auth/v1/user/login`
})
.then(resolve)
.catch(reject);

View File

@ -1,22 +1,25 @@
import axios from "axios";
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_GRAFNODE_SERVER_API
});
apiClient.interceptors.request.use((config) => {
apiClient.interceptors.request.use(
(config) => {
const headers = {
'Content-Type': 'application/json'
};
return ({
return {
...config,
headers: headers
});
}, error => Promise.reject(error));
};
},
(error) => Promise.reject(error)
);
apiClient.interceptors.response.use((response) =>
response,
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
return Promise.reject(error.response ? error.response.data : error);
}

View File

@ -4,27 +4,91 @@ const Bruno = ({width}) => {
return (
<svg id="emoji" width={width} viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
<g id="color">
<path fill="#F4AA41" stroke="none" d="M23.5,14.5855l-4.5,1.75l-7.25,8.5l-4.5,10.75l2,5.25c1.2554,3.7911,3.5231,7.1832,7.25,10l2.5-3.3333 c0,0,3.8218,7.7098,10.7384,8.9598c0,0,10.2616,1.936,15.5949-0.8765c3.4203-1.8037,4.4167-4.4167,4.4167-4.4167l3.4167-3.4167 l1.5833,2.3333l2.0833-0.0833l5.4167-7.25L64,37.3355l-0.1667-4.5l-2.3333-5.5l-4.8333-7.4167c0,0-2.6667-4.9167-8.1667-3.9167 c0,0-6.5-4.8333-11.8333-4.0833S32.0833,10.6688,23.5,14.5855z"/>
<polygon fill="#EA5A47" stroke="none" points="36,47.2521 32.9167,49.6688 30.4167,49.6688 30.3333,53.5021 31.0833,57.0021 32.1667,58.9188 35,60.4188 39.5833,59.8355 41.1667,58.0855 42.1667,53.8355 41.9167,49.8355 39.9167,50.0855"/>
<path
fill="#F4AA41"
stroke="none"
d="M23.5,14.5855l-4.5,1.75l-7.25,8.5l-4.5,10.75l2,5.25c1.2554,3.7911,3.5231,7.1832,7.25,10l2.5-3.3333 c0,0,3.8218,7.7098,10.7384,8.9598c0,0,10.2616,1.936,15.5949-0.8765c3.4203-1.8037,4.4167-4.4167,4.4167-4.4167l3.4167-3.4167 l1.5833,2.3333l2.0833-0.0833l5.4167-7.25L64,37.3355l-0.1667-4.5l-2.3333-5.5l-4.8333-7.4167c0,0-2.6667-4.9167-8.1667-3.9167 c0,0-6.5-4.8333-11.8333-4.0833S32.0833,10.6688,23.5,14.5855z"
/>
<polygon
fill="#EA5A47"
stroke="none"
points="36,47.2521 32.9167,49.6688 30.4167,49.6688 30.3333,53.5021 31.0833,57.0021 32.1667,58.9188 35,60.4188 39.5833,59.8355 41.1667,58.0855 42.1667,53.8355 41.9167,49.8355 39.9167,50.0855"
/>
<polygon fill="#3F3F3F" stroke="none" points="32.5,36.9188 30.9167,40.6688 33.0833,41.9188 34.3333,42.4188 38.6667,42.5855 41.5833,40.3355 39.8333,37.0855" />
</g>
<g id="hair" />
<g id="skin" />
<g id="skin-shadow" />
<g id="line">
<path fill="#000000" stroke="none" d="M29.5059,30.1088c0,0-1.8051,1.2424-2.7484,0.6679c-0.9434-0.5745-1.2424-1.8051-0.6679-2.7484 s1.805-1.2424,2.7484-0.6679S29.5059,30.1088,29.5059,30.1088z"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M33.1089,37.006h6.1457c0.4011,0,0.7634,0.2397,0.9203,0.6089l1.1579,2.7245l-2.1792,1.1456 c-0.6156,0.3236-1.3654-0.0645-1.4567-0.754"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M34.7606,40.763c-0.1132,0.6268-0.7757,0.9895-1.3647,0.7471l-2.3132-0.952l1.0899-2.9035 c0.1465-0.3901,0.5195-0.6486,0.9362-0.6486"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M30.4364,50.0268c0,0-0.7187,8.7934,3.0072,9.9375c2.6459,0.8125,5.1497,0.5324,6.0625-0.25 c0.875-0.75,2.6323-4.4741,1.8267-9.6875"/>
<path fill="#000000" stroke="none" d="M44.2636,30.1088c0,0,1.805,1.2424,2.7484,0.6679c0.9434-0.5745,1.2424-1.8051,0.6679-2.7484 c-0.5745-0.9434-1.805-1.2424-2.7484-0.6679C43.9881,27.9349,44.2636,30.1088,44.2636,30.1088z"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M25.6245,42.8393c-0.475,3.6024,2.2343,5.7505,4.2847,6.8414c1.1968,0.6367,2.6508,0.5182,3.7176-0.3181l2.581-2.0233l2.581,2.0233 c1.0669,0.8363,2.5209,0.9548,3.7176,0.3181c2.0504-1.0909,4.7597-3.239,4.2847-6.8414"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M19.9509,28.3572c-2.3166,5.1597-0.5084,13.0249,0.119,15.3759c0.122,0.4571,0.0755,0.9355-0.1271,1.3631l-1.9874,4.1937 c-0.623,1.3146-2.3934,1.5533-3.331,0.4409c-3.1921-3.7871-8.5584-11.3899-6.5486-16.686 c7.0625-18.6104,15.8677-18.1429,15.8677-18.1429c2.8453-1.9336,13.1042-6.9375,24.8125,0.875c0,0,8.6323-1.7175,14.9375,16.9375 c1.8036,5.3362-3.4297,12.8668-6.5506,16.6442c-0.9312,1.127-2.7162,0.8939-3.3423-0.4272l-1.9741-4.1656 c-0.2026-0.4275-0.2491-0.906-0.1271-1.3631c0.6275-2.3509,2.4356-10.2161,0.119-15.3759"/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M52.6309,46.4628c0,0-3.0781,6.7216-7.8049,8.2712"/>
<path
fill="#000000"
stroke="none"
d="M29.5059,30.1088c0,0-1.8051,1.2424-2.7484,0.6679c-0.9434-0.5745-1.2424-1.8051-0.6679-2.7484 s1.805-1.2424,2.7484-0.6679S29.5059,30.1088,29.5059,30.1088z"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M33.1089,37.006h6.1457c0.4011,0,0.7634,0.2397,0.9203,0.6089l1.1579,2.7245l-2.1792,1.1456 c-0.6156,0.3236-1.3654-0.0645-1.4567-0.754"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M34.7606,40.763c-0.1132,0.6268-0.7757,0.9895-1.3647,0.7471l-2.3132-0.952l1.0899-2.9035 c0.1465-0.3901,0.5195-0.6486,0.9362-0.6486"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M30.4364,50.0268c0,0-0.7187,8.7934,3.0072,9.9375c2.6459,0.8125,5.1497,0.5324,6.0625-0.25 c0.875-0.75,2.6323-4.4741,1.8267-9.6875"
/>
<path
fill="#000000"
stroke="none"
d="M44.2636,30.1088c0,0,1.805,1.2424,2.7484,0.6679c0.9434-0.5745,1.2424-1.8051,0.6679-2.7484 c-0.5745-0.9434-1.805-1.2424-2.7484-0.6679C43.9881,27.9349,44.2636,30.1088,44.2636,30.1088z"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M25.6245,42.8393c-0.475,3.6024,2.2343,5.7505,4.2847,6.8414c1.1968,0.6367,2.6508,0.5182,3.7176-0.3181l2.581-2.0233l2.581,2.0233 c1.0669,0.8363,2.5209,0.9548,3.7176,0.3181c2.0504-1.0909,4.7597-3.239,4.2847-6.8414"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M19.9509,28.3572c-2.3166,5.1597-0.5084,13.0249,0.119,15.3759c0.122,0.4571,0.0755,0.9355-0.1271,1.3631l-1.9874,4.1937 c-0.623,1.3146-2.3934,1.5533-3.331,0.4409c-3.1921-3.7871-8.5584-11.3899-6.5486-16.686 c7.0625-18.6104,15.8677-18.1429,15.8677-18.1429c2.8453-1.9336,13.1042-6.9375,24.8125,0.875c0,0,8.6323-1.7175,14.9375,16.9375 c1.8036,5.3362-3.4297,12.8668-6.5506,16.6442c-0.9312,1.127-2.7162,0.8939-3.3423-0.4272l-1.9741-4.1656 c-0.2026-0.4275-0.2491-0.906-0.1271-1.3631c0.6275-2.3509,2.4356-10.2161,0.119-15.3759"
/>
<path
fill="none"
stroke="#000000"
strokeLinecap="round"
strokeLinejoin="round"
strokeMiterlimit="10"
strokeWidth="2"
d="M52.6309,46.4628c0,0-3.0781,6.7216-7.8049,8.2712"
/>
<path fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" d="M19.437,46.969c0,0,3.0781,6.0823,7.8049,7.632" />
<line x1="36.2078" x2="36.2078" y1="47.3393" y2="44.3093" fill="none" stroke="#000000" strokeLinecap="round" strokeLinejoin="round" strokeMiterlimit="10" strokeWidth="2" />
</g>
</svg>
)
);
};
export default Bruno;

View File

@ -1,40 +1,36 @@
import React from "react";
import Modal from "components/Modal/index";
import {IconSpeakerphone, IconBrandTwitter} from "@tabler/icons";
import StyledWrapper from "./StyledWrapper";
import React from 'react';
import Modal from 'components/Modal/index';
import { IconSpeakerphone, IconBrandTwitter } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
import GithubSvg from 'assets/github.svg';
const BrunoSupport = ({ onClose }) => {
return (
<StyledWrapper>
<Modal
size="sm"
title={"Support"}
handleCancel={onClose}
hideFooter={true}
>
<Modal size="sm" title={'Support'} handleCancel={onClose} hideFooter={true}>
<div className="collection-options">
<div className="mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-center">
<IconSpeakerphone size={18} strokeWidth={2}/><span className="label ml-2">Report Issues</span>
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">Report Issues</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno" target="_blank" className="flex items-center">
<img src={GithubSvg.src} style={{width: "18px"}}/>
<img src={GithubSvg.src} style={{ width: '18px' }} />
<span className="label ml-2">Github</span>
</a>
</div>
<div className="mt-2">
<a href="https://twitter.com/use_bruno" target="_blank" className="flex items-center">
<IconBrandTwitter size={18} strokeWidth={2}/><span className="label ml-2">Twitter</span>
<IconBrandTwitter size={18} strokeWidth={2} />
<span className="label ml-2">Twitter</span>
</a>
</div>
</div>
</Modal>
</StyledWrapper>
);
}
};
export default BrunoSupport;

View File

@ -11,4 +11,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -37,7 +37,7 @@ export default class QueryEditor extends React.Component {
matchBrackets: true,
showCursorWhenSelecting: true,
foldGutter: true,
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
readOnly: this.props.readOnly ? 'nocursor' : false,
extraKeys: {
'Cmd-Enter': () => {
@ -60,10 +60,10 @@ export default class QueryEditor extends React.Component {
this.props.onSave();
}
},
'Tab': function(cm){
cm.replaceSelection(" " , "end");
Tab: function (cm) {
cm.replaceSelection(' ', 'end');
}
}
},
}));
if (editor) {
editor.on('change', this._onEdit);
@ -82,14 +82,10 @@ export default class QueryEditor extends React.Component {
this.editor.options.jump.schema = this.props.schema;
CodeMirror.signal(this.editor, 'change', this.editor);
}
if (
this.props.value !== prevProps.value &&
this.props.value !== this.cachedValue &&
this.editor
) {
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue && this.editor) {
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
this.editor.setOption("mode", this.props.mode);
this.editor.setOption('mode', this.props.mode);
}
this.ignoreChangeEvent = false;
}
@ -106,7 +102,7 @@ export default class QueryEditor extends React.Component {
<StyledWrapper
className="h-full"
aria-label="Code Editor"
ref={node => {
ref={(node) => {
this._node = node;
}}
/>

View File

@ -24,13 +24,13 @@ const Wrapper = styled.div`
.label-item {
display: flex;
align-items: center;
padding: .35rem .6rem;
padding: 0.35rem 0.6rem;
}
.dropdown-item {
display: flex;
align-items: center;
padding: .35rem .6rem;
padding: 0.35rem 0.6rem;
cursor: pointer;
&:hover {

View File

@ -5,16 +5,7 @@ import StyledWrapper from './StyledWrapper';
const Dropdown = ({ icon, children, onCreate, placement }) => {
return (
<StyledWrapper className="dropdown">
<Tippy
content={children}
placement={placement || "bottom-end"}
animation={false}
arrow={false}
onCreate={onCreate}
interactive={true}
trigger="click"
appendTo="parent"
>
<Tippy content={children} placement={placement || 'bottom-end'} animation={false} arrow={false} onCreate={onCreate} interactive={true} trigger="click" appendTo="parent">
{icon}
</Tippy>
</StyledWrapper>

View File

@ -3,7 +3,7 @@ import find from 'lodash/find';
import Dropdown from 'components/Dropdown';
import { selectEnvironment } from 'providers/ReduxStore/slices/collections/actions';
import { IconSettings, IconCaretDown, IconDatabase } from '@tabler/icons';
import EnvironmentSettings from "../EnvironmentSettings";
import EnvironmentSettings from '../EnvironmentSettings';
import toast from 'react-hot-toast';
import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
@ -13,7 +13,7 @@ const EnvironmentSelector = ({collection}) => {
const dropdownTippyRef = useRef();
const [openSettingsModal, setOpenSettingsModal] = useState(false);
const { environments, activeEnvironmentUid } = collection;
const activeEnvironment = activeEnvironmentUid ? find(environments, e => e.uid === activeEnvironmentUid) : null;
const activeEnvironment = activeEnvironmentUid ? find(environments, (e) => e.uid === activeEnvironmentUid) : null;
const Icon = forwardRef((props, ref) => {
return (
@ -24,7 +24,7 @@ const EnvironmentSelector = ({collection}) => {
);
});
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const onSelect = (environment) => {
dispatch(selectEnvironment(environment ? environment.uid : null, collection.uid))
@ -35,25 +35,34 @@ const EnvironmentSelector = ({collection}) => {
toast.success(`No Environments are active now`);
}
})
.catch((err) => console.log(err) && toast.error("An error occured while selecting the environment"));
.catch((err) => console.log(err) && toast.error('An error occured while selecting the environment'));
};
return (
<StyledWrapper>
<div className="flex items-center cursor-pointer environment-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
{(environments && environments.length) ? environments.map((e) => (
<div className="dropdown-item" key={e.uid} onClick={() => {
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
{environments && environments.length
? environments.map((e) => (
<div
className="dropdown-item"
key={e.uid}
onClick={() => {
onSelect(e);
dropdownTippyRef.current.hide();
}}>
}}
>
<IconDatabase size={18} strokeWidth={1.5} /> <span className="ml-2">{e.name}</span>
</div>
)) : null}
<div className="dropdown-item" onClick={() => {
))
: null}
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onSelect(null);
}}>
}}
>
<span>No Environment</span>
</div>
<div className="dropdown-item" style={{ borderTop: 'solid 1px #e7e7e7' }} onClick={() => setOpenSettingsModal(true)}>

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef } from 'react';
import Portal from "components/Portal/index";
import Modal from "components/Modal/index";
import Portal from 'components/Portal/index';
import Modal from 'components/Modal/index';
import toast from 'react-hot-toast';
import { useFormik } from 'formik';
import { addEnvironment } from 'providers/ReduxStore/slices/collections/actions';
@ -13,21 +13,18 @@ const CreateEnvironment = ({collection, onClose}) => {
const formik = useFormik({
enableReinitialize: true,
initialValues: {
name: ""
name: ''
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(addEnvironment(values.name, collection.uid))
.then(() => {
toast.success("Environment created in collection");
toast.success('Environment created in collection');
onClose();
})
.catch(() => toast.error("An error occured while created the environment"));
.catch(() => toast.error('An error occured while created the environment'));
}
});
@ -39,37 +36,35 @@ const CreateEnvironment = ({collection, onClose}) => {
const onSubmit = () => {
formik.handleSubmit();
}
};
return (
<Portal>
<Modal
size="sm"
title={"Create Environment"}
confirmText='Create'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title={'Create Environment'} confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">Environment Name</label>
<label htmlFor="name" className="block font-semibold">
Environment Name
</label>
<input
id="environment-name" type="text" name="name"
id="environment-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>
</Portal>
);
}
};
export default CreateEnvironment;

View File

@ -1,7 +1,7 @@
import React from 'react';
import Portal from "components/Portal/index";
import Portal from 'components/Portal/index';
import toast from 'react-hot-toast';
import Modal from "components/Modal/index";
import Modal from 'components/Modal/index';
import { deleteEnvironment } from 'providers/ReduxStore/slices/collections/actions';
import { useDispatch } from 'react-redux';
import StyledWrapper from './StyledWrapper';
@ -11,29 +11,21 @@ const DeleteEnvironment = ({onClose, environment, collection}) => {
const onConfirm = () => {
dispatch(deleteEnvironment(environment.uid, collection.uid))
.then(() => {
toast.success("Environment deleted successfully");
toast.success('Environment deleted successfully');
onClose();
})
.catch(() => toast.error("An error occured while deleting the environment"));
.catch(() => toast.error('An error occured while deleting the environment'));
};
return (
<Portal>
<StyledWrapper>
<Modal
size="sm"
title={"Delete Environment"}
confirmText="Delete"
handleConfirm={onConfirm}
handleCancel={onClose}
>
<Modal size="sm" title={'Delete Environment'} confirmText="Delete" handleConfirm={onConfirm} handleCancel={onClose}>
Are you sure you want to delete <span className="font-semibold">{environment.name}</span> ?
</Modal>
</StyledWrapper>
</Portal>
);
}
};
export default DeleteEnvironment;

View File

@ -6,7 +6,8 @@ const Wrapper = styled.div`
border-collapse: collapse;
font-weight: 600;
thead, td {
thead,
td {
border: 1px solid #efefef;
}
@ -24,7 +25,7 @@ const Wrapper = styled.div`
font-size: 0.8125rem;
}
input[type="text"] {
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
@ -35,7 +36,7 @@ const Wrapper = styled.div`
}
}
input[type="checkbox"] {
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;

View File

@ -10,20 +10,17 @@ 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 { variables, hasChanges } = state;
const saveChanges = () => {
dispatch(saveEnvironment(cloneDeep(variables), environment.uid, collection.uid))
.then(() => {
toast.success("Changes saved successfully");
toast.success('Changes saved successfully');
reducerDispatch({
type: 'CHANGES_SAVED'
});
})
.catch(() => toast.error("An error occured while saving the changes"));
.catch(() => toast.error('An error occured while saving the changes'));
};
const addVariable = () => {
@ -72,13 +69,17 @@ const EnvironmentVariables = ({environment, collection}) => {
</tr>
</thead>
<tbody>
{variables && variables.length ? variables.map((variable, index) => {
{variables && variables.length
? variables.map((variable, index) => {
return (
<tr key={variable.uid}>
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={variable.name}
className="mousetrap"
onChange={(e) => handleVarChange(e, variable, 'name')}
@ -87,7 +88,10 @@ const EnvironmentVariables = ({environment, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={variable.value}
className="mousetrap"
onChange={(e) => handleVarChange(e, variable, 'value')}
@ -95,12 +99,7 @@ const EnvironmentVariables = ({environment, collection}) => {
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={variable.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleVarChange(e, variable, 'enabled')}
/>
<input type="checkbox" checked={variable.enabled} className="mr-3 mousetrap" onChange={(e) => handleVarChange(e, variable, 'enabled')} />
<button onClick={() => handleRemoveVars(variable)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
@ -108,12 +107,15 @@ const EnvironmentVariables = ({environment, collection}) => {
</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
<div>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addVariable}>+ Add Variable</button>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addVariable}>
+ Add Variable
</button>
</div>
<div>
@ -122,6 +124,6 @@ const EnvironmentVariables = ({environment, collection}) => {
</button>
</div>
</StyledWrapper>
)
);
};
export default EnvironmentVariables;

View File

@ -1,8 +1,8 @@
import React, {useState } from "react";
import { IconEdit, IconTrash, IconDatabase } from "@tabler/icons";
import React, { useState } from 'react';
import { IconEdit, IconTrash, IconDatabase } from '@tabler/icons';
import EnvironmentVariables from './EnvironmentVariables';
import RenameEnvironment from "../../RenameEnvironment";
import DeleteEnvironment from "../../DeleteEnvironment";
import RenameEnvironment from '../../RenameEnvironment';
import DeleteEnvironment from '../../DeleteEnvironment';
const EnvironmentDetails = ({ environment, collection }) => {
const [openEditModal, setOpenEditModal] = useState(false);
@ -28,7 +28,6 @@ const EnvironmentDetails = ({environment, collection}) => {
<EnvironmentVariables key={environment.uid} environment={environment} collection={collection} />
</div>
</div>
);
};

View File

@ -1,4 +1,4 @@
import styled from "styled-components";
import styled from 'styled-components';
const StyledWrapper = styled.div`
margin-inline: -1rem;

View File

@ -1,7 +1,7 @@
import React, { useEffect, useState, forwardRef, useRef } from "react";
import EnvironmentDetails from "./EnvironmentDetails";
import CreateEnvironment from "../CreateEnvironment/index";
import StyledWrapper from "./StyledWrapper";
import React, { useEffect, useState, forwardRef, useRef } from 'react';
import EnvironmentDetails from './EnvironmentDetails';
import CreateEnvironment from '../CreateEnvironment/index';
import StyledWrapper from './StyledWrapper';
const EnvironmentList = ({ collection }) => {
const { environments } = collection;
@ -22,12 +22,10 @@ const EnvironmentList = ({collection}) => {
<div className="flex">
<div>
<div className="environments-sidebar">
{environments && environments.length && environments.map((env) => (
<div
key={env.uid}
className={selectedEnvironment.uid === env.uid ? "environment-item active": "environment-item"}
onClick={() => setSelectedEnvironment(env)}
>
{environments &&
environments.length &&
environments.map((env) => (
<div key={env.uid} className={selectedEnvironment.uid === env.uid ? 'environment-item active' : 'environment-item'} onClick={() => setSelectedEnvironment(env)}>
<span>{env.name}</span>
</div>
))}

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef } from 'react';
import Portal from "components/Portal/index";
import Modal from "components/Modal/index";
import Portal from 'components/Portal/index';
import Modal from 'components/Modal/index';
import toast from 'react-hot-toast';
import { useFormik } from 'formik';
import { renameEnvironment } from 'providers/ReduxStore/slices/collections/actions';
@ -16,18 +16,15 @@ const RenameEnvironment = ({onClose, environment, collection}) => {
name: environment.name
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(renameEnvironment(values.name, environment.uid, collection.uid))
.then(() => {
toast.success("Environment renamed successfully");
toast.success('Environment renamed successfully');
onClose();
})
.catch(() => toast.error("An error occured while renaming the environment"));
.catch(() => toast.error('An error occured while renaming the environment'));
}
});
@ -39,37 +36,35 @@ const RenameEnvironment = ({onClose, environment, collection}) => {
const onSubmit = () => {
formik.handleSubmit();
}
};
return (
<Portal>
<Modal
size="sm"
title={"Rename Environment"}
confirmText='Rename'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title={'Rename Environment'} confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">Environment Name</label>
<label htmlFor="name" className="block font-semibold">
Environment Name
</label>
<input
id="environment-name" type="text" name="name"
id="environment-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>
</Portal>
);
}
};
export default RenameEnvironment;

View File

@ -1,4 +1,4 @@
import styled from "styled-components";
import styled from 'styled-components';
const StyledWrapper = styled.div`
button.btn-create-environment {

View File

@ -1,50 +1,34 @@
import Modal from "components/Modal/index";
import React, { useState } from "react";
import CreateEnvironment from "./CreateEnvironment";
import EnvironmentList from "./EnvironmentList";
import StyledWrapper from "./StyledWrapper";
import Modal from 'components/Modal/index';
import React, { useState } from 'react';
import CreateEnvironment from './CreateEnvironment';
import EnvironmentList from './EnvironmentList';
import StyledWrapper from './StyledWrapper';
const EnvironmentSettings = ({ collection, onClose }) => {
const { environments } = collection;
const [openCreateModal, setOpenCreateModal] = useState(false)
const [openCreateModal, setOpenCreateModal] = useState(false);
if (!environments || !environments.length) {
return (
<StyledWrapper>
<Modal
size="md"
title="Environments"
confirmText={"Close"}
handleConfirm={onClose}
handleCancel={onClose}
hideCancel={true}
>
<Modal size="md" title="Environments" confirmText={'Close'} handleConfirm={onClose} handleCancel={onClose} hideCancel={true}>
{openCreateModal && <CreateEnvironment collection={collection} onClose={() => setOpenCreateModal(false)} />}
<div className="text-center">
<p>No environments found!</p>
<button
className="btn-create-environment text-link pr-2 py-3 mt-2 select-none"
onClick={() => setOpenCreateModal(true)}
>
<button className="btn-create-environment text-link pr-2 py-3 mt-2 select-none" onClick={() => setOpenCreateModal(true)}>
+ <span>Create Environment</span>
</button>
</div>
</Modal>
</StyledWrapper>
)
);
}
return (
<Modal
size="lg"
title="Environments"
handleCancel={onClose}
hideFooter={true}
>
<Modal size="lg" title="Environments" handleCancel={onClose} hideFooter={true}>
<EnvironmentList collection={collection} />
</Modal>
)
}
);
};
export default EnvironmentSettings;

View File

@ -2,10 +2,10 @@ import styled from 'styled-components';
const Wrapper = styled.div`
&.modal--animate-out {
animation: fade-out 0.5s forwards cubic-bezier(.19,1,.22,1);
animation: fade-out 0.5s forwards cubic-bezier(0.19, 1, 0.22, 1);
.bruno-modal-card {
animation: fade-and-slide-out-from-top .50s forwards cubic-bezier(.19,1,.22,1);
animation: fade-and-slide-out-from-top 0.5s forwards cubic-bezier(0.19, 1, 0.22, 1);
}
}
@ -23,8 +23,8 @@ const Wrapper = styled.div`
}
.bruno-modal-card {
animation-duration: .85s;
animation-delay: .1s;
animation-duration: 0.85s;
animation-delay: 0.1s;
background: var(--color-background-top);
border-radius: var(--border-radius);
position: relative;
@ -58,7 +58,7 @@ const Wrapper = styled.div`
max-width: calc(100% - 30px);
}
animation: fade-and-slide-in-from-top .50s forwards cubic-bezier(.19,1,.22,1);
animation: fade-and-slide-in-from-top 0.5s forwards cubic-bezier(0.19, 1, 0.22, 1);
}
.bruno-modal-header {
@ -103,17 +103,17 @@ const Wrapper = styled.div`
background: transparent;
&:before {
content: "";
content: '';
height: 100%;
width: 100%;
left: 0;
opacity: .4;
opacity: 0.4;
top: 0;
background: black;
position: fixed;
}
animation: fade-in .1s forwards cubic-bezier(.19,1,.22,1);
animation: fade-in 0.1s forwards cubic-bezier(0.19, 1, 0.22, 1);
}
.bruno-modal-footer {

View File

@ -12,11 +12,7 @@ const ModalHeader = ({title, handleCancel}) => (
</div>
);
const ModalContent = ({children}) => (
<div className="bruno-modal-content px-4 py-6">
{children}
</div>
);
const ModalContent = ({ children }) => <div className="bruno-modal-content px-4 py-6">{children}</div>;
const ModalFooter = ({ confirmText, cancelText, handleSubmit, handleCancel, confirmDisabled, hideCancel, hideFooter }) => {
confirmText = confirmText || 'Save';
@ -28,7 +24,7 @@ const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confi
return (
<div className="flex justify-end p-4 bruno-modal-footer">
<span className={hideCancel ? "hidden" : "mr-2"}>
<span className={hideCancel ? 'hidden' : 'mr-2'}>
<button type="button" onClick={handleCancel} className="btn btn-md btn-close">
{cancelText}
</button>
@ -40,20 +36,9 @@ const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confi
</span>
</div>
);
}
};
const Modal = ({
size,
title,
confirmText,
cancelText,
handleCancel,
handleConfirm,
children,
confirmDisabled,
hideCancel,
hideFooter
}) => {
const Modal = ({ size, title, confirmText, cancelText, handleCancel, handleConfirm, children, confirmDisabled, hideCancel, hideFooter }) => {
const [isClosing, setIsClosing] = useState(false);
const escFunction = (event) => {
const escKeyCode = 27;
@ -72,7 +57,7 @@ const Modal = ({
return () => {
document.removeEventListener('keydown', escFunction, false);
}
};
}, []);
let classes = 'bruno-modal';

View File

@ -14,7 +14,6 @@ const StyledWrapper = styled.div`
user-select: none;
}
}
`;
export default StyledWrapper;

View File

@ -1,7 +1,7 @@
import React, { useState, forwardRef, useRef } from 'react';
import Dropdown from '../Dropdown';
import { faCaretDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCaretDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { IconBox, IconSearch, IconDots } from '@tabler/icons';
import StyledWrapper from './StyledWrapper';
@ -9,7 +9,7 @@ const Navbar = () => {
const [modalOpen, setModalOpen] = useState(false);
const menuDropdownTippyRef = useRef();
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
const onMenuDropdownCreate = (ref) => (menuDropdownTippyRef.current = ref);
const MenuIcon = forwardRef((props, ref) => {
return (
<div ref={ref} className="dropdown-icon cursor-pointer">
@ -25,27 +25,36 @@ const Navbar = () => {
{/* <FontAwesomeIcon className="ml-2" icon={faCaretDown} style={{fontSize: 13}}/> */}
</div>
<div className="collection-dropdown flex flex-grow items-center justify-end">
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement='bottom-start'>
<div className="dropdown-item" onClick={(e) => {
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setModalOpen(true);
}}>
}}
>
Create Collection
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
}}>
}}
>
Import Collection
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
}}>
}}
>
Settings
</div>
</Dropdown>
</div>
</StyledWrapper>
)
);
};
export default Navbar;

View File

@ -1,7 +1,7 @@
import { createPortal } from 'react-dom';
function Portal({ children, wrapperId }) {
wrapperId = wrapperId || "bruno-app-body";
wrapperId = wrapperId || 'bruno-app-body';
return createPortal(children, document.getElementById(wrapperId));
}

View File

@ -6,7 +6,8 @@ const Wrapper = styled.div`
border-collapse: collapse;
font-weight: 600;
thead, td {
thead,
td {
border: 1px solid #efefef;
}
@ -24,7 +25,7 @@ const Wrapper = styled.div`
font-size: 0.8125rem;
}
input[type="text"] {
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
@ -35,7 +36,7 @@ const Wrapper = styled.div`
}
}
input[type="checkbox"] {
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;

View File

@ -11,10 +11,12 @@ const FormUrlEncodedParams = ({item, collection}) => {
const params = item.draft ? get(item, 'draft.request.body.formUrlEncoded') : get(item, 'request.body.formUrlEncoded');
const addParam = () => {
dispatch(addFormUrlEncodedParam({
dispatch(
addFormUrlEncodedParam({
itemUid: item.uid,
collectionUid: collection.uid,
}));
collectionUid: collection.uid
})
);
};
const handleParamChange = (e, _param, type) => {
@ -37,19 +39,23 @@ const FormUrlEncodedParams = ({item, collection}) => {
break;
}
}
dispatch(updateFormUrlEncodedParam({
dispatch(
updateFormUrlEncodedParam({
param: param,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
const handleRemoveParams = (param) => {
dispatch(deleteFormUrlEncodedParam({
dispatch(
deleteFormUrlEncodedParam({
paramUid: param.uid,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
return (
@ -64,13 +70,17 @@ const FormUrlEncodedParams = ({item, collection}) => {
</tr>
</thead>
<tbody>
{params && params.length ? params.map((param, index) => {
{params && params.length
? params.map((param, index) => {
return (
<tr key={param.uid}>
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.name}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'name')}
@ -79,7 +89,10 @@ const FormUrlEncodedParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.value}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'value')}
@ -88,7 +101,10 @@ const FormUrlEncodedParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.description}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'description')}
@ -96,12 +112,7 @@ const FormUrlEncodedParams = ({item, collection}) => {
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={param.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParams(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
@ -109,11 +120,14 @@ const FormUrlEncodedParams = ({item, collection}) => {
</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addParam}>+ Add Param</button>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addParam}>
+ Add Param
</button>
</StyledWrapper>
)
);
};
export default FormUrlEncodedParams;

View File

@ -18,7 +18,11 @@ const StyledWrapper = styled.div`
color: rgb(125 125 125);
outline: none !important;
&:focus, &:active, &:focus-within, &:focus-visible, &:target {
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
outline: none !important;
box-shadow: none !important;
}
@ -38,7 +42,11 @@ const StyledWrapper = styled.div`
outline: none !important;
box-shadow: none !important;
&:focus, &:active, &:focus-within, &:focus-visible, &:target {
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
border: none;
outline: none !important;
box-shadow: none !important;
@ -49,7 +57,6 @@ const StyledWrapper = styled.div`
box-shadow: none !important;
}
}
`;
export default StyledWrapper;

View File

@ -7,20 +7,14 @@ import StyledWrapper from './StyledWrapper';
const GraphQLRequestPane = ({ onRunQuery, schema, leftPaneWidth, value, onQueryChange }) => {
return (
<StyledWrapper className="h-full">
<Tabs className='react-tabs mt-1 flex flex-grow flex-col h-full' forceRenderTabPanel>
<Tabs className="react-tabs mt-1 flex flex-grow flex-col h-full" forceRenderTabPanel>
<TabList>
<Tab tabIndex="-1">Query</Tab>
<Tab tabIndex="-1">Headers</Tab>
</TabList>
<TabPanel>
<div className="mt-4">
<QueryEditor
schema={schema}
width={leftPaneWidth}
value={value}
onRunQuery={onRunQuery}
onEdit={onQueryChange}
/>
<QueryEditor schema={schema} width={leftPaneWidth} value={value} onRunQuery={onRunQuery} onEdit={onQueryChange} />
</div>
</TabPanel>
<TabPanel>
@ -28,7 +22,7 @@ const GraphQLRequestPane = ({onRunQuery, schema, leftPaneWidth, value, onQueryCh
</TabPanel>
</Tabs>
</StyledWrapper>
)
);
};
export default GraphQLRequestPane;

View File

@ -10,7 +10,11 @@ const StyledWrapper = styled.div`
color: var(--color-tab-inactive);
cursor: pointer;
&:focus, &:active, &:focus-within, &:focus-visible, &:target {
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
outline: none !important;
box-shadow: none !important;
}

View File

@ -15,10 +15,12 @@ const HttpRequestPane = ({item, collection, leftPaneWidth}) => {
const activeTabUid = useSelector((state) => state.tabs.activeTabUid);
const selectTab = (tab) => {
dispatch(updateRequestPaneTab({
dispatch(
updateRequestPaneTab({
uid: item.uid,
requestPaneTab: tab
}))
})
);
};
const getTabPanel = (tab) => {
@ -36,33 +38,35 @@ const HttpRequestPane = ({item, collection, leftPaneWidth}) => {
return <div className="mt-4">404 | Not found</div>;
}
}
}
};
if (!activeTabUid) {
return (
<div>Something went wrong</div>
);
return <div>Something went wrong</div>;
}
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
if (!focusedTab || !focusedTab.uid || !focusedTab.requestPaneTab) {
return (
<div className="pb-4 px-4">An error occured!</div>
);
return <div className="pb-4 px-4">An error occured!</div>;
}
const getTabClassname = (tabName) => {
return classnames(`tab select-none ${tabName}`, {
'active': tabName === focusedTab.requestPaneTab
active: tabName === focusedTab.requestPaneTab
});
};
return (
<StyledWrapper className="flex flex-col h-full relative">
<div className="flex items-center tabs" role="tablist">
<div className={getTabClassname('params')} role="tab" onClick={() => selectTab('params')}>Params</div>
<div className={getTabClassname('body')} role="tab" onClick={() => selectTab('body')}>Body</div>
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>Headers</div>
<div className={getTabClassname('params')} role="tab" onClick={() => selectTab('params')}>
Params
</div>
<div className={getTabClassname('body')} role="tab" onClick={() => selectTab('body')}>
Body
</div>
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
Headers
</div>
{/* Moved to post mvp */}
{/* <div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}>Auth</div> */}
{focusedTab.requestPaneTab === 'body' ? (
@ -71,11 +75,9 @@ const HttpRequestPane = ({item, collection, leftPaneWidth}) => {
</div>
) : null}
</div>
<section className="flex w-full mt-5">
{getTabPanel(focusedTab.requestPaneTab)}
</section>
<section className="flex w-full mt-5">{getTabPanel(focusedTab.requestPaneTab)}</section>
</StyledWrapper>
)
);
};
export default HttpRequestPane;

View File

@ -6,7 +6,8 @@ const Wrapper = styled.div`
border-collapse: collapse;
font-weight: 600;
thead, td {
thead,
td {
border: 1px solid #efefef;
}
@ -24,7 +25,7 @@ const Wrapper = styled.div`
font-size: 0.8125rem;
}
input[type="text"] {
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
@ -35,7 +36,7 @@ const Wrapper = styled.div`
}
}
input[type="checkbox"] {
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;

View File

@ -11,10 +11,12 @@ const MultipartFormParams = ({item, collection}) => {
const params = item.draft ? get(item, 'draft.request.body.multipartForm') : get(item, 'request.body.multipartForm');
const addParam = () => {
dispatch(addMultipartFormParam({
dispatch(
addMultipartFormParam({
itemUid: item.uid,
collectionUid: collection.uid,
}));
collectionUid: collection.uid
})
);
};
const handleParamChange = (e, _param, type) => {
@ -37,19 +39,23 @@ const MultipartFormParams = ({item, collection}) => {
break;
}
}
dispatch(updateMultipartFormParam({
dispatch(
updateMultipartFormParam({
param: param,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
const handleRemoveParams = (param) => {
dispatch(deleteMultipartFormParam({
dispatch(
deleteMultipartFormParam({
paramUid: param.uid,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
return (
@ -64,13 +70,17 @@ const MultipartFormParams = ({item, collection}) => {
</tr>
</thead>
<tbody>
{params && params.length ? params.map((param, index) => {
{params && params.length
? params.map((param, index) => {
return (
<tr key={param.uid}>
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.name}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'name')}
@ -79,7 +89,10 @@ const MultipartFormParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.value}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'value')}
@ -88,7 +101,10 @@ const MultipartFormParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.description}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'description')}
@ -96,12 +112,7 @@ const MultipartFormParams = ({item, collection}) => {
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={param.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParams(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
@ -109,11 +120,14 @@ const MultipartFormParams = ({item, collection}) => {
</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addParam}>+ Add Param</button>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={addParam}>
+ Add Param
</button>
</StyledWrapper>
)
);
};
export default MultipartFormParams;

View File

@ -13,4 +13,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -44,44 +44,37 @@ export default class QueryEditor extends React.Component {
showCursorWhenSelecting: true,
readOnly: this.props.readOnly ? 'nocursor' : false,
foldGutter: {
minFoldSize: 4,
minFoldSize: 4
},
lint: {
schema: this.props.schema,
validationRules: this.props.validationRules ?? null,
// linting accepts string or FragmentDefinitionNode[]
externalFragments: this.props?.externalFragments,
externalFragments: this.props?.externalFragments
},
hintOptions: {
schema: this.props.schema,
closeOnUnfocus: false,
completeSingle: false,
container: this._node,
externalFragments: this.props?.externalFragments,
externalFragments: this.props?.externalFragments
},
info: {
schema: this.props.schema,
renderDescription: (text) => md.render(text),
onClick: (reference) =>
this.props.onClickReference && this.props.onClickReference(reference),
onClick: (reference) => this.props.onClickReference && this.props.onClickReference(reference)
},
jump: {
schema: this.props.schema,
onClick: (reference) =>
this.props.onClickReference && this.props.onClickReference(reference)
onClick: (reference) => this.props.onClickReference && this.props.onClickReference(reference)
},
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
extraKeys: {
'Cmd-Space': () =>
editor.showHint({ completeSingle: true, container: this._node }),
'Ctrl-Space': () =>
editor.showHint({ completeSingle: true, container: this._node }),
'Alt-Space': () =>
editor.showHint({ completeSingle: true, container: this._node }),
'Shift-Space': () =>
editor.showHint({ completeSingle: true, container: this._node }),
'Shift-Alt-Space': () =>
editor.showHint({ completeSingle: true, container: this._node }),
'Cmd-Space': () => editor.showHint({ completeSingle: true, container: this._node }),
'Ctrl-Space': () => editor.showHint({ completeSingle: true, container: this._node }),
'Alt-Space': () => editor.showHint({ completeSingle: true, container: this._node }),
'Shift-Space': () => editor.showHint({ completeSingle: true, container: this._node }),
'Shift-Alt-Space': () => editor.showHint({ completeSingle: true, container: this._node }),
'Cmd-Enter': () => {
if (this.props.onRunQuery) {
@ -129,8 +122,8 @@ export default class QueryEditor extends React.Component {
if (this.props.onRunQuery) {
// empty
}
},
},
}
}
}));
if (editor) {
editor.on('change', this._onEdit);
@ -152,11 +145,7 @@ export default class QueryEditor extends React.Component {
this.editor.options.jump.schema = this.props.schema;
CodeMirror.signal(this.editor, 'change', this.editor);
}
if (
this.props.value !== prevProps.value &&
this.props.value !== this.cachedValue &&
this.editor
) {
if (this.props.value !== prevProps.value && this.props.value !== this.cachedValue && this.editor) {
this.cachedValue = this.props.value;
this.editor.setValue(this.props.value);
}
@ -177,7 +166,7 @@ export default class QueryEditor extends React.Component {
<StyledWrapper
className="h-full"
aria-label="Query Editor"
ref={node => {
ref={(node) => {
this._node = node;
}}
/>

View File

@ -7,10 +7,7 @@
import escapeHTML from 'escape-html';
import MD from 'markdown-it';
import {
GraphQLNonNull,
GraphQLList
} from 'graphql';
import { GraphQLNonNull, GraphQLList } from 'graphql';
const md = new MD();
@ -18,21 +15,14 @@ const md = new MD();
* Render a custom UI for CodeMirror's hint which includes additional info
* about the type and description for the selected context.
*/
export default function onHasCompletion(
_cm,
data,
onHintInformationRender,
) {
export default function onHasCompletion(_cm, data, onHintInformationRender) {
const CodeMirror = require('codemirror');
let information;
let deprecation;
// When a hint result is selected, we augment the UI with information.
CodeMirror.on(
data,
'select',
(ctx, el) => {
CodeMirror.on(data, 'select', (ctx, el) => {
// Only the first time (usually when the hint UI is first displayed)
// do we create the information nodes.
if (!information) {
@ -61,31 +51,19 @@ export default function onHasCompletion(
deprecation = null;
onRemoveFn = null;
}
}),
})
);
}
// Now that the UI has been set up, add info to information.
const description = ctx.description
? md.render(ctx.description)
: 'Self descriptive.';
const type = ctx.type
? '<span className="infoType">' + renderType(ctx.type) + '</span>'
: '';
const description = ctx.description ? md.render(ctx.description) : 'Self descriptive.';
const type = ctx.type ? '<span className="infoType">' + renderType(ctx.type) + '</span>' : '';
information.innerHTML =
'<div className="content">' +
(description.slice(0, 3) === '<p>'
? '<p>' + type + description.slice(3)
: type + description) +
'</div>';
information.innerHTML = '<div className="content">' + (description.slice(0, 3) === '<p>' ? '<p>' + type + description.slice(3) : type + description) + '</div>';
if (ctx && deprecation && ctx.deprecationReason) {
const reason = ctx.deprecationReason
? md.render(ctx.deprecationReason)
: '';
deprecation.innerHTML =
'<span className="deprecation-label">Deprecated</span>' + reason;
const reason = ctx.deprecationReason ? md.render(ctx.deprecationReason) : '';
deprecation.innerHTML = '<span className="deprecation-label">Deprecated</span>' + reason;
deprecation.style.display = 'block';
} else if (deprecation) {
deprecation.style.display = 'none';
@ -95,8 +73,7 @@ export default function onHasCompletion(
if (onHintInformationRender) {
onHintInformationRender(information);
}
},
);
});
}
function renderType(type) {

View File

@ -6,7 +6,8 @@ const Wrapper = styled.div`
border-collapse: collapse;
font-weight: 600;
thead, td {
thead,
td {
border: 1px solid #efefef;
}
@ -27,7 +28,7 @@ const Wrapper = styled.div`
}
}
input[type="text"] {
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
@ -38,7 +39,7 @@ const Wrapper = styled.div`
}
}
input[type="checkbox"] {
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;

View File

@ -12,10 +12,12 @@ const QueryParams = ({item, collection}) => {
const params = item.draft ? get(item, 'draft.request.params') : get(item, 'request.params');
const handleAddParam = () => {
dispatch(addQueryParam({
dispatch(
addQueryParam({
itemUid: item.uid,
collectionUid: collection.uid,
}));
collectionUid: collection.uid
})
);
};
const handleParamChange = (e, _param, type) => {
@ -40,19 +42,23 @@ const QueryParams = ({item, collection}) => {
}
}
dispatch(updateQueryParam({
dispatch(
updateQueryParam({
param,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
const handleRemoveParam = (param) => {
dispatch(deleteQueryParam({
dispatch(
deleteQueryParam({
paramUid: param.uid,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
return (
@ -67,13 +73,17 @@ const QueryParams = ({item, collection}) => {
</tr>
</thead>
<tbody>
{params && params.length ? params.map((param, index) => {
{params && params.length
? params.map((param, index) => {
return (
<tr key={param.uid}>
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.name}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'name')}
@ -82,7 +92,10 @@ const QueryParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.value}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'value')}
@ -91,7 +104,10 @@ const QueryParams = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={param.description}
className="mousetrap"
onChange={(e) => handleParamChange(e, param, 'description')}
@ -99,12 +115,7 @@ const QueryParams = ({item, collection}) => {
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={param.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleParamChange(e, param, 'enabled')}
/>
<input type="checkbox" checked={param.enabled} className="mr-3 mousetrap" onChange={(e) => handleParamChange(e, param, 'enabled')} />
<button onClick={() => handleRemoveParam(param)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
@ -112,13 +123,14 @@ const QueryParams = ({item, collection}) => {
</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={handleAddParam}>
+&nbsp;<span>Add Param</span>
</button>
</StyledWrapper>
)
);
};
export default QueryParams;

View File

@ -17,7 +17,7 @@ const Wrapper = styled.div`
}
.dropdown-item {
padding: .25rem .6rem !important;
padding: 0.25rem 0.6rem !important;
}
}

View File

@ -5,13 +5,15 @@ import StyledWrapper from './StyledWrapper';
const HttpMethodSelector = ({ method, onMethodSelect }) => {
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex w-full items-center pl-3 py-1 select-none uppercase">
<div className="flex-grow font-medium">{method}</div>
<div><IconCaretDown className="caret ml-2 mr-2" size={14} strokeWidth={2}/></div>
<div>
<IconCaretDown className="caret ml-2 mr-2" size={14} strokeWidth={2} />
</div>
</div>
);
});
@ -20,10 +22,13 @@ const HttpMethodSelector = ({method, onMethodSelect}) => {
const Verb = ({ verb }) => {
return (
<div className="dropdown-item" onClick={() => {
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
handleMethodSelect(verb);
}}>
}}
>
{verb}
</div>
);
@ -32,14 +37,14 @@ const HttpMethodSelector = ({method, onMethodSelect}) => {
return (
<StyledWrapper>
<div className="flex items-center cursor-pointer method-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-start'>
<Verb verb='GET' />
<Verb verb='POST' />
<Verb verb='PUT' />
<Verb verb='DELETE' />
<Verb verb='PATCH' />
<Verb verb='OPTIONS' />
<Verb verb='HEAD' />
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-start">
<Verb verb="GET" />
<Verb verb="POST" />
<Verb verb="PUT" />
<Verb verb="DELETE" />
<Verb verb="PATCH" />
<Verb verb="OPTIONS" />
<Verb verb="HEAD" />
</Dropdown>
</div>
</StyledWrapper>

View File

@ -12,19 +12,23 @@ const QueryUrl = ({item, collection, handleRun}) => {
let url = item.draft ? get(item, 'draft.request.url') : get(item, 'request.url');
const onUrlChange = (value) => {
dispatch(requestUrlChanged({
dispatch(
requestUrlChanged({
itemUid: item.uid,
collectionUid: collection.uid,
url: value
}));
})
);
};
const onMethodSelect = (verb) => {
dispatch(updateRequestMethod({
dispatch(
updateRequestMethod({
method: verb,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
return (
@ -35,8 +39,12 @@ const QueryUrl = ({item, collection, handleRun}) => {
<div className="flex items-center flex-grow input-container h-full">
<input
className="px-3 w-full mousetrap"
type="text" value={url}
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
type="text"
value={url}
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={(event) => onUrlChange(event.target.value)}
/>
<div className="flex items-center h-full mr-2 cursor-pointer" onClick={handleRun}>
@ -44,7 +52,7 @@ const QueryUrl = ({item, collection, handleRun}) => {
</div>
</div>
</StyledWrapper>
)
);
};
export default QueryUrl;

View File

@ -8,12 +8,12 @@ const Wrapper = styled.div`
border-radius: 3px;
.dropdown-item {
padding: .2rem .6rem !important;
padding: 0.2rem 0.6rem !important;
padding-left: 1.5rem !important;
}
.label-item {
padding: .2rem .6rem !important;
padding: 0.2rem 0.6rem !important;
}
}

View File

@ -10,10 +10,9 @@ import StyledWrapper from './StyledWrapper';
const RequestBodyMode = ({ item, collection }) => {
const dispatch = useDispatch();
const dropdownTippyRef = useRef();
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const bodyMode = item.draft ? get(item, 'draft.request.body.mode') : get(item, 'request.body.mode');
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="flex items-center justify-center pl-3 py-1 select-none">
@ -23,60 +22,74 @@ const RequestBodyMode = ({item, collection}) => {
});
const onModeChange = (value) => {
dispatch(updateRequestBodyMode({
dispatch(
updateRequestBodyMode({
itemUid: item.uid,
collectionUid: collection.uid,
mode: value
}));
})
);
};
return (
<StyledWrapper>
<div className="inline-flex items-center cursor-pointer body-mode-selector">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
<div className="label-item font-medium">
Form
</div>
<div className="dropdown-item" onClick={() => {
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div className="label-item font-medium">Form</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('multipartForm');
}}>
}}
>
Multipart Form
</div>
<div className="dropdown-item" onClick={() => {
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('formUrlEncoded');
}}>
}}
>
Form Url Encoded
</div>
<div className="label-item font-medium">
Raw
</div>
<div className="dropdown-item" onClick={() => {
<div className="label-item font-medium">Raw</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('json');
}}>
}}
>
JSON
</div>
<div className="dropdown-item" onClick={() => {
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('xml');
}}>
}}
>
XML
</div>
<div className="dropdown-item" onClick={() => {
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('text');
}}>
}}
>
TEXT
</div>
<div className="label-item font-medium">
Other
</div>
<div className="dropdown-item" onClick={() => {
<div className="label-item font-medium">Other</div>
<div
className="dropdown-item"
onClick={() => {
dropdownTippyRef.current.hide();
onModeChange('none');
}}>
}}
>
No Body
</div>
</Dropdown>

View File

@ -14,14 +14,16 @@ const RequestBody = ({item, collection}) => {
const bodyMode = item.draft ? get(item, 'draft.request.body.mode') : get(item, 'request.body.mode');
const onEdit = (value) => {
dispatch(updateRequestBody({
dispatch(
updateRequestBody({
content: value,
itemUid: item.uid,
collectionUid: collection.uid,
}));
collectionUid: collection.uid
})
);
};
const onRun = () => dispatch(sendRequest(item, collection.uid));;
const onRun = () => dispatch(sendRequest(item, collection.uid));
const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
if (['json', 'xml', 'text'].includes(bodyMode)) {
@ -39,13 +41,7 @@ const RequestBody = ({item, collection}) => {
return (
<StyledWrapper className="w-full">
<CodeEditor
value={bodyContent[bodyMode] || ''}
onEdit={onEdit}
onRun={onRun}
onSave={onSave}
mode={codeMirrorMode[bodyMode]}
/>
<CodeEditor value={bodyContent[bodyMode] || ''} onEdit={onEdit} onRun={onRun} onSave={onSave} mode={codeMirrorMode[bodyMode]} />
</StyledWrapper>
);
}
@ -58,10 +54,6 @@ const RequestBody = ({item, collection}) => {
return <MultipartFormParams item={item} collection={collection} />;
}
return(
<StyledWrapper className="w-full">
No Body
</StyledWrapper>
);
return <StyledWrapper className="w-full">No Body</StyledWrapper>;
};
export default RequestBody;

View File

@ -6,7 +6,8 @@ const Wrapper = styled.div`
border-collapse: collapse;
font-weight: 600;
thead, td {
thead,
td {
border: 1px solid #efefef;
}
@ -26,7 +27,7 @@ const Wrapper = styled.div`
padding: 5px;
}
input[type="text"] {
input[type='text'] {
width: 100%;
border: solid 1px transparent;
outline: none !important;
@ -37,7 +38,7 @@ const Wrapper = styled.div`
}
}
input[type="checkbox"] {
input[type='checkbox'] {
cursor: pointer;
position: relative;
top: 1px;

View File

@ -11,10 +11,12 @@ const RequestHeaders = ({item, collection}) => {
const headers = item.draft ? get(item, 'draft.request.headers') : get(item, 'request.headers');
const addHeader = () => {
dispatch(addRequestHeader({
dispatch(
addRequestHeader({
itemUid: item.uid,
collectionUid: collection.uid,
}));
collectionUid: collection.uid
})
);
};
const handleHeaderValueChange = (e, _header, type) => {
@ -37,19 +39,23 @@ const RequestHeaders = ({item, collection}) => {
break;
}
}
dispatch(updateRequestHeader({
dispatch(
updateRequestHeader({
header: header,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
const handleRemoveHeader = (header) => {
dispatch(deleteRequestHeader({
dispatch(
deleteRequestHeader({
headerUid: header.uid,
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
};
return (
@ -64,13 +70,17 @@ const RequestHeaders = ({item, collection}) => {
</tr>
</thead>
<tbody>
{headers && headers.length ? headers.map((header, index) => {
{headers && headers.length
? headers.map((header, index) => {
return (
<tr key={header.uid}>
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={header.name}
className="mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'name')}
@ -79,7 +89,10 @@ const RequestHeaders = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={header.value}
className="mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'value')}
@ -88,7 +101,10 @@ const RequestHeaders = ({item, collection}) => {
<td>
<input
type="text"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={header.description}
className="mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'description')}
@ -96,12 +112,7 @@ const RequestHeaders = ({item, collection}) => {
</td>
<td>
<div className="flex items-center">
<input
type="checkbox"
checked={header.enabled}
className="mr-3 mousetrap"
onChange={(e) => handleHeaderValueChange(e, header, 'enabled')}
/>
<input type="checkbox" checked={header.enabled} className="mr-3 mousetrap" onChange={(e) => handleHeaderValueChange(e, header, 'enabled')} />
<button onClick={() => handleRemoveHeader(header)}>
<IconTrash strokeWidth={1.5} size={20} />
</button>
@ -109,11 +120,14 @@ const RequestHeaders = ({item, collection}) => {
</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
<button className="btn-add-header select-none" onClick={addHeader}>+ Add Header</button>
<button className="btn-add-header select-none" onClick={addHeader}>
+ Add Header
</button>
</StyledWrapper>
)
);
};
export default RequestHeaders;

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import { faFolder } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFolder } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import StyledWrapper from './StyledWrapper';
import Modal from 'components//Modal';
@ -9,14 +9,14 @@ const SaveRequest = ({items, onClose}) => {
useEffect(() => {
setShowFolders(items || []);
}, [items])
}, [items]);
const handleFolderClick = (folder) => {
let subFolders = [];
if (folder.items && folder.items.length) {
for (let item of folder.items) {
if (item.items) {
subFolders.push(item)
subFolders.push(item);
}
}
@ -24,30 +24,21 @@ const SaveRequest = ({items, onClose}) => {
setShowFolders(subFolders);
}
}
}
};
return (
<StyledWrapper>
<Modal
size ="md"
title ="Save Request"
confirmText ="Save"
cancelText ="Cancel"
handleCancel = {onClose}
handleConfirm = {onClose}
>
<Modal size="md" title="Save Request" confirmText="Save" cancelText="Cancel" handleCancel={onClose} handleConfirm={onClose}>
<p className="mb-2">Select a folder to save request:</p>
<div className="folder-list">
{showFolders && showFolders.length ? showFolders.map((folder) => (
<div
key={folder.uid}
className="folder-name"
onClick={() => handleFolderClick(folder)}
>
{showFolders && showFolders.length
? showFolders.map((folder) => (
<div key={folder.uid} className="folder-name" onClick={() => handleFolderClick(folder)}>
<FontAwesomeIcon className="mr-3 text-gray-500" icon={faFolder} style={{ fontSize: 20 }} />
{folder.name}
</div>
)): null}
))
: null}
</div>
</Modal>
</StyledWrapper>

View File

@ -6,18 +6,18 @@ const RequestNotFound = ({itemUid}) => {
const dispatch = useDispatch();
const closeTab = () => {
dispatch(closeTabs({
dispatch(
closeTabs({
tabUids: [itemUid]
}));
})
);
};
return (
<div className="mt-6 px-6">
<div className="p-4 bg-orange-100 border-l-4 border-yellow-500 text-yellow-700 bg-yellow-100 p-4">
<div>Request no longer exists.</div>
<div className="mt-2">
This can happen when the yml file associated with this request was deleted on your filesystem.
</div>
<div className="mt-2">This can happen when the yml file associated with this request was deleted on your filesystem.</div>
</div>
<button className="btn btn-md btn-secondary mt-6" onClick={closeTab}>
Close Tab

View File

@ -28,7 +28,7 @@ const RequestTabPanel = () => {
let asideWidth = useSelector((state) => state.app.leftSidebarWidth);
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
const [leftPaneWidth, setLeftPaneWidth] = useState(focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : ((screenWidth - asideWidth)/2.2)); // 2.2 so that request pane is relatively smaller
const [leftPaneWidth, setLeftPaneWidth] = useState(focusedTab && focusedTab.requestPaneWidth ? focusedTab.requestPaneWidth : (screenWidth - asideWidth) / 2.2); // 2.2 so that request pane is relatively smaller
const [rightPaneWidth, setRightPaneWidth] = useState(screenWidth - asideWidth - leftPaneWidth - 5);
const [dragging, setDragging] = useState(false);
@ -45,17 +45,19 @@ const RequestTabPanel = () => {
if (dragging) {
e.preventDefault();
setLeftPaneWidth(e.clientX - asideWidth - 5);
setRightPaneWidth(screenWidth - (e.clientX) - 5);
setRightPaneWidth(screenWidth - e.clientX - 5);
}
};
const handleMouseUp = (e) => {
if (dragging) {
e.preventDefault();
setDragging(false);
dispatch(updateRequestPaneTabWidth({
dispatch(
updateRequestPaneTabWidth({
uid: activeTabUid,
requestPaneWidth: e.clientX - asideWidth - 5
}));
})
);
}
};
const handleDragbarMouseDown = (e) => {
@ -78,38 +80,30 @@ const RequestTabPanel = () => {
};
}, [dragging, asideWidth]);
if (!activeTabUid) {
return (
<Welcome/>
);
return <Welcome />;
}
if (!focusedTab || !focusedTab.uid || !focusedTab.collectionUid) {
return (
<div className="pb-4 px-4">An error occured!</div>
);
return <div className="pb-4 px-4">An error occured!</div>;
}
let collection = find(collections, (c) => c.uid === focusedTab.collectionUid);
if (!collection || !collection.uid) {
return (
<div className="pb-4 px-4">Collection not found!</div>
);
return <div className="pb-4 px-4">Collection not found!</div>;
}
const item = findItemInCollection(collection, activeTabUid);
if (!item || !item.uid) {
return (
<RequestNotFound itemUid={activeTabUid}/>
);
};
return <RequestNotFound itemUid={activeTabUid} />;
}
const handleRun = async () => {
dispatch(sendRequest(item, collection.uid))
.catch((err) => toast.custom((t) => (<NetworkError onClose={() => toast.dismiss(t.id)}/>), {
dispatch(sendRequest(item, collection.uid)).catch((err) =>
toast.custom((t) => <NetworkError onClose={() => toast.dismiss(t.id)} />, {
duration: 5000
}));
})
);
};
const onGraphqlQueryChange = (value) => {};
const runQuery = async () => {};
@ -117,18 +111,11 @@ const RequestTabPanel = () => {
return (
<StyledWrapper className={`flex flex-col flex-grow ${dragging ? 'dragging' : ''}`}>
<div className="pt-4 pb-3 px-4">
<QueryUrl
item = {item}
collection={collection}
handleRun={handleRun}
/>
<QueryUrl item={item} collection={collection} handleRun={handleRun} />
</div>
<section className="main flex flex-grow pb-4">
<section className="request-pane">
<div
className="px-4"
style={{width: `${leftPaneWidth}px`, height: 'calc(100% - 5px)'}}
>
<div className="px-4" style={{ width: `${leftPaneWidth}px`, height: 'calc(100% - 5px)' }}>
{item.type === 'graphql-request' ? (
<GraphQLRequestPane
onRunQuery={runQuery}
@ -139,13 +126,7 @@ const RequestTabPanel = () => {
/>
) : null}
{item.type === 'http-request' ? (
<HttpRequestPane
item={item}
collection={collection}
leftPaneWidth={leftPaneWidth}
/>
) : null}
{item.type === 'http-request' ? <HttpRequestPane item={item} collection={collection} leftPaneWidth={leftPaneWidth} /> : null}
</div>
</section>
@ -154,16 +135,11 @@ const RequestTabPanel = () => {
</div>
<section className="response-pane flex-grow">
<ResponsePane
item={item}
collection={collection}
rightPaneWidth={rightPaneWidth}
response={item.response}
/>
<ResponsePane item={item} collection={collection} rightPaneWidth={rightPaneWidth} response={item.response} />
</section>
</section>
</StyledWrapper>
)
);
};
export default RequestTabPanel;

View File

@ -1,6 +1,5 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
`;
const StyledWrapper = styled.div``;
export default StyledWrapper;

View File

@ -16,7 +16,7 @@ const CollectionToolBar = ({collection}) => {
</div>
</div>
</StyledWrapper>
)
);
};
export default CollectionToolBar;

View File

@ -25,7 +25,8 @@ const StyledWrapper = styled.div`
padding-top: 6px;
}
&:hover, &:hover .close-icon {
&:hover,
&:hover .close-icon {
background-color: #eaeaea;
color: rgb(76 76 76);
}
@ -37,4 +38,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -12,9 +12,11 @@ const RequestTab = ({tab, collection}) => {
const handleCloseClick = (event) => {
event.stopPropagation();
event.preventDefault();
dispatch(closeTabs({
dispatch(
closeTabs({
tabUids: [tab.uid]
}))
})
);
};
const getMethodColor = (method = '') => {
@ -65,7 +67,10 @@ const RequestTab = ({tab, collection}) => {
</div>
<div className="flex px-2 close-icon-container" onClick={(e) => handleCloseClick(e)}>
<svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" className="close-icon">
<path fill="currentColor" d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"></path>
<path
fill="currentColor"
d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"
></path>
</svg>
</div>
</StyledWrapper>
@ -77,13 +82,20 @@ const RequestTab = ({tab, collection}) => {
return (
<StyledWrapper className="flex items-center justify-between tab-container px-1">
<div className="flex items-baseline tab-label pl-2">
<span className="tab-method uppercase" style={{color: getMethodColor(method), fontSize: 12}}>{method}</span>
<span className="text-gray-700 ml-1 tab-name" title={item.name}>{item.name}</span>
<span className="tab-method uppercase" style={{ color: getMethodColor(method), fontSize: 12 }}>
{method}
</span>
<span className="text-gray-700 ml-1 tab-name" title={item.name}>
{item.name}
</span>
</div>
<div className="flex px-2 close-icon-container" onClick={(e) => handleCloseClick(e)}>
{!item.draft ? (
<svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" className="close-icon">
<path fill="currentColor" d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"></path>
<path
fill="currentColor"
d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"
></path>
</svg>
) : (
<svg focusable="false" xmlns="http://www.w3.org/2000/svg" width="8" height="16" fill="#cc7b1b" className="has-changes-icon" viewBox="0 0 8 8">

View File

@ -21,16 +21,18 @@ const RequestTabs = () => {
const screenWidth = useSelector((state) => state.app.screenWidth);
const getTabClassname = (tab, index) => {
return classnames("request-tab select-none", {
'active': tab.uid === activeTabUid,
'last-tab': tabs && tabs.length && (index === tabs.length - 1)
return classnames('request-tab select-none', {
active: tab.uid === activeTabUid,
'last-tab': tabs && tabs.length && index === tabs.length - 1
});
};
const handleClick = (tab) => {
dispatch(focusTab({
dispatch(
focusTab({
uid: tab.uid
}));
})
);
};
const createNewTab = () => setNewRequestModalOpen(true);
@ -41,18 +43,14 @@ const RequestTabs = () => {
const activeTab = find(tabs, (t) => t.uid === activeTabUid);
if (!activeTab) {
return (
<StyledWrapper>
Something went wrong!
</StyledWrapper>
);
return <StyledWrapper>Something went wrong!</StyledWrapper>;
}
const activeCollection = find(collections, (c) => c.uid === activeTab.collectionUid);
const collectionRequestTabs = filter(tabs, (t) => t.collectionUid === activeTab.collectionUid);
const maxTablistWidth = screenWidth - leftSidebarWidth - 150;
const tabsWidth = (collectionRequestTabs.length * 150) + 34; // 34: (+)icon
const tabsWidth = collectionRequestTabs.length * 150 + 34; // 34: (+)icon
const showChevrons = maxTablistWidth < tabsWidth;
const leftSlide = () => {
@ -102,13 +100,15 @@ const RequestTabs = () => {
</li> */}
</ul>
<ul role="tablist" style={{ maxWidth: maxTablistWidth }} ref={tabsRef}>
{collectionRequestTabs && collectionRequestTabs.length ? collectionRequestTabs.map((tab, index) => {
{collectionRequestTabs && collectionRequestTabs.length
? collectionRequestTabs.map((tab, index) => {
return (
<li key={tab.uid} className={getTabClassname(tab, index)} role="tab" onClick={() => handleClick(tab)}>
<RequestTab key={tab.uid} tab={tab} collection={activeCollection} activeTab={activeTab} />
</li>
)
}) : null}
);
})
: null}
</ul>
<ul role="tablist">

View File

@ -6,12 +6,9 @@ const NetworkError = ({onClose}) => {
<div className="flex-1 w-0 p-4">
<div className="flex items-start">
<div className="ml-3 flex-1">
<p className="text-sm font-medium text-red-800">
Network Error
</p>
<p className="text-sm font-medium text-red-800">Network Error</p>
<p className="mt-2 text-xs text-gray-500">
Please note that if you are using Bruno on the web, then the api you are connecting to must allow CORS.
If not, please use the chrome extension or the desktop app
Please note that if you are using Bruno on the web, then the api you are connecting to must allow CORS. If not, please use the chrome extension or the desktop app
</p>
</div>
</div>

View File

@ -18,4 +18,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -23,7 +23,8 @@ const ResponseLoadingOverlay = ({item, collection}) => {
<IconRefresh size={24} className="animate-spin" />
<button
onClick={handleCancelRequest}
className="mt-4 uppercase bg-gray-200 active:bg-blueGray-600 text-xs px-4 py-2 rounded shadow hover:shadow-md outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150" type="button"
className="mt-4 uppercase bg-gray-200 active:bg-blueGray-600 text-xs px-4 py-2 rounded shadow hover:shadow-md outline-none focus:outline-none mr-1 mb-1 ease-linear transition-all duration-150"
type="button"
>
Cancel Request
</button>

View File

@ -6,4 +6,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -8,4 +8,3 @@ const StyledWrapper = styled.div`
`;
export default StyledWrapper;

View File

@ -12,17 +12,19 @@ const ResponseHeaders = ({headers}) => {
</tr>
</thead>
<tbody>
{headers && headers.length ? headers.map((header, index) => {
{headers && headers.length
? headers.map((header, index) => {
return (
<tr key={index}>
<td className="key">{header[0]}</td>
<td className="value">{header[1]}</td>
</tr>
);
}) : null}
})
: null}
</tbody>
</table>
</StyledWrapper>
)
);
};
export default ResponseHeaders;

View File

@ -2,20 +2,17 @@ import React from 'react';
import StyledWrapper from './StyledWrapper';
const ResponseSize = ({ size }) => {
let sizeToDisplay = ''
let sizeToDisplay = '';
if(size > 1024 ) { // size is greater than 1kb
if (size > 1024) {
// size is greater than 1kb
let kb = Math.floor(size / 1024);
let decimal = ((size % 1024) / 1024).toFixed(2) * 100;
sizeToDisplay = kb + '.' + decimal + 'KB';
} else {
sizeToDisplay = size + 'B'
sizeToDisplay = size + 'B';
}
return (
<StyledWrapper className="ml-4">
{sizeToDisplay}
</StyledWrapper>
)
return <StyledWrapper className="ml-4">{sizeToDisplay}</StyledWrapper>;
};
export default ResponseSize;

View File

@ -2,20 +2,17 @@ import React from 'react';
import StyledWrapper from './StyledWrapper';
const ResponseTime = ({ duration }) => {
let durationToDisplay = ''
let durationToDisplay = '';
if(duration > 1000 ) { // duration greater than a second
if (duration > 1000) {
// duration greater than a second
let seconds = Math.floor(duration / 1000);
let decimal = ((duration % 1000) / 1000) * 100;
durationToDisplay = seconds + '.' + decimal.toFixed(0) + 's';
} else {
durationToDisplay = duration + 'ms'
durationToDisplay = duration + 'ms';
}
return (
<StyledWrapper className="ml-4">
{durationToDisplay}
</StyledWrapper>
)
return <StyledWrapper className="ml-4">{durationToDisplay}</StyledWrapper>;
};
export default ResponseTime;

View File

@ -41,7 +41,7 @@ const statusCodePhraseMap = {
415: 'Unsupported Media Type',
416: 'Range Not Satisfiable',
417: 'Expectation Failed',
418: 'I\'m a teapot',
418: "I'm a teapot",
421: 'Misdirected Request',
422: 'Unprocessable Entity',
423: 'Locked',

View File

@ -18,6 +18,6 @@ const StatusCode = ({status}) => {
<StyledWrapper className={getTabClassname()}>
{status} {statusCodePhraseMap[status]}
</StyledWrapper>
)
);
};
export default StatusCode;

View File

@ -10,7 +10,11 @@ const StyledWrapper = styled.div`
color: var(--color-tab-inactive);
cursor: pointer;
&:focus, &:active, &:focus-within, &:focus-visible, &:target {
&:focus,
&:active,
&:focus-within,
&:focus-visible,
&:target {
outline: none !important;
box-shadow: none !important;
}

View File

@ -19,10 +19,12 @@ const ResponsePane = ({rightPaneWidth, item, collection}) => {
const isLoading = item.response && item.response.state === 'sending';
const selectTab = (tab) => {
dispatch(updateResponsePaneTab({
dispatch(
updateResponsePaneTab({
uid: item.uid,
responsePaneTab: tab
}))
})
);
};
const response = item.response || {};
@ -30,17 +32,10 @@ const ResponsePane = ({rightPaneWidth, item, collection}) => {
const getTabPanel = (tab) => {
switch (tab) {
case 'response': {
return (
<QueryResult
width={rightPaneWidth}
value={response.data ? JSON.stringify(response.data, null, 2) : ''}
/>
);
return <QueryResult width={rightPaneWidth} value={response.data ? JSON.stringify(response.data, null, 2) : ''} />;
}
case 'headers': {
return (
<ResponseHeaders headers={response.headers}/>
);
return <ResponseHeaders headers={response.headers} />;
}
default: {
@ -66,29 +61,29 @@ const ResponsePane = ({rightPaneWidth, item, collection}) => {
}
if (!activeTabUid) {
return (
<div>Something went wrong</div>
);
return <div>Something went wrong</div>;
}
const focusedTab = find(tabs, (t) => t.uid === activeTabUid);
if (!focusedTab || !focusedTab.uid || !focusedTab.responsePaneTab) {
return (
<div className="pb-4 px-4">An error occured!</div>
);
return <div className="pb-4 px-4">An error occured!</div>;
}
const getTabClassname = (tabName) => {
return classnames(`tab select-none ${tabName}`, {
'active': tabName === focusedTab.responsePaneTab
active: tabName === focusedTab.responsePaneTab
});
};
return (
<StyledWrapper className="flex flex-col h-full relative">
<div className="flex items-center px-3 tabs" role="tablist">
<div className={getTabClassname('response')} role="tab" onClick={() => selectTab('response')}>Response</div>
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>Headers</div>
<div className={getTabClassname('response')} role="tab" onClick={() => selectTab('response')}>
Response
</div>
<div className={getTabClassname('headers')} role="tab" onClick={() => selectTab('headers')}>
Headers
</div>
{!isLoading ? (
<div className="flex flex-grow justify-end items-center">
<StatusCode status={response.status} />
@ -97,11 +92,9 @@ const ResponsePane = ({rightPaneWidth, item, collection}) => {
</div>
) : null}
</div>
<section className="flex flex-grow mt-5">
{getTabPanel(focusedTab.responsePaneTab)}
</section>
<section className="flex flex-grow mt-5">{getTabPanel(focusedTab.responsePaneTab)}</section>
</StyledWrapper>
)
);
};
export default ResponsePane;

View File

@ -16,10 +16,7 @@ const CloneCollectionItem = ({collection, item, onClose}) => {
name: item.name
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(cloneItem(values.name, item.uid, collection.uid));
@ -36,27 +33,26 @@ const CloneCollectionItem = ({collection, item, onClose}) => {
const onSubmit = () => formik.handleSubmit();
return (
<Modal
size="sm"
title={`Clone ${isFolder ? 'Folder' : 'Request'}`}
confirmText='Clone'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title={`Clone ${isFolder ? 'Folder' : 'Request'}`} confirmText="Clone" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">{isFolder ? 'Folder' : 'Request'} Name</label>
<label htmlFor="name" className="block font-semibold">
{isFolder ? 'Folder' : 'Request'} Name
</label>
<input
id="collection-item-name" type="text" name="name"
id="collection-item-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>

View File

@ -11,16 +11,19 @@ const DeleteCollectionItem = ({onClose, item, collection}) => {
const dispatch = useDispatch();
const isFolder = isItemAFolder(item);
const onConfirm = () => {
dispatch(deleteItem(item.uid, collection.uid))
.then(() => {
dispatch(deleteItem(item.uid, collection.uid)).then(() => {
if (isFolder) {
dispatch(closeTabs({
dispatch(
closeTabs({
tabUids: recursivelyGetAllItemUids(item.items)
}));
})
);
} else {
dispatch(closeTabs({
dispatch(
closeTabs({
tabUids: [item.uid]
}));
})
);
}
});
onClose();
@ -28,13 +31,7 @@ const DeleteCollectionItem = ({onClose, item, collection}) => {
return (
<StyledWrapper>
<Modal
size="sm"
title={`Delete ${isFolder ? 'Folder' : 'Request'}`}
confirmText="Delete"
handleConfirm={onConfirm}
handleCancel={onClose}
>
<Modal size="sm" title={`Delete ${isFolder ? 'Folder' : 'Request'}`} confirmText="Delete" handleConfirm={onConfirm} handleCancel={onClose}>
Are you sure you want to delete <span className="font-semibold">{item.name}</span> ?
</Modal>
</StyledWrapper>

View File

@ -16,10 +16,7 @@ const RenameCollectionItem = ({collection, item, onClose}) => {
name: item.name
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(renameItem(values.name, item.uid, collection.uid));
@ -36,27 +33,26 @@ const RenameCollectionItem = ({collection, item, onClose}) => {
const onSubmit = () => formik.handleSubmit();
return (
<Modal
size="sm"
title={`Rename ${isFolder ? 'Folder' : 'Request'}`}
confirmText='Rename'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title={`Rename ${isFolder ? 'Folder' : 'Request'}`} confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">{isFolder ? 'Folder' : 'Request'} Name</label>
<label htmlFor="name" className="block font-semibold">
{isFolder ? 'Folder' : 'Request'} Name
</label>
<input
id="collection-item-name" type="text" name="name"
id="collection-item-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>

View File

@ -12,13 +12,27 @@ const Wrapper = styled.div`
top: 1px;
}
.method-get { color: var(--color-method-get);}
.method-post { color: var(--color-method-post);}
.method-put { color: var(--color-method-put);}
.method-delete { color: var(--color-method-delete);}
.method-patch { color: var(--color-method-patch);}
.method-options { color: var(--color-method-options);}
.method-head { color: var(--color-method-head);}
.method-get {
color: var(--color-method-get);
}
.method-post {
color: var(--color-method-post);
}
.method-put {
color: var(--color-method-put);
}
.method-delete {
color: var(--color-method-delete);
}
.method-patch {
color: var(--color-method-patch);
}
.method-options {
color: var(--color-method-options);
}
.method-head {
color: var(--color-method-head);
}
`;
export default Wrapper;

View File

@ -9,7 +9,7 @@ const RequestMethod = ({item}) => {
const getClassname = (method = '') => {
method = method.toLocaleLowerCase();
return classnames("mr-1", {
return classnames('mr-1', {
'method-get': method === 'get',
'method-post': method === 'post',
'method-put': method === 'put',

View File

@ -5,10 +5,10 @@ const Wrapper = styled.div`
color: rgb(110 110 110);
.dropdown {
div[aria-expanded="true"] {
div[aria-expanded='true'] {
visibility: visible;
}
div[aria-expanded="false"] {
div[aria-expanded='false'] {
visibility: hidden;
}
}
@ -37,7 +37,7 @@ const Wrapper = styled.div`
background: #e7e7e7;
.menu-icon {
.dropdown {
div[aria-expanded="false"] {
div[aria-expanded='false'] {
visibility: visible;
}
}

View File

@ -60,26 +60,32 @@ const CollectionItem = ({item, collection, searchText}) => {
const handleClick = (event) => {
if (isItemARequest(item)) {
if (itemIsOpenedInTabs(item, tabs)) {
dispatch(focusTab({
dispatch(
focusTab({
uid: item.uid
}));
})
);
} else {
dispatch(addTab({
dispatch(
addTab({
uid: item.uid,
collectionUid: collection.uid
}));
})
);
}
dispatch(hideHomePage());
} else {
dispatch(collectionFolderClicked({
dispatch(
collectionFolderClicked({
itemUid: item.uid,
collectionUid: collection.uid
}));
})
);
}
};
let indents = range(item.depth);
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const isFolder = isItemAFolder(item);
const className = classnames('flex flex-col w-full', {
@ -94,7 +100,7 @@ const CollectionItem = ({item, collection, searchText}) => {
} else {
if (!doesFolderHaveItemsMatchSearchText(item, searchText)) {
return null;
};
}
}
}
@ -110,7 +116,8 @@ const CollectionItem = ({item, collection, searchText}) => {
{newFolderModalOpen && <NewFolder item={item} collection={collection} onClose={() => setNewFolderModalOpen(false)} />}
<div className={itemRowClassName}>
<div className="flex items-center h-full w-full">
{indents && indents.length ? indents.map((i) => {
{indents && indents.length
? indents.map((i) => {
return (
<div
onClick={handleClick}
@ -125,7 +132,8 @@ const CollectionItem = ({item, collection, searchText}) => {
&nbsp;{/* Indent */}
</div>
);
}) : null}
})
: null}
<div
onClick={handleClick}
className="flex flex-grow items-center h-full overflow-hidden"
@ -134,52 +142,67 @@ const CollectionItem = ({item, collection, searchText}) => {
}}
>
<div style={{ width: 16, minWidth: 16 }}>
{isFolder ? (
<IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{color: 'rgb(160 160 160)'}}/>
) : null}
{isFolder ? <IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{ color: 'rgb(160 160 160)' }} /> : null}
</div>
<div className="ml-1 flex items-center overflow-hidden">
<RequestMethod item={item} />
<span className="item-name" title={item.name}>{item.name}</span>
<span className="item-name" title={item.name}>
{item.name}
</span>
</div>
</div>
<div className="menu-icon pr-2">
<Dropdown onCreate={onDropdownCreate} icon={<MenuIcon />} placement='bottom-start'>
<Dropdown onCreate={onDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
{isFolder && (
<>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setNewRequestModalOpen(true);
}}>
}}
>
New Request
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setNewFolderModalOpen(true);
}}>
}}
>
New Folder
</div>
</>
)}
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setRenameItemModalOpen(true);
}}>
}}
>
Rename
</div>
{!isFolder && (
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setCloneItemModalOpen(true);
}}>
}}
>
Clone
</div>
)}
<div className="dropdown-item delete-item" onClick={(e) => {
<div
className="dropdown-item delete-item"
onClick={(e) => {
dropdownTippyRef.current.hide();
setDeleteItemModalOpen(true);
}}>
}}
>
Delete
</div>
</Dropdown>
@ -189,22 +212,16 @@ const CollectionItem = ({item, collection, searchText}) => {
{!itemIsCollapsed ? (
<div>
{requestItems && requestItems.length ? requestItems.map((i) => {
return <CollectionItem
key={i.uid}
item={i}
collection={collection}
searchText={searchText}
/>
}) : null}
{folderItems && folderItems.length ? folderItems.map((i) => {
return <CollectionItem
key={i.uid}
item={i}
collection={collection}
searchText={searchText}
/>
}) : null}
{requestItems && requestItems.length
? requestItems.map((i) => {
return <CollectionItem key={i.uid} item={i} collection={collection} searchText={searchText} />;
})
: null}
{folderItems && folderItems.length
? folderItems.map((i) => {
return <CollectionItem key={i.uid} item={i} collection={collection} searchText={searchText} />;
})
: null}
</div>
) : null}
</StyledWrapper>

View File

@ -10,20 +10,14 @@ const DeleteCollection = ({onClose, collection}) => {
const onConfirm = () => {
dispatch(deleteCollection(collection.uid))
.then(() => {
toast.success("Collection deleted");
toast.success('Collection deleted');
})
.catch(() => toast.error("An error occured while deleting the collection"));
.catch(() => toast.error('An error occured while deleting the collection'));
};
return (
<StyledWrapper>
<Modal
size="sm"
title="Delete Collection"
confirmText="Delete"
handleConfirm={onConfirm}
handleCancel={onClose}
>
<Modal size="sm" title="Delete Collection" confirmText="Delete" handleConfirm={onConfirm} handleCancel={onClose}>
Are you sure you want to delete the collection <span className="font-semibold">{collection.name}</span> ?
</Modal>
</StyledWrapper>

View File

@ -14,23 +14,19 @@ const RemoveCollectionFromWorkspace = ({onClose, collection}) => {
const onConfirm = () => {
dispatch(removeCollectionFromWorkspace(activeWorkspaceUid, collection.uid))
.then(() => {
dispatch(closeTabs({
dispatch(
closeTabs({
tabUids: recursivelyGetAllItemUids(collection.items)
}));
})
);
})
.then(() => dispatch(removeLocalCollection(collection.uid)))
.then(() => toast.success("Collection removed from workspace"))
.catch((err) => console.log(err) && toast.error("An error occured while removing the collection"));
.then(() => toast.success('Collection removed from workspace'))
.catch((err) => console.log(err) && toast.error('An error occured while removing the collection'));
};
return (
<Modal
size="sm"
title="Remove Collection from Workspace"
confirmText="Remove"
handleConfirm={onConfirm}
handleCancel={onClose}
>
<Modal size="sm" title="Remove Collection from Workspace" confirmText="Remove" handleConfirm={onConfirm} handleCancel={onClose}>
Are you sure you want to remove the collection <span className="font-semibold">{collection.name}</span> from this workspace?
</Modal>
);

View File

@ -10,20 +10,14 @@ const RemoveLocalCollection = ({onClose, collection}) => {
const onConfirm = () => {
dispatch(removeLocalCollection(collection.uid))
.then(() => {
toast.success("Collection removed");
toast.success('Collection removed');
onClose();
})
.catch(() => toast.error("An error occured while removing the collection"));
.catch(() => toast.error('An error occured while removing the collection'));
};
return (
<Modal
size="sm"
title="Remove Collection"
confirmText="Remove"
handleConfirm={onConfirm}
handleCancel={onClose}
>
<Modal size="sm" title="Remove Collection" confirmText="Remove" handleConfirm={onConfirm} handleCancel={onClose}>
Are you sure you want to remove this collection?
</Modal>
);

View File

@ -15,10 +15,7 @@ const RenameCollection = ({collection, onClose}) => {
name: collection.name
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(renameCollection(values.name, collection.uid));
@ -36,27 +33,26 @@ const RenameCollection = ({collection, onClose}) => {
const onSubmit = () => formik.handleSubmit();
return (
<Modal
size="sm"
title="Rename Collection"
confirmText='Rename'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title="Rename Collection" confirmText="Rename" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">Name</label>
<label htmlFor="name" className="block font-semibold">
Name
</label>
<input
id="collection-name" type="text" name="name"
id="collection-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>

View File

@ -14,15 +14,14 @@ const Wrapper = styled.div`
.collection-actions {
.dropdown {
div[aria-expanded="true"] {
div[aria-expanded='true'] {
visibility: visible;
}
div[aria-expanded="false"] {
div[aria-expanded='false'] {
visibility: hidden;
}
}
svg {
height: 22px;
color: rgb(110 110 110);
@ -32,7 +31,7 @@ const Wrapper = styled.div`
&:hover {
.collection-actions {
.dropdown {
div[aria-expanded="false"] {
div[aria-expanded='false'] {
visibility: visible;
}
}

View File

@ -30,7 +30,7 @@ const Collection = ({collection, searchText}) => {
const dispatch = useDispatch();
const menuDropdownTippyRef = useRef();
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
const onMenuDropdownCreate = (ref) => (menuDropdownTippyRef.current = ref);
const MenuIcon = forwardRef((props, ref) => {
return (
<div ref={ref} className="pr-2">
@ -84,58 +84,75 @@ const Collection = ({collection, searchText}) => {
<IconChevronRight size={16} strokeWidth={2} className={iconClassName} style={{ width: 16, color: 'rgb(160 160 160)' }} />
<div className="ml-1">{collection.name}</div>
</div>
<div className='collection-actions'>
<Dropdown
onCreate={onMenuDropdownCreate}
icon={<MenuIcon />}
placement='bottom-start'
>
<div className="dropdown-item" onClick={(e) => {
<div className="collection-actions">
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowNewRequestModal(true);
}}>
}}
>
New Request
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowNewFolderModal(true);
}}>
}}
>
New Folder
</div>
{!isLocal ? (
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowRenameCollectionModal(true);
}}>
}}
>
Rename
</div>
) : null}
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
handleExportClick(true);
}}>
}}
>
Export
</div>
{!isLocal ? (
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowRemoveCollectionFromWSModal(true);
}}>
}}
>
Remove from Workspace
</div>
) : (
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowRemoveLocalCollectionModal(true);
}}>
}}
>
Remove
</div>
)}
{!isLocal ? (
<div className="dropdown-item delete-collection" onClick={(e) => {
<div
className="dropdown-item delete-collection"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setShowDeleteCollectionModal(true);
}}>
}}
>
Delete
</div>
) : null}
@ -146,23 +163,17 @@ const Collection = ({collection, searchText}) => {
<div>
{!collectionIsCollapsed ? (
<div>
{requestItems && requestItems.length ? requestItems.map((i) => {
return <CollectionItem
key={i.uid}
item={i}
collection={collection}
searchText={searchText}
/>
}) : null}
{requestItems && requestItems.length
? requestItems.map((i) => {
return <CollectionItem key={i.uid} item={i} collection={collection} searchText={searchText} />;
})
: null}
{folderItems && folderItems.length ? folderItems.map((i) => {
return <CollectionItem
key={i.uid}
item={i}
collection={collection}
searchText={searchText}
/>
}) : null}
{folderItems && folderItems.length
? folderItems.map((i) => {
return <CollectionItem key={i.uid} item={i} collection={collection} searchText={searchText} />;
})
: null}
</div>
) : null}
</div>

View File

@ -16,43 +16,42 @@ const CreateOrAddCollection = () => {
setCreateCollectionModalOpen(false);
dispatch(createCollection(values.collectionName))
.then(() => {
toast.success("Collection created");
toast.success('Collection created');
})
.catch(() => toast.error("An error occured while creating the collection"));
.catch(() => toast.error('An error occured while creating the collection'));
};
const handleAddCollectionToWorkspace = (collectionUid) => {
setAddCollectionToWSModalOpen(false);
dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid))
.then(() => {
toast.success("Collection added to workspace");
toast.success('Collection added to workspace');
})
.catch(() => toast.error("An error occured while adding collection to workspace"));
.catch(() => toast.error('An error occured while adding collection to workspace'));
};
const CreateLink = () => <span className='underline text-link cursor-pointer' onClick={() => setCreateCollectionModalOpen(true)}>Create</span>;
const AddLink = () => <span className='underline text-link cursor-pointer' onClick={() => setAddCollectionToWSModalOpen(true)}>Add</span>;
const CreateLink = () => (
<span className="underline text-link cursor-pointer" onClick={() => setCreateCollectionModalOpen(true)}>
Create
</span>
);
const AddLink = () => (
<span className="underline text-link cursor-pointer" onClick={() => setAddCollectionToWSModalOpen(true)}>
Add
</span>
);
return (
<div className='px-2 mt-4 text-gray-600'>
{createCollectionModalOpen ? (
<CreateCollection
handleCancel={() => setCreateCollectionModalOpen(false)}
handleConfirm={handleCreateCollection}
/>
) : null}
<div className="px-2 mt-4 text-gray-600">
{createCollectionModalOpen ? <CreateCollection handleCancel={() => setCreateCollectionModalOpen(false)} handleConfirm={handleCreateCollection} /> : null}
{addCollectionToWSModalOpen ? (
<SelectCollection
title='Add Collection to Workspace'
onClose={() => setAddCollectionToWSModalOpen(false)}
onSelect={handleAddCollectionToWorkspace}
/>
<SelectCollection title="Add Collection to Workspace" onClose={() => setAddCollectionToWSModalOpen(false)} onSelect={handleAddCollectionToWorkspace} />
) : null}
<div className='text-xs text-center'>
<div className="text-xs text-center">
<div>No collections found.</div>
<div className='mt-2'>
<div className="mt-2">
<CreateLink /> or <AddLink /> Collection to Workspace.
</div>
</div>

View File

@ -1,9 +1,9 @@
import React from "react";
import filter from "lodash/filter";
import Modal from "components/Modal/index";
import React from 'react';
import filter from 'lodash/filter';
import Modal from 'components/Modal/index';
import { IconFiles } from '@tabler/icons';
import { useSelector } from "react-redux";
import { isLocalCollection } from "utils/collections";
import { useSelector } from 'react-redux';
import { isLocalCollection } from 'utils/collections';
import StyledWrapper from './StyledWrapper';
const SelectCollection = ({ onClose, onSelect, title }) => {
@ -12,24 +12,21 @@ const SelectCollection = ({onClose, onSelect, title}) => {
return (
<StyledWrapper>
<Modal
size="sm"
title={title || "Select Collection"}
hideFooter={true}
handleCancel={onClose}
>
<Modal size="sm" title={title || 'Select Collection'} hideFooter={true} handleCancel={onClose}>
<ul className="mb-2">
{(collectionsToDisplay && collectionsToDisplay.length) ? collectionsToDisplay.map((c) => (
{collectionsToDisplay && collectionsToDisplay.length ? (
collectionsToDisplay.map((c) => (
<div className="collection" key={c.uid} onClick={() => onSelect(c.uid)}>
<IconFiles size={18} strokeWidth={1.5} /> <span className="ml-2">{c.name}</span>
</div>
)) : (
))
) : (
<div>No collections found</div>
)}
</ul>
</Modal>
</StyledWrapper>
);
}
};
export default SelectCollection;

View File

@ -10,7 +10,7 @@ import { isLocalCollection } from 'utils/collections';
const Collections = ({ searchText }) => {
const { collections } = useSelector((state) => state.collections);
const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces);
const activeWorkspace = find(workspaces, w => w.uid === activeWorkspaceUid);
const activeWorkspace = find(workspaces, (w) => w.uid === activeWorkspaceUid);
if (!activeWorkspace) {
return null;
@ -24,13 +24,11 @@ const Collections = ({searchText}) => {
return (
<div className="mt-4 flex flex-col">
{collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => {
return <Collection
searchText={searchText}
collection={c}
key={c.uid}
/>
}) : null}
{collectionToDisplay && collectionToDisplay.length
? collectionToDisplay.map((c) => {
return <Collection searchText={searchText} collection={c} key={c.uid} />;
})
: null}
</div>
);
};

View File

@ -20,19 +20,16 @@ const CreateCollection = ({onClose, isLocal}) => {
collectionLocation: ''
},
validationSchema: Yup.object({
collectionName: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
collectionName: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
const action = isLocal && isPlatformElectron ? createLocalCollection : createCollection;
dispatch(action(values.collectionName, values.collectionLocation))
.then(() => {
toast.success("Collection created");
toast.success('Collection created');
onClose();
})
.catch(() => toast.error("An error occured while creating the collection"));
.catch(() => toast.error('An error occured while creating the collection'));
}
});
@ -56,16 +53,12 @@ const CreateCollection = ({onClose, isLocal}) => {
const onSubmit = () => formik.handleSubmit();
return (
<Modal
size="sm"
title='Create Collection'
confirmText='Create'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title="Create Collection" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="collectionName" className="block font-semibold">Name</label>
<label htmlFor="collectionName" className="block font-semibold">
Name
</label>
<input
id="collection-name"
type="text"
@ -73,23 +66,29 @@ const CreateCollection = ({onClose, isLocal}) => {
ref={inputRef}
className="block textbox mt-2 w-full"
onChange={formik.handleChange}
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.collectionName || ''}
/>
{formik.touched.collectionName && formik.errors.collectionName ? (
<div className="text-red-500">{formik.errors.collectionName}</div>
) : null}
{formik.touched.collectionName && formik.errors.collectionName ? <div className="text-red-500">{formik.errors.collectionName}</div> : null}
{isLocal && isPlatformElectron ? (
<>
<label htmlFor="collectionLocation" className="block font-semibold mt-3">Location</label>
<label htmlFor="collectionLocation" className="block font-semibold mt-3">
Location
</label>
<input
id="collection-location"
type="text"
name="collectionLocation"
readOnly={true}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
value={formik.values.collectionLocation || ''}
onClick={browse}
/>
@ -101,7 +100,9 @@ const CreateCollection = ({onClose, isLocal}) => {
{isLocal && isPlatformElectron ? (
<div className="mt-1">
<span className="text-link cursor-pointer hover:underline" onClick={browse}>Browse</span>
<span className="text-link cursor-pointer hover:underline" onClick={browse}>
Browse
</span>
</div>
) : null}
</div>

View File

@ -2,7 +2,7 @@ import styled from 'styled-components';
const Wrapper = styled.div`
.current-workspace {
margin-inline: .5rem;
margin-inline: 0.5rem;
background: #e1e1e1;
border-radius: 5px;

View File

@ -24,38 +24,29 @@ const LocalCollections = ({searchText}) => {
const Icon = forwardRef((props, ref) => {
return (
<div ref={ref} className="current-workspace flex justify-between items-center pl-2 pr-2 py-1 select-none">
<div className='flex items-center'>
<span className='mr-2'>
<div className="flex items-center">
<span className="mr-2">
<IconFolders size={18} strokeWidth={1.5} />
</span>
<span>
Local Collections
</span>
<span>Local Collections</span>
</div>
<IconCaretDown className="caret" size={14} strokeWidth={2} />
</div>
);
});
const onDropdownCreate = (ref) => dropdownTippyRef.current = ref;
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
const handleOpenLocalCollection = () => {
dispatch(openLocalCollection())
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
}
dispatch(openLocalCollection()).catch((err) => console.log(err) && toast.error('An error occured while opening the local collection'));
};
return (
<StyledWrapper>
{createCollectionModalOpen ? (
<CreateCollection
isLocal={true}
onClose={() => setCreateCollectionModalOpen(false)}
/>
) : null}
{createCollectionModalOpen ? <CreateCollection isLocal={true} onClose={() => setCreateCollectionModalOpen(false)} /> : null}
<div className="items-center cursor-pointer mt-6 relative">
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement='bottom-end'>
<Dropdown onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
<div className="dropdown-item" onClick={() => setCreateCollectionModalOpen(true)}>
<div className="pr-2 text-gray-600">
<IconPlus size={18} strokeWidth={1.5} />
@ -69,19 +60,17 @@ const LocalCollections = ({searchText}) => {
<span>Open Collection</span>
</div>
<div className='px-2 pt-2 text-gray-600' style={{fontSize: 10, borderTop: 'solid 1px #e7e7e7'}}>
<div className="px-2 pt-2 text-gray-600" style={{ fontSize: 10, borderTop: 'solid 1px #e7e7e7' }}>
Note: Local collections are not tied to a workspace
</div>
</Dropdown>
</div>
<div className="mt-4 flex flex-col">
{collectionToDisplay && collectionToDisplay.length ? collectionToDisplay.map((c) => {
return <Collection
searchText={searchText}
collection={c}
key={c.uid}
/>
}) : null}
{collectionToDisplay && collectionToDisplay.length
? collectionToDisplay.map((c) => {
return <Collection searchText={searchText} collection={c} key={c.uid} />;
})
: null}
</div>
</StyledWrapper>
);

View File

@ -9,7 +9,8 @@ const Wrapper = styled.div`
padding: 0.6rem;
cursor: pointer;
&:hover, &.active {
&:hover,
&.active {
color: rgba(255, 255, 255);
}
}

View File

@ -15,7 +15,7 @@ const MenuBar = () => {
const isPlatformElectron = isElectron();
const getClassName = (menu) => {
return router.pathname === menu ? "active menu-item": "menu-item";
return router.pathname === menu ? 'active menu-item' : 'menu-item';
};
return (

View File

@ -15,15 +15,12 @@ const NewFolder = ({collection, item, onClose}) => {
folderName: ''
},
validationSchema: Yup.object({
folderName: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
folderName: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(newFolder(values.folderName, collection.uid, item ? item.uid : null))
.then(() => onClose())
.catch(() => toast.error("An error occured while adding the request"));
.catch(() => toast.error('An error occured while adding the request'));
}
});
@ -36,27 +33,26 @@ const NewFolder = ({collection, item, onClose}) => {
const onSubmit = () => formik.handleSubmit();
return (
<Modal
size="sm"
title='New Folder'
confirmText='Create'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title="New Folder" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="folderName" className="block font-semibold">Folder Name</label>
<label htmlFor="folderName" className="block font-semibold">
Folder Name
</label>
<input
id="collection-name" type="text" name="folderName"
id="collection-name"
type="text"
name="folderName"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.folderName || ''}
/>
{formik.touched.folderName && formik.errors.folderName ? (
<div className="text-red-500">{formik.errors.folderName}</div>
) : null}
{formik.touched.folderName && formik.errors.folderName ? <div className="text-red-500">{formik.errors.folderName}</div> : null}
</div>
</form>
</Modal>

View File

@ -13,7 +13,8 @@ const StyledWrapper = styled.div`
}
}
div.method-selector-container, div.input-container {
div.method-selector-container,
div.input-container {
height: 2.3rem;
}

View File

@ -2,7 +2,7 @@ import React, { useRef, useEffect } from 'react';
import { useFormik } from 'formik';
import * as Yup from 'yup';
import toast from 'react-hot-toast';
import { uuid } from 'utils/common';;
import { uuid } from 'utils/common';
import Modal from 'components/Modal';
import { useDispatch } from 'react-redux';
import { newEphermalHttpRequest } from 'providers/ReduxStore/slices/collections';
@ -23,41 +23,44 @@ const NewRequest = ({collection, item, isEphermal, onClose}) => {
requestMethod: 'GET'
},
validationSchema: Yup.object({
requestName: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(50, 'must be 50 characters or less')
.required('name is required')
requestName: Yup.string().min(1, 'must be atleast 1 characters').max(50, 'must be 50 characters or less').required('name is required')
}),
onSubmit: (values) => {
if (isEphermal) {
const uid = uuid();
dispatch(newEphermalHttpRequest({
dispatch(
newEphermalHttpRequest({
uid: uid,
requestName: values.requestName,
requestType: values.requestType,
requestUrl: values.requestUrl,
requestMethod: values.requestMethod,
collectionUid: collection.uid
}))
})
)
.then(() => {
dispatch(addTab({
dispatch(
addTab({
uid: uid,
collectionUid: collection.uid
}));
})
);
onClose();
})
.catch(() => toast.error("An error occured while adding the request"));
.catch(() => toast.error('An error occured while adding the request'));
} else {
dispatch(newHttpRequest({
dispatch(
newHttpRequest({
requestName: values.requestName,
requestType: values.requestType,
requestUrl: values.requestUrl,
requestMethod: values.requestMethod,
collectionUid: collection.uid,
itemUid: item ? item.uid : null
}))
})
)
.then(() => onClose())
.catch(() => toast.error("An error occured while adding the request"));
.catch(() => toast.error('An error occured while adding the request'));
}
}
});
@ -72,60 +75,69 @@ const NewRequest = ({collection, item, isEphermal, onClose}) => {
return (
<StyledWrapper>
<Modal
size="md"
title='New Request'
confirmText='Create'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="md" title="New Request" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div className="hidden">
<label htmlFor="requestName" className="block font-semibold">Type</label>
<label htmlFor="requestName" className="block font-semibold">
Type
</label>
<div className="flex items-center mt-2">
<input
id="http"
className="cursor-pointer"
type="radio" name="requestType"
type="radio"
name="requestType"
onChange={formik.handleChange}
value="http"
checked={formik.values.requestType === 'http-request'}
/>
<label htmlFor="http" className="ml-1 cursor-pointer select-none">Http</label>
<label htmlFor="http" className="ml-1 cursor-pointer select-none">
Http
</label>
<input
id="graphql"
className="ml-4 cursor-pointer"
type="radio" name="requestType"
type="radio"
name="requestType"
onChange={(event) => {
formik.setFieldValue('requestMethod', 'POST')
formik.setFieldValue('requestMethod', 'POST');
formik.handleChange(event);
}}
value="graphql"
checked={formik.values.requestType === 'graphql-request'}
/>
<label htmlFor="graphql" className="ml-1 cursor-pointer select-none">Graphql</label>
<label htmlFor="graphql" className="ml-1 cursor-pointer select-none">
Graphql
</label>
</div>
</div>
<div>
<label htmlFor="requestName" className="block font-semibold">Name</label>
<label htmlFor="requestName" className="block font-semibold">
Name
</label>
<input
id="collection-name" type="text" name="requestName"
id="collection-name"
type="text"
name="requestName"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.requestName || ''}
/>
{formik.touched.requestName && formik.errors.requestName ? (
<div className="text-red-500">{formik.errors.requestName}</div>
) : null}
{formik.touched.requestName && formik.errors.requestName ? <div className="text-red-500">{formik.errors.requestName}</div> : null}
</div>
<div className="mt-4">
<label htmlFor="request-url" className="block font-semibold">Url</label>
<label htmlFor="request-url" className="block font-semibold">
Url
</label>
<div className="flex items-center mt-2 ">
<div className="flex items-center h-full method-selector-container">
@ -133,17 +145,20 @@ const NewRequest = ({collection, item, isEphermal, onClose}) => {
</div>
<div className="flex items-center flex-grow input-container h-full">
<input
id="request-url" type="text" name="requestUrl"
id="request-url"
type="text"
name="requestUrl"
className="px-3 w-full "
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.requestUrl || ''}
/>
</div>
</div>
{formik.touched.requestUrl && formik.errors.requestUrl ? (
<div className="text-red-500">{formik.errors.requestUrl}</div>
) : null}
{formik.touched.requestUrl && formik.errors.requestUrl ? <div className="text-red-500">{formik.errors.requestUrl}</div> : null}
</div>
</form>
</Modal>

View File

@ -23,7 +23,7 @@ const TitleBar = () => {
const dispatch = useDispatch();
const menuDropdownTippyRef = useRef();
const onMenuDropdownCreate = (ref) => menuDropdownTippyRef.current = ref;
const onMenuDropdownCreate = (ref) => (menuDropdownTippyRef.current = ref);
const MenuIcon = forwardRef((props, ref) => {
return (
<div ref={ref} className="dropdown-icon cursor-pointer">
@ -35,17 +35,16 @@ const TitleBar = () => {
const handleTitleClick = () => dispatch(showHomePage());
const handleOpenLocalCollection = () => {
dispatch(openLocalCollection())
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
}
dispatch(openLocalCollection()).catch((err) => console.log(err) && toast.error('An error occured while opening the local collection'));
};
const handleAddCollectionToWorkspace = (collectionUid) => {
setAddCollectionToWSModalOpen(false);
dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid))
.then(() => {
toast.success("Collection added to workspace");
toast.success('Collection added to workspace');
})
.catch(() => toast.error("An error occured while adding collection to workspace"));
.catch(() => toast.error('An error occured while adding collection to workspace'));
};
const handleImportCollection = () => {
@ -59,19 +58,10 @@ const TitleBar = () => {
return (
<StyledWrapper className="px-2 py-2">
{createCollectionModalOpen ? (
<CreateCollection
isLocal={createCollectionModalOpen === 'local' ? true : false}
onClose={() => setCreateCollectionModalOpen(false)}
/>
) : null}
{createCollectionModalOpen ? <CreateCollection isLocal={createCollectionModalOpen === 'local' ? true : false} onClose={() => setCreateCollectionModalOpen(false)} /> : null}
{addCollectionToWSModalOpen ? (
<SelectCollection
title='Add Collection to Workspace'
onClose={() => setAddCollectionToWSModalOpen(false)}
onSelect={handleAddCollectionToWorkspace}
/>
<SelectCollection title="Add Collection to Workspace" onClose={() => setAddCollectionToWSModalOpen(false)} onSelect={handleAddCollectionToWorkspace} />
) : null}
<div className="flex items-center">
@ -86,47 +76,60 @@ const TitleBar = () => {
bruno
</div>
<div className="collection-dropdown flex flex-grow items-center justify-end">
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement='bottom-start'>
<div className="dropdown-item" onClick={(e) => {
<Dropdown onCreate={onMenuDropdownCreate} icon={<MenuIcon />} placement="bottom-start">
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setCreateCollectionModalOpen(true);
}}>
}}
>
Create Collection
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
handleImportCollection();
}}>
}}
>
Import Collection
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
menuDropdownTippyRef.current.hide();
setAddCollectionToWSModalOpen(true);
}}>
}}
>
Add Collection to Workspace
</div>
{isPlatformElectron ? (
<>
<div className="font-medium label-item font-medium local-collection-label">
<div className='flex items-center'>
<span className='mr-2'>
<div className="flex items-center">
<span className="mr-2">
<IconFolders size={18} strokeWidth={1.5} />
</span>
<span>
Local Collections
</span>
<span>Local Collections</span>
</div>
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
setCreateCollectionModalOpen('local');
menuDropdownTippyRef.current.hide();
}}>
}}
>
Create Local Collection
</div>
<div className="dropdown-item" onClick={(e) => {
<div
className="dropdown-item"
onClick={(e) => {
handleOpenLocalCollection();
menuDropdownTippyRef.current.hide();
}}>
}}
>
Open Local Collection
</div>
</>
@ -135,12 +138,11 @@ const TitleBar = () => {
Note: Local collections are only available on the desktop app.
</div>
)}
</Dropdown>
</div>
</div>
</StyledWrapper>
)
);
};
export default TitleBar;

View File

@ -36,20 +36,26 @@ const Sidebar = () => {
if (dragging) {
e.preventDefault();
setDragging(false);
dispatch(updateLeftSidebarWidth({
dispatch(
updateLeftSidebarWidth({
leftSidebarWidth: asideWidth
}));
dispatch(updateIsDragging({
})
);
dispatch(
updateIsDragging({
isDragging: false
}));
})
);
}
};
const handleDragbarMouseDown = (e) => {
e.preventDefault();
setDragging(true);
dispatch(updateIsDragging({
dispatch(
updateIsDragging({
isDragging: true
}));
})
);
};
useEffect(() => {
@ -87,7 +93,10 @@ const Sidebar = () => {
type="text"
name="search"
id="search"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
className="block w-full pl-7 py-1 sm:text-sm"
placeholder="search"
onChange={(e) => setSearchText(e.target.value.toLowerCase())}
@ -103,12 +112,17 @@ const Sidebar = () => {
{/* <IconLayoutGrid size={20} strokeWidth={1.5} className="mr-2"/> */}
{/* Need to ut github stars link here */}
</div>
<div className='pl-1'>
<iframe src="https://ghbtns.com/github-btn.html?user=usebruno&repo=bruno&type=star&count=true" frameBorder="0" scrolling="0" width="100" height="20" title="GitHub"></iframe>
</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">
v1.25.2
<div className="pl-1">
<iframe
src="https://ghbtns.com/github-btn.html?user=usebruno&repo=bruno&type=star&count=true"
frameBorder="0"
scrolling="0"
width="100"
height="20"
title="GitHub"
></iframe>
</div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.25.2</div>
</div>
</div>
</div>

View File

@ -9,9 +9,8 @@ const StyledWrapper = styled.div`
border: 0.25em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border .75s linear infinite;
animation: spinner-border 0.75s linear infinite;
}
`;
export default StyledWrapper;

View File

@ -6,13 +6,9 @@ const Spinner = ({size, color, children}) => {
return (
<StyledWrapper>
<div className="animate-spin"></div>
{children &&
<div>
{children}
</div>
}
{children && <div>{children}</div>}
</StyledWrapper>
)
);
};
export default Spinner;

View File

@ -20,11 +20,7 @@ const StopWatch = () => {
}
let seconds = milliseconds / 1000;
return (
<span>
{seconds.toFixed(1)}s
</span>
)
return <span>{seconds.toFixed(1)}s</span>;
};
export default StopWatch;

View File

@ -12,16 +12,16 @@ const Wrapper = styled.div`
}
.bruno-toast-card {
-webkit-animation-duration: .85s;
animation-duration: .85s;
-webkit-animation-delay: .1s;
animation-delay: .1s;
-webkit-animation-duration: 0.85s;
animation-duration: 0.85s;
-webkit-animation-delay: 0.1s;
animation-delay: 0.1s;
border-radius: var(--border-radius);
position: relative;
max-width: calc(100% - var(--spacing-base-unit));
margin: 3vh 10vw;
animation: fade-and-slide-in-from-top .50s forwards cubic-bezier(.19,1,.22,1);
animation: fade-and-slide-in-from-top 0.5s forwards cubic-bezier(0.19, 1, 0.22, 1);
}
.notification-toast-content {
@ -34,9 +34,9 @@ const Wrapper = styled.div`
.alert {
position: relative;
padding: .25rem .75rem;
padding: 0.25rem 0.75rem;
border: 1px solid transparent;
border-radius: .25rem;
border-radius: 0.25rem;
display: flex;
justify-content: space-between;
}

View File

@ -1,7 +1,7 @@
import React, { useEffect } from 'react';
import StyledWrapper from './StyledWrapper';
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTimes } from '@fortawesome/free-solid-svg-icons';
const ToastContent = ({ type, text, handleClose }) => (
<div className={`alert alert-${type}`} role="alert">
@ -12,12 +12,7 @@ const ToastContent = ({type, text, handleClose}) => (
</div>
);
const Toast = ({
text,
type,
duration,
handleClose
}) => {
const Toast = ({ text, type, duration, handleClose }) => {
let lifetime = duration ? duration : 3000;
useEffect(() => {
@ -27,8 +22,8 @@ const Toast = ({
}, [text]);
return (
<StyledWrapper className='bruno-toast'>
<div className='bruno-toast-card'>
<StyledWrapper className="bruno-toast">
<div className="bruno-toast-card">
<ToastContent type={type} text={text} handleClose={handleClose}></ToastContent>
</div>
</StyledWrapper>

View File

@ -1,15 +1,6 @@
import React, { useState } from 'react';
import toast from 'react-hot-toast';
import {
IconPlus,
IconUpload,
IconFiles,
IconFolders,
IconPlayerPlay,
IconBrandChrome,
IconSpeakerphone,
IconDeviceDesktop
} from '@tabler/icons';
import { IconPlus, IconUpload, IconFiles, IconFolders, IconPlayerPlay, IconBrandChrome, IconSpeakerphone, IconDeviceDesktop } from '@tabler/icons';
import { useSelector, useDispatch } from 'react-redux';
import { collectionImported } from 'providers/ReduxStore/slices/collections';
import { openLocalCollection } from 'providers/ReduxStore/slices/collections/actions';
@ -33,9 +24,9 @@ const Welcome = () => {
setAddCollectionToWSModalOpen(false);
dispatch(addCollectionToWorkspace(activeWorkspaceUid, collectionUid))
.then(() => {
toast.success("Collection added to workspace");
toast.success('Collection added to workspace');
})
.catch(() => toast.error("An error occured while adding collection to workspace"));
.catch(() => toast.error('An error occured while adding collection to workspace'));
};
const handleImportCollection = () => {
@ -53,33 +44,23 @@ const Welcome = () => {
dispatch(collectionImported({ collection: collection }));
dispatch(addCollectionToWorkspace(activeWorkspaceUid, collection.uid));
})
.then(() => toast.success("Sample Collection loaded successfully"))
.then(() => toast.success('Sample Collection loaded successfully'))
.catch((err) => {
toast.error("Load sample collection failed");
toast.error('Load sample collection failed');
console.log(err);
});
};
const handleOpenLocalCollection = () => {
dispatch(openLocalCollection())
.catch((err) => console.log(err) && toast.error("An error occured while opening the local collection"));
}
dispatch(openLocalCollection()).catch((err) => console.log(err) && toast.error('An error occured while opening the local collection'));
};
return (
<StyledWrapper className="pb-4 px-6 mt-6">
{createCollectionModalOpen ? (
<CreateCollection
isLocal={createCollectionModalOpen === 'local' ? true : false}
onClose={() => setCreateCollectionModalOpen(false)}
/>
) : null}
{createCollectionModalOpen ? <CreateCollection isLocal={createCollectionModalOpen === 'local' ? true : false} onClose={() => setCreateCollectionModalOpen(false)} /> : null}
{addCollectionToWSModalOpen ? (
<SelectCollection
title='Add Collection to Workspace'
onClose={() => setAddCollectionToWSModalOpen(false)}
onSelect={handleAddCollectionToWorkspace}
/>
<SelectCollection title="Add Collection to Workspace" onClose={() => setAddCollectionToWSModalOpen(false)} onSelect={handleAddCollectionToWorkspace} />
) : null}
<div className="">
@ -91,16 +72,24 @@ const Welcome = () => {
<div className="uppercase font-semibold create-request mt-10">Collections</div>
<div className="mt-4 flex items-center collection-options select-none">
<div className="flex items-center">
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setCreateCollectionModalOpen(true)}>Create Collection</span>
<IconPlus size={18} strokeWidth={2} />
<span className="label ml-2" onClick={() => setCreateCollectionModalOpen(true)}>
Create Collection
</span>
</div>
<div className="flex items-center ml-6">
<IconFiles size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setAddCollectionToWSModalOpen(true)}>Add Collection to Workspace</span>
<IconFiles size={18} strokeWidth={2} />
<span className="label ml-2" onClick={() => setAddCollectionToWSModalOpen(true)}>
Add Collection to Workspace
</span>
</div>
<div className="flex items-center ml-6" onClick={handleImportCollection}>
<IconUpload size={18} strokeWidth={2}/><span className="label ml-2">Import Collection</span>
<IconUpload size={18} strokeWidth={2} />
<span className="label ml-2">Import Collection</span>
</div>
<div className="flex items-center ml-6" onClick={handleImportSampleCollection}>
<IconPlayerPlay size={18} strokeWidth={2}/><span className="label ml-2">Load Sample Collection</span>
<IconPlayerPlay size={18} strokeWidth={2} />
<span className="label ml-2">Load Sample Collection</span>
</div>
</div>
@ -108,33 +97,40 @@ const Welcome = () => {
{isPlatformElectron ? (
<div className="mt-4 flex items-center collection-options select-none">
<div className="flex items-center">
<IconPlus size={18} strokeWidth={2}/><span className="label ml-2" onClick={() => setCreateCollectionModalOpen('local')}>Create Collection</span>
<IconPlus size={18} strokeWidth={2} />
<span className="label ml-2" onClick={() => setCreateCollectionModalOpen('local')}>
Create Collection
</span>
</div>
<div className="flex items-center ml-6">
<IconFolders size={18} strokeWidth={2}/><span className="label ml-2" onClick={handleOpenLocalCollection}>Open Collection</span>
<IconFolders size={18} strokeWidth={2} />
<span className="label ml-2" onClick={handleOpenLocalCollection}>
Open Collection
</span>
</div>
</div>
) : (
<div className="mt-4 flex items-center collection-options select-none text-gray-600 text-xs">
Local collections are only available on the desktop app.
</div>
<div className="mt-4 flex items-center collection-options select-none text-gray-600 text-xs">Local collections are only available on the desktop app.</div>
)}
<div className="uppercase font-semibold create-request mt-10 pt-6">Links</div>
<div className="mt-4 flex flex-col collection-options select-none">
<div>
<a href="https://www.usebruno.com/downloads" target="_blank" className="flex items-center">
<IconBrandChrome size={18} strokeWidth={2}/><span className="label ml-2">Chrome Extension</span>
<IconBrandChrome size={18} strokeWidth={2} />
<span className="label ml-2">Chrome Extension</span>
</a>
</div>
<div className="mt-2">
<a href="https://www.usebruno.com/downloads" target="_blank" className="flex items-center">
<IconDeviceDesktop size={18} strokeWidth={2}/><span className="label ml-2">Desktop App</span>
<IconDeviceDesktop size={18} strokeWidth={2} />
<span className="label ml-2">Desktop App</span>
</a>
</div>
<div className="mt-2">
<a href="https://github.com/usebruno/bruno/issues" target="_blank" className="flex items-center">
<IconSpeakerphone size={18} strokeWidth={2}/><span className="label ml-2">Report Issues</span>
<IconSpeakerphone size={18} strokeWidth={2} />
<span className="label ml-2">Report Issues</span>
</a>
</div>
{/* <div className="flex items-center mt-2">
@ -147,9 +143,8 @@ const Welcome = () => {
</a>
</div>
</div>
</StyledWrapper>
)
);
};
export default Welcome;

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef } from 'react';
import Portal from "components/Portal/index";
import Modal from "components/Modal/index";
import Portal from 'components/Portal/index';
import Modal from 'components/Modal/index';
import { useFormik } from 'formik';
import { addWorkspace } from 'providers/ReduxStore/slices/workspaces/actions';
import * as Yup from 'yup';
@ -13,21 +13,18 @@ const AddWorkspace = ({onClose}) => {
const formik = useFormik({
enableReinitialize: true,
initialValues: {
name: ""
name: ''
},
validationSchema: Yup.object({
name: Yup.string()
.min(1, 'must be atleast 1 characters')
.max(30, 'must be 30 characters or less')
.required('name is required')
name: Yup.string().min(1, 'must be atleast 1 characters').max(30, 'must be 30 characters or less').required('name is required')
}),
onSubmit: (values) => {
dispatch(addWorkspace(values.name))
.then(() => {
toast.success("Workspace created!");
toast.success('Workspace created!');
onClose();
})
.catch(() => toast.error("An error occured while creating the workspace"));
.catch(() => toast.error('An error occured while creating the workspace'));
}
});
@ -39,37 +36,35 @@ const AddWorkspace = ({onClose}) => {
const onSubmit = () => {
formik.handleSubmit();
}
};
return (
<Portal>
<Modal
size="sm"
title={"Add Workspace"}
confirmText='Add'
handleConfirm={onSubmit}
handleCancel={onClose}
>
<Modal size="sm" title={'Add Workspace'} confirmText="Add" handleConfirm={onSubmit} handleCancel={onClose}>
<form className="bruno-form" onSubmit={formik.handleSubmit}>
<div>
<label htmlFor="name" className="block font-semibold">Workspace Name</label>
<label htmlFor="name" className="block font-semibold">
Workspace Name
</label>
<input
id="workspace-name" type="text" name="name"
id="workspace-name"
type="text"
name="name"
ref={inputRef}
className="block textbox mt-2 w-full"
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
autoComplete="off"
autoCorrect="off"
autoCapitalize="off"
spellCheck="false"
onChange={formik.handleChange}
value={formik.values.name || ''}
/>
{formik.touched.name && formik.errors.name ? (
<div className="text-red-500">{formik.errors.name}</div>
) : null}
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
</div>
</form>
</Modal>
</Portal>
);
}
};
export default AddWorkspace;

Some files were not shown because too many files have changed in this diff Show More