mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-23 12:45:38 +00:00
feat: custom filename can be shown by clicking advanced options
This commit is contained in:
@@ -24,7 +24,7 @@ const StyledWrapper = styled.div`
|
||||
}
|
||||
|
||||
|
||||
.filename, .file-extension {
|
||||
.name-container, .file-extension {
|
||||
color: ${(props) => props.theme.colors.text.yellow};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,36 +4,17 @@ import path from 'utils/common/path';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const PathDisplay = ({
|
||||
collection,
|
||||
dirName = '',
|
||||
baseName = ''
|
||||
baseName = '',
|
||||
iconType = 'file'
|
||||
}) => {
|
||||
const extName = path.extname(baseName)
|
||||
const hasExtension = Boolean(extName);
|
||||
const pathSegments = dirName?.split(path.sep).filter(Boolean);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="path-display mt-2">
|
||||
<div className="path-layout flex font-mono">
|
||||
<div className="icon-column flex">
|
||||
{hasExtension ? <IconFile size={16} /> : <IconFolder size={16} />}
|
||||
{iconType === 'file' ? <IconFile size={16} /> : <IconFolder size={16} />}
|
||||
</div>
|
||||
{collection?.name && (
|
||||
<div className="path-segment collection-segment">
|
||||
{collection.name}
|
||||
<span className="separator">/</span>
|
||||
</div>
|
||||
)}
|
||||
{pathSegments?.map((segment, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<div className="path-segment">
|
||||
{segment}
|
||||
</div>
|
||||
<span className="separator">/</span>
|
||||
</React.Fragment>
|
||||
))}
|
||||
<span className="filename">
|
||||
<span className="name-container">
|
||||
{baseName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.advanced-options {
|
||||
.caret {
|
||||
color: ${(props) => props.theme.textLink};
|
||||
fill: ${(props) => props.theme.textLink};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import React, { useState, useRef, useEffect, forwardRef } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
@@ -6,11 +6,14 @@ import Modal from 'components/Modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isItemAFolder } from 'utils/tabs';
|
||||
import { cloneItem } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { IconArrowBackUp, IconEdit } from "@tabler/icons";
|
||||
import { IconArrowBackUp, IconEdit, IconCaretDown } from "@tabler/icons";
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import Help from 'components/Help';
|
||||
import PathDisplay from 'components/PathDisplay/index';
|
||||
import path from 'utils/common/path';
|
||||
import Portal from 'components/Portal';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -19,6 +22,11 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const itemName = item?.name;
|
||||
const itemType = item?.type;
|
||||
const [showFilesystemName, toggleShowFilesystemName] = useState(false);
|
||||
|
||||
const dropdownTippyRef = useRef();
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
@@ -58,112 +66,159 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
}
|
||||
}, [inputRef]);
|
||||
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
const AdvancedOptions = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex mr-2 text-link cursor-pointer items-center">
|
||||
<button
|
||||
className="btn-advanced"
|
||||
type="button"
|
||||
>
|
||||
Advanced
|
||||
</button>
|
||||
<IconCaretDown className="caret ml-1" size={14} strokeWidth={2}/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md"
|
||||
title={`Clone ${isFolder ? 'Folder' : 'Request'}`}
|
||||
confirmText="Clone"
|
||||
handleConfirm={onSubmit}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<form className="bruno-form" onSubmit={e => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-item-name"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder='Enter Item name'
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.name || ''}
|
||||
/>
|
||||
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Name
|
||||
{ isFolder ? (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno stores folders in the UI as folders on your filesystem.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can specify a custom folder name if you'd prefer a different name or need one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
) : (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
)}
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<Portal>
|
||||
<StyledWrapper>
|
||||
<Modal
|
||||
size="md"
|
||||
title={`Clone ${isFolder ? 'Folder' : 'Request'}`}
|
||||
handleCancel={onClose}
|
||||
hideFooter
|
||||
>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'Request'} Name
|
||||
</label>
|
||||
<input
|
||||
id="file-name"
|
||||
id="collection-item-name"
|
||||
type="text"
|
||||
name="filename"
|
||||
placeholder={isFolder ? 'Folder Name' : 'File Name'}
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
name="name"
|
||||
placeholder='Enter Item name'
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.filename || ''}
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.name || ''}
|
||||
/>
|
||||
{itemType !== 'folder' && <span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>}
|
||||
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
|
||||
{showFilesystemName && (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Name <small className='font-normal text-muted ml-1'>(on filesystem)</small>
|
||||
{ isFolder ? (
|
||||
<Help width="300">
|
||||
<p>
|
||||
You can choose to save the folder as a different name on your file system versus what is displayed in the app.
|
||||
</p>
|
||||
</Help>
|
||||
) : (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
)}
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<input
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="filename"
|
||||
placeholder={isFolder ? 'Folder Name' : 'File Name'}
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.filename || ''}
|
||||
/>
|
||||
{itemType !== 'folder' && <span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>}
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex justify-between items-center mt-8 bruno-modal-footer">
|
||||
<div className='flex advanced-options'>
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<AdvancedOptions />} placement="bottom-start">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
key="show-filesystem-name"
|
||||
onClick={(e) => {
|
||||
dropdownTippyRef.current.hide();
|
||||
toggleShowFilesystemName(!showFilesystemName);
|
||||
}}
|
||||
>
|
||||
{showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className='flex justify-end'>
|
||||
<span className='mr-2'>
|
||||
<button type="button" onClick={onClose} className="btn btn-md btn-close">
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
type="submit"
|
||||
className="submit btn btn-md btn-secondary"
|
||||
>
|
||||
Clone
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ const CollectionItemInfo = ({ collection, item, onClose }) => {
|
||||
<td className="py-2 px-2 text-nowrap truncate max-w-[500px]" title={name}>{name}</td>
|
||||
</tr>
|
||||
<tr className="">
|
||||
<td className="py-2 px-2 text-right opacity-50">{type=='folder' ? 'Directory Name' : 'File Name'} :</td>
|
||||
<td className="py-2 px-2 text-right opacity-50">{type=='folder' ? 'Folder Name' : 'File Name'} :</td>
|
||||
<td className="py-2 px-2 break-all text-nowrap truncate max-w-[500px]" title={filename}>{filename}</td>
|
||||
</tr>
|
||||
<tr className="">
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.advanced-options {
|
||||
.caret {
|
||||
color: ${(props) => props.theme.textLink};
|
||||
fill: ${(props) => props.theme.textLink};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import React, { useRef, useEffect, useState, forwardRef } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import Modal from 'components/Modal';
|
||||
@@ -6,12 +6,15 @@ import { useDispatch } from 'react-redux';
|
||||
import { isItemAFolder } from 'utils/tabs';
|
||||
import { renameItem, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import path from 'utils/common/path';
|
||||
import { IconArrowBackUp, IconEdit } from '@tabler/icons';
|
||||
import { IconArrowBackUp, IconEdit, IconCaretDown } from '@tabler/icons';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import toast from 'react-hot-toast';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
import Help from 'components/Help';
|
||||
import PathDisplay from 'components/PathDisplay';
|
||||
import Portal from 'components/Portal';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
@@ -21,6 +24,11 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
const itemName = item?.name;
|
||||
const itemType = item?.type;
|
||||
const itemFilename = item?.filename ? path.parse(item?.filename).name : '';
|
||||
const [showFilesystemName, toggleShowFilesystemName] = useState(false);
|
||||
|
||||
const dropdownTippyRef = useRef();
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
@@ -78,112 +86,157 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
}
|
||||
}, [inputRef]);
|
||||
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
const AdvancedOptions = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex mr-2 text-link cursor-pointer items-center">
|
||||
<button
|
||||
className="btn-advanced"
|
||||
type="button"
|
||||
>
|
||||
Advanced
|
||||
</button>
|
||||
<IconCaretDown className="caret ml-1" size={14} strokeWidth={2}/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md"
|
||||
title={`Rename ${isFolder ? 'Folder' : 'Request'}`}
|
||||
confirmText="Rename"
|
||||
handleConfirm={onSubmit}
|
||||
handleCancel={onClose}
|
||||
>
|
||||
<form className="bruno-form" onSubmit={e => {e.preventDefault()}}>
|
||||
<div className='flex flex-col mt-2'>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
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"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.name || ''}
|
||||
/>
|
||||
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Name
|
||||
{ isFolder ? (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno stores folders in the UI as folders on your filesystem.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can specify a custom folder name if you'd prefer a different name or need one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
) : (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
)}
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<Portal>
|
||||
<StyledWrapper>
|
||||
<Modal
|
||||
size="md"
|
||||
title={`Rename ${isFolder ? 'Folder' : 'Request'}`}
|
||||
handleCancel={onClose}
|
||||
hideFooter
|
||||
>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div className='flex flex-col mt-2'>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'Request'} Name
|
||||
</label>
|
||||
<input
|
||||
id="file-name"
|
||||
id="collection-item-name"
|
||||
type="text"
|
||||
name="filename"
|
||||
placeholder={isFolder ? 'Folder Name' : 'File Name'}
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
name="name"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.filename || ''}
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.name || ''}
|
||||
/>
|
||||
{itemType !== 'folder' && <span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>}
|
||||
{formik.touched.name && formik.errors.name ? <div className="text-red-500">{formik.errors.name}</div> : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
|
||||
{showFilesystemName && (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Name <small className='font-normal text-muted ml-1'>(on filesystem)</small>
|
||||
{ isFolder ? (
|
||||
<Help width="300">
|
||||
<p>
|
||||
You can choose to save the folder as a different name on your file system versus what is displayed in the app.
|
||||
</p>
|
||||
</Help>
|
||||
) : (
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
)}
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<input
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="filename"
|
||||
placeholder={isFolder ? 'Folder Name' : 'File Name'}
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.filename || ''}
|
||||
/>
|
||||
{itemType !== 'folder' && <span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>}
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between items-center mt-8 bruno-modal-footer">
|
||||
<div className='flex advanced-options'>
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<AdvancedOptions />} placement="bottom-start">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
key="show-filesystem-name"
|
||||
onClick={(e) => {
|
||||
dropdownTippyRef.current.hide();
|
||||
toggleShowFilesystemName(!showFilesystemName);
|
||||
}}
|
||||
>
|
||||
{showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className='flex justify-end'>
|
||||
<span className='mr-2'>
|
||||
<button type="button" onClick={onClose} className="btn btn-md btn-close">
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
type="submit"
|
||||
className="submit btn btn-md btn-secondary"
|
||||
>
|
||||
Rename
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
.advanced-options {
|
||||
.caret {
|
||||
color: ${(props) => props.theme.textLink};
|
||||
fill: ${(props) => props.theme.textLink};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default StyledWrapper;
|
||||
@@ -1,19 +1,27 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import React, { useRef, useEffect, useState, forwardRef } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import toast from 'react-hot-toast';
|
||||
import * as Yup from 'yup';
|
||||
import Portal from 'components/Portal';
|
||||
import Modal from 'components/Modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { newFolder } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { IconArrowBackUp, IconEdit} from '@tabler/icons';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import PathDisplay from 'components/PathDisplay/index';
|
||||
import path from "utils/common/path";
|
||||
import Help from 'components/Help';
|
||||
import Dropdown from "components/Dropdown";
|
||||
import { IconCaretDown } from "@tabler/icons";
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const NewFolder = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const inputRef = useRef();
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const [showFilesystemName, toggleShowFilesystemName] = useState(false);
|
||||
|
||||
const dropdownTippyRef = useRef();
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
@@ -59,96 +67,139 @@ const NewFolder = ({ collection, item, onClose }) => {
|
||||
}
|
||||
}, [inputRef]);
|
||||
|
||||
const onSubmit = () => formik.handleSubmit();
|
||||
const AdvancedOptions = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex mr-2 text-link cursor-pointer items-center">
|
||||
<button
|
||||
className="btn-advanced"
|
||||
type="button"
|
||||
>
|
||||
Advanced
|
||||
</button>
|
||||
<IconCaretDown className="caret ml-1" size={14} strokeWidth={2}/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal size="md" title="New Folder" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="folderName" className="block font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-name"
|
||||
type="text"
|
||||
name="folderName"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('folderName', e.target.value);
|
||||
!isEditing && formik.setFieldValue('directoryName', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.folderName || ''}
|
||||
/>
|
||||
{formik.touched.folderName && formik.errors.folderName ? (
|
||||
<div className="text-red-500">{formik.errors.folderName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="directoryName" className="flex items-center font-semibold">
|
||||
<Portal>
|
||||
<StyledWrapper>
|
||||
<Modal size="md" title="New Folder" hideFooter={true} handleCancel={onClose}>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<label htmlFor="folderName" className="block font-semibold">
|
||||
Folder Name
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno stores folders in the UI as folders on your filesystem.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can specify a custom folder name if you'd prefer a different name or need one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
): (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
<input
|
||||
id="collection-name"
|
||||
type="text"
|
||||
name="folderName"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('folderName', e.target.value);
|
||||
!isEditing && formik.setFieldValue('directoryName', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.folderName || ''}
|
||||
/>
|
||||
{formik.touched.folderName && formik.errors.folderName ? (
|
||||
<div className="text-red-500">{formik.errors.folderName}</div>
|
||||
) : null}
|
||||
|
||||
{showFilesystemName && (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="directoryName" className="flex items-center font-semibold">
|
||||
Folder Name <small className='font-normal text-muted ml-1'>(on filesystem)</small>
|
||||
<Help width="300">
|
||||
<p>
|
||||
You can choose to save the folder as a different name on your file system versus what is displayed in the app.
|
||||
</p>
|
||||
</Help>
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
): (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<input
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="directoryName"
|
||||
placeholder="Folder Name"
|
||||
className={`block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.directoryName || ''}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
iconType="folder"
|
||||
baseName={formik.values.directoryName}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.directoryName && formik.errors.directoryName ? (
|
||||
<div className="text-red-500">{formik.errors.directoryName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<input
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="directoryName"
|
||||
placeholder="Directory Name"
|
||||
className={`block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.directoryName || ''}
|
||||
/>
|
||||
<div className="flex justify-between items-center mt-8 bruno-modal-footer">
|
||||
<div className='flex advanced-options'>
|
||||
<Dropdown onCreate={onDropdownCreate} icon={<AdvancedOptions />} placement="bottom-start">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
key="show-filesystem-name"
|
||||
onClick={(e) => {
|
||||
dropdownTippyRef.current.hide();
|
||||
toggleShowFilesystemName(!showFilesystemName);
|
||||
}}
|
||||
>
|
||||
{showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<div className='flex justify-end'>
|
||||
<span className='mr-2'>
|
||||
<button type="button" onClick={onClose} className="btn btn-md btn-close">
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
type="submit"
|
||||
className="submit btn btn-md btn-secondary"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, item?.pathname || collection?.pathname)}
|
||||
baseName={formik.values.directoryName}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.directoryName && formik.errors.directoryName ? (
|
||||
<div className="text-red-500">{formik.errors.directoryName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IconArrowBackUp, IconCaretDown, IconEdit } from '@tabler/icons';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import Dropdown from 'components/Dropdown';
|
||||
import PathDisplay from 'components/PathDisplay';
|
||||
import Portal from 'components/Portal';
|
||||
import Help from 'components/Help';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
@@ -26,10 +27,14 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
brunoConfig: { presets: collectionPresets = {} }
|
||||
} = collection;
|
||||
const [curlRequestTypeDetected, setCurlRequestTypeDetected] = useState(null);
|
||||
const [showFilesystemName, toggleShowFilesystemName] = useState(false);
|
||||
|
||||
const dropdownTippyRef = useRef();
|
||||
const onDropdownCreate = (ref) => (dropdownTippyRef.current = ref);
|
||||
|
||||
const advancedDropdownTippyRef = useRef();
|
||||
const onAdvancedDropdownCreate = (ref) => (advancedDropdownTippyRef.current = ref);
|
||||
|
||||
const Icon = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex items-center justify-end auth-type-label select-none">
|
||||
@@ -231,230 +236,279 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Modal size="md" title="New Request" confirmText="Create" handleConfirm={onSubmit} handleCancel={onClose}>
|
||||
<form
|
||||
className="bruno-form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
formik.handleSubmit();
|
||||
}
|
||||
}}
|
||||
const AdvancedOptions = forwardRef((props, ref) => {
|
||||
return (
|
||||
<div ref={ref} className="flex mr-2 text-link cursor-pointer items-center">
|
||||
<button
|
||||
className="btn-advanced"
|
||||
type="button"
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="requestName" className="block font-semibold">
|
||||
Type
|
||||
</label>
|
||||
Advanced
|
||||
</button>
|
||||
<IconCaretDown className="caret ml-1" size={14} strokeWidth={2}/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
<div className="flex items-center mt-2">
|
||||
<input
|
||||
id="http-request"
|
||||
className="cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="http-request"
|
||||
checked={formik.values.requestType === 'http-request'}
|
||||
/>
|
||||
<label htmlFor="http-request" className="ml-1 cursor-pointer select-none">
|
||||
HTTP
|
||||
return (
|
||||
<Portal>
|
||||
<StyledWrapper>
|
||||
<Modal size="md" title="New Request" hideFooter handleCancel={onClose}>
|
||||
<form
|
||||
className="bruno-form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
formik.handleSubmit();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<label htmlFor="requestName" className="block font-semibold">
|
||||
Type
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="graphql-request"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={(event) => {
|
||||
formik.setFieldValue('requestMethod', 'POST');
|
||||
formik.handleChange(event);
|
||||
}}
|
||||
value="graphql-request"
|
||||
checked={formik.values.requestType === 'graphql-request'}
|
||||
/>
|
||||
<label htmlFor="graphql-request" className="ml-1 cursor-pointer select-none">
|
||||
GraphQL
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="from-curl"
|
||||
className="cursor-pointer ml-auto"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="from-curl"
|
||||
checked={formik.values.requestType === 'from-curl'}
|
||||
/>
|
||||
|
||||
<label htmlFor="from-curl" className="ml-1 cursor-pointer select-none">
|
||||
From cURL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="requestName" className="block font-semibold">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="request-name"
|
||||
type="text"
|
||||
name="requestName"
|
||||
placeholder="Request Name"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('requestName', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.requestName || ''}
|
||||
/>
|
||||
{formik.touched.requestName && formik.errors.requestName ? (
|
||||
<div className="text-red-500">{formik.errors.requestName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
File Name
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<div className="flex items-center mt-2">
|
||||
<input
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="filename"
|
||||
placeholder="File Name"
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
id="http-request"
|
||||
className="cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.filename || ''}
|
||||
value="http-request"
|
||||
checked={formik.values.requestType === 'http-request'}
|
||||
/>
|
||||
<span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, item?.pathname || collection?.pathname)}
|
||||
baseName={formik.values.filename? `${formik.values.filename}.bru` : ''}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{formik.values.requestType !== 'from-curl' ? (
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
URL
|
||||
<label htmlFor="http-request" className="ml-1 cursor-pointer select-none">
|
||||
HTTP
|
||||
</label>
|
||||
<div className="flex items-center mt-2 ">
|
||||
<div className="flex items-center h-full method-selector-container">
|
||||
<HttpMethodSelector
|
||||
method={formik.values.requestMethod}
|
||||
onMethodSelect={(val) => formik.setFieldValue('requestMethod', val)}
|
||||
|
||||
<input
|
||||
id="graphql-request"
|
||||
className="ml-4 cursor-pointer"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={(event) => {
|
||||
formik.setFieldValue('requestMethod', 'POST');
|
||||
formik.handleChange(event);
|
||||
}}
|
||||
value="graphql-request"
|
||||
checked={formik.values.requestType === 'graphql-request'}
|
||||
/>
|
||||
<label htmlFor="graphql-request" className="ml-1 cursor-pointer select-none">
|
||||
GraphQL
|
||||
</label>
|
||||
|
||||
<input
|
||||
id="from-curl"
|
||||
className="cursor-pointer ml-auto"
|
||||
type="radio"
|
||||
name="requestType"
|
||||
onChange={formik.handleChange}
|
||||
value="from-curl"
|
||||
checked={formik.values.requestType === 'from-curl'}
|
||||
/>
|
||||
|
||||
<label htmlFor="from-curl" className="ml-1 cursor-pointer select-none">
|
||||
From cURL
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<label htmlFor="requestName" className="block font-semibold">
|
||||
Request Name
|
||||
</label>
|
||||
<input
|
||||
id="request-name"
|
||||
type="text"
|
||||
name="requestName"
|
||||
placeholder="Request Name"
|
||||
ref={inputRef}
|
||||
className="block textbox mt-2 w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('requestName', e.target.value);
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.requestName || ''}
|
||||
/>
|
||||
{formik.touched.requestName && formik.errors.requestName ? (
|
||||
<div className="text-red-500">{formik.errors.requestName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{showFilesystemName && (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="flex items-center font-semibold">
|
||||
File Name <small className='font-normal text-muted ml-1'>(on filesystem)</small>
|
||||
<Help width="300">
|
||||
<p>
|
||||
Bruno saves each request as a file in your collection's folder.
|
||||
</p>
|
||||
<p className="mt-2">
|
||||
You can choose a file name different from your request's name or one compatible with filesystem rules.
|
||||
</p>
|
||||
</Help>
|
||||
</label>
|
||||
{isEditing ? (
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
) : (
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{isEditing ? (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<input
|
||||
id="request-url"
|
||||
id="file-name"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
placeholder="Request URL"
|
||||
className="px-3 w-full "
|
||||
name="filename"
|
||||
placeholder="File Name"
|
||||
className={`!pr-10 block textbox mt-2 w-full`}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.requestUrl || ''}
|
||||
onPaste={handlePaste}
|
||||
value={formik.values.filename || ''}
|
||||
/>
|
||||
<span className='absolute right-2 top-4 flex justify-center items-center file-extension'>.bru</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
dirName={path.relative(collection?.pathname, item?.pathname || collection?.pathname)}
|
||||
baseName={formik.values.filename? `${formik.values.filename}.bru` : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{formik.touched.requestUrl && formik.errors.requestUrl ? (
|
||||
<div className="text-red-500">{formik.errors.requestUrl}</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<div className="flex justify-between">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
cURL Command
|
||||
</label>
|
||||
<Dropdown className="dropdown" onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
curlRequestTypeChange('http-request');
|
||||
}}
|
||||
>
|
||||
HTTP
|
||||
)}
|
||||
{formik.values.requestType !== 'from-curl' ? (
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<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">
|
||||
<HttpMethodSelector
|
||||
method={formik.values.requestMethod}
|
||||
onMethodSelect={(val) => formik.setFieldValue('requestMethod', val)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center flex-grow input-container h-full">
|
||||
<input
|
||||
id="request-url"
|
||||
type="text"
|
||||
name="requestUrl"
|
||||
placeholder="Request URL"
|
||||
className="px-3 w-full "
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.requestUrl || ''}
|
||||
onPaste={handlePaste}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
{formik.touched.requestUrl && formik.errors.requestUrl ? (
|
||||
<div className="text-red-500">{formik.errors.requestUrl}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className="mt-4">
|
||||
<div className="flex justify-between">
|
||||
<label htmlFor="request-url" className="block font-semibold">
|
||||
cURL Command
|
||||
</label>
|
||||
<Dropdown className="dropdown" onCreate={onDropdownCreate} icon={<Icon />} placement="bottom-end">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
curlRequestTypeChange('http-request');
|
||||
}}
|
||||
>
|
||||
HTTP
|
||||
</div>
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
curlRequestTypeChange('graphql-request');
|
||||
}}
|
||||
>
|
||||
GraphQL
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<textarea
|
||||
name="curlCommand"
|
||||
placeholder="Enter cURL request here.."
|
||||
className="block textbox w-full mt-4 curl-command"
|
||||
value={formik.values.curlCommand}
|
||||
onChange={handleCurlCommandChange}
|
||||
></textarea>
|
||||
{formik.touched.curlCommand && formik.errors.curlCommand ? (
|
||||
<div className="text-red-500">{formik.errors.curlCommand}</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between items-center mt-8 bruno-modal-footer">
|
||||
<div className='flex advanced-options'>
|
||||
<Dropdown onCreate={onAdvancedDropdownCreate} icon={<AdvancedOptions />} placement="bottom-start">
|
||||
<div
|
||||
className="dropdown-item"
|
||||
onClick={() => {
|
||||
dropdownTippyRef.current.hide();
|
||||
curlRequestTypeChange('graphql-request');
|
||||
key="show-filesystem-name"
|
||||
onClick={(e) => {
|
||||
advancedDropdownTippyRef.current.hide();
|
||||
toggleShowFilesystemName(!showFilesystemName);
|
||||
}}
|
||||
>
|
||||
GraphQL
|
||||
{showFilesystemName ? 'Hide Filesystem Name' : 'Show Filesystem Name'}
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<textarea
|
||||
name="curlCommand"
|
||||
placeholder="Enter cURL request here.."
|
||||
className="block textbox w-full mt-4 curl-command"
|
||||
value={formik.values.curlCommand}
|
||||
onChange={handleCurlCommandChange}
|
||||
></textarea>
|
||||
{formik.touched.curlCommand && formik.errors.curlCommand ? (
|
||||
<div className="text-red-500">{formik.errors.curlCommand}</div>
|
||||
) : null}
|
||||
<div className='flex justify-end'>
|
||||
<span className='mr-2'>
|
||||
<button type="button" onClick={onClose} className="btn btn-md btn-close">
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
<span>
|
||||
<button
|
||||
type="submit"
|
||||
className="submit btn btn-md btn-secondary"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
</form>
|
||||
</Modal>
|
||||
</StyledWrapper>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user