mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-12 10:21:30 +00:00
fix: use generic error component, tab padding fixes (#6563)
* fix: use generic error component, tab padding fixes * fix: script error padding * rename errorMessage to error banner, move to ui folder * fix: replace errorAlert with errorBanner component * show orange dot always
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
import Button from 'ui/Button';
|
||||
|
||||
const ExampleNotFound = ({ exampleUid }) => {
|
||||
@@ -23,14 +24,16 @@ const ExampleNotFound = ({ exampleUid }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const errors = [
|
||||
{
|
||||
title: 'Response example no longer exists',
|
||||
message: 'This can occur when the example definition in your local file has been deleted or updated.'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mt-6 px-6">
|
||||
<div className="p-4 bg-orange-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<div>Response example no longer exists.</div>
|
||||
<div className="mt-2">
|
||||
This can occur when the example definition in your local file has been deleted or updated.
|
||||
</div>
|
||||
</div>
|
||||
<ErrorBanner errors={errors} className="mb-4" />
|
||||
<Button size="md" color="secondary" variant="ghost" onClick={closeTab}>
|
||||
Close Tab
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
import Button from 'ui/Button';
|
||||
|
||||
const FolderNotFound = ({ folderUid }) => {
|
||||
@@ -25,14 +26,16 @@ const FolderNotFound = ({ folderUid }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const errors = [
|
||||
{
|
||||
title: 'Folder no longer exists',
|
||||
message: 'This can happen when the folder was renamed or deleted on your filesystem.'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mt-6 px-6">
|
||||
<div className="p-4 bg-orange-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<div>Folder no longer exists.</div>
|
||||
<div className="mt-2">
|
||||
This can happen when the folder was renamed or deleted on your filesystem.
|
||||
</div>
|
||||
</div>
|
||||
<ErrorBanner errors={errors} className="mb-4" />
|
||||
<Button size="md" color="secondary" variant="ghost" onClick={closeTab}>
|
||||
Close Tab
|
||||
</Button>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
import Button from 'ui/Button';
|
||||
|
||||
const RequestNotFound = ({ itemUid }) => {
|
||||
@@ -29,14 +30,16 @@ const RequestNotFound = ({ itemUid }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const errors = [
|
||||
{
|
||||
title: 'Request no longer exists',
|
||||
message: 'This can happen when the .bru file associated with this request was deleted on your filesystem.'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mt-6 px-6">
|
||||
<div className="p-4 bg-orange-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<div>Request no longer exists.</div>
|
||||
<div className="mt-2">
|
||||
This can happen when the .bru file associated with this request was deleted on your filesystem.
|
||||
</div>
|
||||
</div>
|
||||
<ErrorBanner errors={errors} className="mb-4" />
|
||||
<Button size="md" color="secondary" variant="ghost" onClick={closeTab}>
|
||||
Close Tab
|
||||
</Button>
|
||||
|
||||
@@ -58,7 +58,7 @@ const ExampleTab = ({ tab, collection }) => {
|
||||
if (!item || !example) {
|
||||
return (
|
||||
<StyledWrapper
|
||||
className="flex items-center justify-between tab-container px-3"
|
||||
className="flex items-center justify-between tab-container"
|
||||
onMouseUp={(e) => {
|
||||
if (e.button === 1) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -20,7 +20,7 @@ const RequestTabNotFound = ({ handleCloseClick }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center tab-label pl-2">
|
||||
<div className="flex items-center tab-label px-3">
|
||||
{showErrorMessage ? (
|
||||
<>
|
||||
<IconAlertTriangle size={18} strokeWidth={1.5} className="text-yellow-600" />
|
||||
|
||||
@@ -320,7 +320,7 @@ const RequestTab = ({ tab, collection, tabIndex, collectionRequestTabs, folderUi
|
||||
if (!item) {
|
||||
return (
|
||||
<StyledWrapper
|
||||
className="flex items-center justify-between tab-container px-1"
|
||||
className="flex items-center justify-between tab-container"
|
||||
onMouseUp={(e) => {
|
||||
if (e.button === 1) {
|
||||
e.preventDefault();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import ReactJson from 'react-json-view';
|
||||
import ErrorAlert from 'ui/ErrorAlert/index';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
|
||||
const JsonPreview = ({ data, displayedTheme }) => {
|
||||
// Helper function to validate and parse JSON data
|
||||
@@ -29,16 +29,16 @@ const JsonPreview = ({ data, displayedTheme }) => {
|
||||
|
||||
// Show error if parsing failed
|
||||
if (jsonData.error) {
|
||||
return <ErrorAlert title="Cannot preview as JSON" message={jsonData.error} />;
|
||||
return <ErrorBanner errors={[{ title: 'Cannot preview as JSON', message: jsonData.error }]} />;
|
||||
}
|
||||
|
||||
// Validate that data can be rendered as JSON tree
|
||||
if (jsonData.data === null || jsonData.data === undefined) {
|
||||
return <ErrorAlert title="Cannot preview as JSON" message="Data is null or undefined. Expected a valid JSON object or array." />;
|
||||
return <ErrorBanner errors={[{ title: 'Cannot preview as JSON', message: 'Data is null or undefined. Expected a valid JSON object or array.' }]} />;
|
||||
}
|
||||
|
||||
if (typeof jsonData.data !== 'object') {
|
||||
return <ErrorAlert title="Cannot preview as JSON" message="Data cannot be rendered as a JSON tree. Expected a JSON object or array." />;
|
||||
return <ErrorBanner errors={[{ title: 'Cannot preview as JSON', message: 'Data cannot be rendered as a JSON tree. Expected a JSON object or array.' }]} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import ErrorAlert from 'ui/ErrorAlert/index';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function XmlPreview({ data, defaultExpanded = true }) {
|
||||
if (parsedData && typeof parsedData === 'object' && parsedData.error) {
|
||||
return (
|
||||
<div className="px-2">
|
||||
<ErrorAlert title="Cannot preview as XML" message={parsedData.error} />
|
||||
<ErrorBanner errors={[{ title: 'Cannot preview as XML', message: parsedData.error }]} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ export default function XmlPreview({ data, defaultExpanded = true }) {
|
||||
if (!isValidTreeData(parsedData)) {
|
||||
return (
|
||||
<div className="px-2">
|
||||
<ErrorAlert title="Cannot preview as XML" message="Data cannot be rendered as a tree. Expected a valid XML string." />
|
||||
<ErrorBanner errors={[{ title: 'Cannot preview as XML', message: 'Data cannot be rendered as a tree. Expected a valid XML string.' }]} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -55,7 +55,7 @@ export default function XmlPreview({ data, defaultExpanded = true }) {
|
||||
// Empty object with no children
|
||||
return (
|
||||
<div className="px-2">
|
||||
<ErrorAlert title="Cannot preview as XML" message="Cannot render XML tree. Root object is empty." />
|
||||
<ErrorBanner errors={[{ title: 'Cannot preview as XML', message: 'Cannot render XML tree. Root object is empty.' }]} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { IconX } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
import ErrorBanner from 'ui/ErrorBanner';
|
||||
|
||||
const ScriptError = ({ item, onClose }) => {
|
||||
const preRequestError = item?.preRequestScriptErrorMessage;
|
||||
@@ -32,31 +31,7 @@ const ScriptError = ({ item, onClose }) => {
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-4 mb-2">
|
||||
<div className="flex items-start gap-3 px-4 py-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
{errors.map((error, index) => (
|
||||
<div key={index}>
|
||||
{index > 0 && <div className="separator my-2"></div>}
|
||||
<div className="error-title">
|
||||
{error.title}
|
||||
</div>
|
||||
<div className="error-message">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="close-button flex-shrink-0 cursor-pointer"
|
||||
onClick={onClose}
|
||||
>
|
||||
<IconX size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
return <ErrorBanner errors={errors} onClose={onClose} className="mt-4 mb-2" />;
|
||||
};
|
||||
|
||||
export default ScriptError;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
import { IconX } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ErrorAlert = ({ title, message, onClose }) => {
|
||||
if (!message) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-4 mb-2">
|
||||
<div className="flex items-start gap-3 px-4 py-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
{title && <div className="error-title">{title}</div>}
|
||||
<div className="error-message">{typeof message === 'string' ? message : JSON.stringify(message, null, 2)}</div>
|
||||
</div>
|
||||
{onClose && (
|
||||
<div className="close-button flex-shrink-0 cursor-pointer" onClick={onClose}>
|
||||
<IconX size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorAlert;
|
||||
@@ -1,36 +1,33 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const StyledWrapper = styled.div`
|
||||
border-left: 4px solid ${(props) => props.theme.colors.text.danger};
|
||||
border-top: 1px solid transparent;
|
||||
border-right: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
max-height: 200px;
|
||||
min-height: 70px;
|
||||
overflow-y: auto;
|
||||
background-color: ${(props) => (props.theme.bg === '#1e1e1e' ? 'rgba(40, 40, 40, 0.5)' : 'rgba(250, 250, 250, 0.9)')};
|
||||
|
||||
background-color: ${(props) => props.theme.background.base};
|
||||
border: solid 1px ${(props) => props.theme.border.border2};
|
||||
border-left: 4px solid ${(props) => props.theme.colors.text.danger};
|
||||
border-radius: ${(props) => props.theme.border.radius.base};
|
||||
|
||||
.close-button {
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
svg {
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.error-title {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.375rem;
|
||||
color: ${(props) => props.theme.colors.text.danger};
|
||||
}
|
||||
|
||||
|
||||
.error-message {
|
||||
font-family: monospace;
|
||||
font-size: ${(props) => props.theme.font.size.xs};
|
||||
@@ -39,6 +36,9 @@ const StyledWrapper = styled.div`
|
||||
word-break: break-all;
|
||||
color: ${(props) => props.theme.text};
|
||||
}
|
||||
`;
|
||||
|
||||
.separator {
|
||||
border-top: 1px solid ${(props) => props.theme.border.border1};
|
||||
}
|
||||
`;
|
||||
export default StyledWrapper;
|
||||
37
packages/bruno-app/src/ui/ErrorBanner/index.js
Normal file
37
packages/bruno-app/src/ui/ErrorBanner/index.js
Normal file
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { IconX } from '@tabler/icons';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const ErrorBanner = ({ errors, onClose, className = '' }) => {
|
||||
if (!errors || errors.length === 0) return null;
|
||||
|
||||
return (
|
||||
<StyledWrapper className={className}>
|
||||
<div className="flex items-start gap-3 px-4 py-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
{errors.map((error, index) => (
|
||||
<div key={index}>
|
||||
{index > 0 && <div className="separator my-2"></div>}
|
||||
<div className="error-title">
|
||||
{error.title}
|
||||
</div>
|
||||
<div className="error-message">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{onClose && (
|
||||
<div
|
||||
className="close-button flex-shrink-0 cursor-pointer"
|
||||
onClick={onClose}
|
||||
>
|
||||
<IconX size={16} strokeWidth={1.5} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorBanner;
|
||||
Reference in New Issue
Block a user