mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-24 21:25:45 +00:00
filename support ui updates, regex update for generating validation error for the last char, getEncoding function headers props optional chaining validation (#4243)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
width: 100%;
|
||||
.path-display {
|
||||
background: ${(props) => props.theme.requestTabPanel.url.bg};
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -1,63 +1,34 @@
|
||||
import React from 'react';
|
||||
import { IconEdit, IconFolder, IconFile } from '@tabler/icons';
|
||||
import { IconFolder, IconFile } from '@tabler/icons';
|
||||
import path from 'utils/common/path';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const PathDisplay = ({
|
||||
collection,
|
||||
item,
|
||||
filename,
|
||||
extension = '.bru',
|
||||
showExtension = true,
|
||||
toggleEditingFilename,
|
||||
showDirectory = false
|
||||
dirName = '',
|
||||
baseName = ''
|
||||
}) => {
|
||||
const relativePath = item?.pathname && path.relative(collection?.pathname, showDirectory ? path.dirname(item?.pathname) : item?.pathname);
|
||||
const pathSegments = relativePath?.split(path.sep).filter(Boolean);
|
||||
const extName = path.extname(baseName)
|
||||
const hasExtension = Boolean(extName);
|
||||
const pathSegments = dirName?.split(path.sep).filter(Boolean);
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<label className="block font-medium">Location</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className="path-display">
|
||||
<div className="path-layout flex">
|
||||
<div className="icon-column flex">
|
||||
{showExtension ? <IconFile size={16} /> : <IconFolder size={16} />}
|
||||
</div>
|
||||
<div className="path-container flex font-mono items-center">
|
||||
<div className="path-segment collection-segment">
|
||||
{collection?.name}
|
||||
</div>
|
||||
|
||||
{pathSegments?.length > 0 && pathSegments?.map((segment, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<span className="separator">/</span>
|
||||
<div className="path-segment">
|
||||
{segment}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
{collection && (
|
||||
<span className="separator">/</span>
|
||||
)}
|
||||
|
||||
<span className="filename">
|
||||
{filename}
|
||||
{showExtension && filename?.length ? (
|
||||
<span className="file-extension">{extension}</span>
|
||||
) : null}
|
||||
</span>
|
||||
</div>
|
||||
<div className="path-display mt-2">
|
||||
<div className="path-layout flex">
|
||||
<div className="icon-column flex">
|
||||
{hasExtension ? <IconFile size={16} /> : <IconFolder size={16} />}
|
||||
</div>
|
||||
{pathSegments?.map((segment, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<div className="path-segment">
|
||||
{segment}
|
||||
</div>
|
||||
<span className="separator">/</span>
|
||||
</React.Fragment>
|
||||
))}
|
||||
<span className="filename">
|
||||
{baseName}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
|
||||
@@ -9,18 +9,19 @@ import Modal from 'components/Modal';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import PathDisplay from 'components/PathDisplay/index';
|
||||
import { useState } from 'react';
|
||||
import { IconArrowBackUp } from "@tabler/icons";
|
||||
import { IconArrowBackUp, IconEdit } from "@tabler/icons";
|
||||
|
||||
const CloneCollection = ({ onClose, collection }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const { name } = collection;
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
collectionName: '',
|
||||
collectionFolderName: '',
|
||||
collectionName: `${name} copy`,
|
||||
collectionFolderName: `${sanitizeName(name)} copy`,
|
||||
collectionLocation: ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
@@ -31,7 +32,7 @@ const CloneCollection = ({ onClose, collection }) => {
|
||||
collectionFolderName: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(255, 'must be 255 characters or less')
|
||||
.test('is-valid-dir-name', function(value) {
|
||||
.test('is-valid-collection-name', function(value) {
|
||||
const isValid = validateName(value);
|
||||
return isValid ? true : this.createError({ message: validateNameError(value) });
|
||||
})
|
||||
@@ -92,7 +93,7 @@ const CloneCollection = ({ onClose, collection }) => {
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
!isEditingFilename && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value));
|
||||
!isEditing && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value));
|
||||
}}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -128,41 +129,51 @@ const CloneCollection = ({ onClose, collection }) => {
|
||||
Browse
|
||||
</span>
|
||||
</div>
|
||||
{isEditingFilename ?
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
Directory Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
id="collection-folder-name"
|
||||
type="text"
|
||||
name="collectionFolderName"
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={formik.handleChange}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionFolderName || ''}
|
||||
{isEditing ?
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
Folder Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
<input
|
||||
id="collection-folder-name"
|
||||
type="text"
|
||||
name="collectionFolderName"
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={formik.handleChange}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionFolderName || ''}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<PathDisplay
|
||||
filename={formik.values.collectionFolderName}
|
||||
showExtension={false}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="baseName" className="block font-semibold">
|
||||
Folder Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
baseName={formik.values.collectionFolderName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{formik.touched.collectionFolderName && formik.errors.collectionFolderName ? (
|
||||
<div className="text-red-500">{formik.errors.collectionFolderName}</div>
|
||||
|
||||
@@ -6,24 +6,23 @@ 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 } from '@tabler/icons';
|
||||
import * as path from 'path';
|
||||
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';
|
||||
|
||||
const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const isFolder = isItemAFolder(item);
|
||||
const inputRef = useRef();
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const itemName = item?.name;
|
||||
const itemType = item?.type;
|
||||
const itemFilename = item?.filename ? path.parse(item?.filename).name : '';
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
name: itemName,
|
||||
filename: sanitizeName(itemFilename)
|
||||
name: `${itemName} copy`,
|
||||
filename: `${sanitizeName(itemName)} copy`
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
name: Yup.string()
|
||||
@@ -34,7 +33,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(255, 'must be 255 characters or less')
|
||||
.required('name is required')
|
||||
.test('is-valid-filename', function(value) {
|
||||
.test('is-valid-name', function(value) {
|
||||
const isValid = validateName(value);
|
||||
return isValid ? true : this.createError({ message: validateNameError(value) });
|
||||
})
|
||||
@@ -71,7 +70,7 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
<form className="bruno-form" onSubmit={e => e.preventDefault()}>
|
||||
<div>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'Request'} Name
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-item-name"
|
||||
@@ -86,23 +85,23 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditingFilename && formik.setFieldValue('filename', sanitizeName(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>
|
||||
{isEditingFilename ? (
|
||||
{isEditing ? (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
{isFolder ? 'Directory' : 'File'} Name
|
||||
{isFolder ? 'Folder' : 'File'} Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
@@ -123,15 +122,25 @@ const CloneCollectionItem = ({ collection, item, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
item={item}
|
||||
filename={formik.values.filename}
|
||||
showExtension={itemType !== 'folder'}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
showDirectory={true}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="baseName" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import Modal from 'components/Modal';
|
||||
import * as path from 'path';
|
||||
import path from 'utils/common/path';
|
||||
|
||||
const CollectionItemInfo = ({ collection, item, onClose }) => {
|
||||
const { pathname: collectionPathname } = collection;
|
||||
|
||||
@@ -6,7 +6,7 @@ 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 } from '@tabler/icons';
|
||||
import { IconArrowBackUp, IconEdit } from '@tabler/icons';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import toast from 'react-hot-toast';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
@@ -16,7 +16,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const isFolder = isItemAFolder(item);
|
||||
const inputRef = useRef();
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
const itemName = item?.name;
|
||||
const itemType = item?.type;
|
||||
const itemFilename = item?.filename ? path.parse(item?.filename).name : '';
|
||||
@@ -35,7 +35,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(255, 'must be 255 characters or less')
|
||||
.required('name is required')
|
||||
.test('is-valid-filename', function(value) {
|
||||
.test('is-valid-name', function(value) {
|
||||
const isValid = validateName(value);
|
||||
return isValid ? true : this.createError({ message: validateNameError(value) });
|
||||
})
|
||||
@@ -90,7 +90,7 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
<form className="bruno-form" onSubmit={e => {e.preventDefault()}}>
|
||||
<div className='flex flex-col mt-2'>
|
||||
<label htmlFor="name" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'Request'} Name
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-item-name"
|
||||
@@ -104,24 +104,24 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('name', e.target.value);
|
||||
!isEditingFilename && formik.setFieldValue('filename', sanitizeName(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>
|
||||
|
||||
{isEditingFilename ? (
|
||||
{isEditing ? (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
{isFolder ? 'Directory' : 'File'} Name
|
||||
{isFolder ? 'Folder' : 'File'} Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
@@ -142,15 +142,25 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
item={item}
|
||||
filename={formik.values.filename}
|
||||
showExtension={itemType !== 'folder'}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
showDirectory={true}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="baseName" className="block font-semibold">
|
||||
{isFolder ? 'Folder' : 'File'} Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
dirName={path.relative(collection?.pathname, path.dirname(item?.pathname))}
|
||||
baseName={formik.values.filename}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
|
||||
@@ -24,7 +24,6 @@ import toast from 'react-hot-toast';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import NetworkError from 'components/ResponsePane/NetworkError/index';
|
||||
import CollectionItemInfo from './CollectionItemInfo/index';
|
||||
import { findItemInCollection } from 'utils/collections';
|
||||
import CollectionItemIcon from './CollectionItemIcon';
|
||||
import { scrollToTheActiveTab } from 'utils/tabs';
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@ import Modal from 'components/Modal';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import PathDisplay from 'components/PathDisplay/index';
|
||||
import { useState } from 'react';
|
||||
import { IconArrowBackUp } from '@tabler/icons';
|
||||
import { IconArrowBackUp, IconEdit } from '@tabler/icons';
|
||||
import Help from 'components/Help';
|
||||
|
||||
const CreateCollection = ({ onClose }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
@@ -32,7 +32,7 @@ const CreateCollection = ({ onClose }) => {
|
||||
collectionFolderName: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(255, 'must be 255 characters or less')
|
||||
.test('is-valid-dir-name', function(value) {
|
||||
.test('is-valid-collection-name', function(value) {
|
||||
const isValid = validateName(value);
|
||||
return isValid ? true : this.createError({ message: validateNameError(value) });
|
||||
})
|
||||
@@ -86,7 +86,7 @@ const CreateCollection = ({ onClose }) => {
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={(e) => {
|
||||
formik.handleChange(e);
|
||||
!isEditingFilename && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value));
|
||||
!isEditing && formik.setFieldValue('collectionFolderName', sanitizeName(e.target.value));
|
||||
}}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
@@ -130,41 +130,51 @@ const CreateCollection = ({ onClose }) => {
|
||||
Browse
|
||||
</span>
|
||||
</div>
|
||||
{isEditingFilename ?
|
||||
<>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
Directory Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
id="collection-folder-name"
|
||||
type="text"
|
||||
name="collectionFolderName"
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={formik.handleChange}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionFolderName || ''}
|
||||
{isEditing ?
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
Folder Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
<PathDisplay
|
||||
filename={formik.values.collectionFolderName}
|
||||
showExtension={false}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
/>
|
||||
<input
|
||||
id="collection-folder-name"
|
||||
type="text"
|
||||
name="collectionFolderName"
|
||||
className="block textbox mt-2 w-full"
|
||||
onChange={formik.handleChange}
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.collectionFolderName || ''}
|
||||
/>
|
||||
</div>
|
||||
:
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="baseName" className="block font-semibold">
|
||||
Folder Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
baseName={formik.values.collectionFolderName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
{formik.touched.collectionFolderName && formik.errors.collectionFolderName ? (
|
||||
<div className="text-red-500">{formik.errors.collectionFolderName}</div>
|
||||
|
||||
@@ -5,14 +5,15 @@ import * as Yup from 'yup';
|
||||
import Modal from 'components/Modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { newFolder } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { IconArrowBackUp } from '@tabler/icons';
|
||||
import { IconArrowBackUp, IconEdit} from '@tabler/icons';
|
||||
import { sanitizeName, validateName, validateNameError } from 'utils/common/regex';
|
||||
import PathDisplay from 'components/PathDisplay';
|
||||
import PathDisplay from 'components/PathDisplay/index';
|
||||
import path from "utils/common/path";
|
||||
|
||||
const NewFolder = ({ collection, item, onClose }) => {
|
||||
const dispatch = useDispatch();
|
||||
const inputRef = useRef();
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
@@ -65,7 +66,7 @@ const NewFolder = ({ collection, item, onClose }) => {
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="folderName" className="block font-semibold">
|
||||
Folder Name
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="collection-name"
|
||||
@@ -79,7 +80,7 @@ const NewFolder = ({ collection, item, onClose }) => {
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('folderName', e.target.value);
|
||||
!isEditingFilename && formik.setFieldValue('directoryName', sanitizeName(e.target.value));
|
||||
!isEditing && formik.setFieldValue('directoryName', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.folderName || ''}
|
||||
/>
|
||||
@@ -88,17 +89,17 @@ const NewFolder = ({ collection, item, onClose }) => {
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{isEditingFilename ? (
|
||||
{isEditing ? (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="directoryName" className="block font-semibold">
|
||||
Directory Name
|
||||
Folder Name
|
||||
</label>
|
||||
<IconArrowBackUp
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
@@ -118,14 +119,25 @@ const NewFolder = ({ collection, item, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
item={item}
|
||||
filename={formik.values.directoryName}
|
||||
showExtension={false}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="directoryName" className="block font-semibold">
|
||||
Folder Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
dirName={path.relative(collection?.pathname, item?.pathname || collection?.pathname)}
|
||||
baseName={formik.values.directoryName}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.directoryName && formik.errors.directoryName ? (
|
||||
<div className="text-red-500">{formik.errors.directoryName}</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useRef, useEffect, useCallback, forwardRef, useState } from 'rea
|
||||
import { useFormik } from 'formik';
|
||||
import * as Yup from 'yup';
|
||||
import toast from 'react-hot-toast';
|
||||
import path from 'utils/common/path';
|
||||
import { uuid } from 'utils/common';
|
||||
import Modal from 'components/Modal';
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -11,7 +12,7 @@ import { addTab } from 'providers/ReduxStore/slices/tabs';
|
||||
import HttpMethodSelector from 'components/RequestPane/QueryUrl/HttpMethodSelector';
|
||||
import { getDefaultRequestPaneTab } from 'utils/collections';
|
||||
import { getRequestFromCurlCommand } from 'utils/curl';
|
||||
import { IconArrowBackUp, IconCaretDown } from '@tabler/icons';
|
||||
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';
|
||||
@@ -57,7 +58,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
setCurlRequestTypeDetected(type);
|
||||
};
|
||||
|
||||
const [isEditingFilename, toggleEditingFilename] = useState(false);
|
||||
const [isEditing, toggleEditing] = useState(false);
|
||||
|
||||
const getRequestType = (collectionPresets) => {
|
||||
if (!collectionPresets || !collectionPresets.requestType) {
|
||||
@@ -309,7 +310,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
spellCheck="false"
|
||||
onChange={e => {
|
||||
formik.setFieldValue('requestName', e.target.value);
|
||||
!isEditingFilename && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
!isEditing && formik.setFieldValue('filename', sanitizeName(e.target.value));
|
||||
}}
|
||||
value={formik.values.requestName || ''}
|
||||
/>
|
||||
@@ -317,7 +318,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
<div className="text-red-500">{formik.errors.requestName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
{isEditingFilename ? (
|
||||
{isEditing ? (
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="filename" className="block font-semibold">
|
||||
@@ -327,7 +328,7 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditingFilename(false)}
|
||||
onClick={() => toggleEditing(false)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
@@ -348,13 +349,25 @@ const NewRequest = ({ collection, item, isEphemeral, onClose }) => {
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<PathDisplay
|
||||
collection={collection}
|
||||
item={item}
|
||||
filename={formik.values.filename}
|
||||
isEditingFilename={isEditingFilename}
|
||||
toggleEditingFilename={toggleEditingFilename}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<label htmlFor="baseName" className="block font-semibold">
|
||||
File Path
|
||||
</label>
|
||||
<IconEdit
|
||||
className="cursor-pointer opacity-50 hover:opacity-80"
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
onClick={() => toggleEditing(true)}
|
||||
/>
|
||||
</div>
|
||||
<div className='relative flex flex-row gap-1 items-center justify-between'>
|
||||
<PathDisplay
|
||||
dirName={path.relative(collection?.pathname, item?.pathname || collection?.pathname)}
|
||||
baseName={formik.values.filename? `${formik.values.filename}.bru` : ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{formik.touched.filename && formik.errors.filename ? (
|
||||
<div className="text-red-500">{formik.errors.filename}</div>
|
||||
|
||||
@@ -187,6 +187,6 @@ export const stringifyIfNot = v => typeof v === 'string' ? v : String(v);
|
||||
|
||||
export const getEncoding = (headers) => {
|
||||
// Parse the charset from content type: https://stackoverflow.com/a/33192813
|
||||
const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(headers['content-type'] || '');
|
||||
const charsetMatch = /charset=([^()<>@,;:"/[\]?.=\s]*)/i.exec(headers?.['content-type'] || '');
|
||||
return charsetMatch?.[1];
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ const invalidCharacters = /[<>:"/\\|?*\x00-\x1F]/g; // replace invalid character
|
||||
const reservedDeviceNames = /^(CON|PRN|AUX|NUL|COM[0-9]|LPT[0-9])$/i;
|
||||
const firstCharacter = /^[^.\s\-\<>:"/\\|?*\x00-\x1F]/; // no dot, space, or hyphen at start
|
||||
const middleCharacters = /^[^<>:"/\\|?*\x00-\x1F]*$/; // no invalid characters
|
||||
const lastCharacter = /[^.\s]$/; // no dot or space at end, hyphen allowed
|
||||
const lastCharacter = /^[^.\s\-\<>:"/\\|?*\x00-\x1F]/; // no dot or space at end, hyphen allowed
|
||||
|
||||
export const variableNameRegex = /^[\w-.]*$/;
|
||||
|
||||
@@ -29,6 +29,7 @@ export const validateName = (name) => {
|
||||
|
||||
export const validateNameError = (name) => {
|
||||
if (!name) return "Name cannot be empty.";
|
||||
|
||||
if (name.length > 255) {
|
||||
return "Name cannot exceed 255 characters.";
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
import parseCurlCommand from './parse-curl';
|
||||
import * as querystring from 'query-string';
|
||||
import * as jsesc from 'jsesc';
|
||||
import * as path from 'path';
|
||||
|
||||
function getContentType(headers = {}) {
|
||||
const contentType = Object.keys(headers).find((key) => key.toLowerCase() === 'content-type');
|
||||
|
||||
Reference in New Issue
Block a user