Better status tags and delete request on key

This commit is contained in:
Gregory Schier
2023-04-04 12:36:30 -07:00
parent 7d154800a0
commit b3c461afdd
12 changed files with 125 additions and 74 deletions

View File

@@ -2,6 +2,7 @@ interface Props {
data: string;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function ImageView({ data }: Props) {
// const dataUri = `data:image/png;base64,${window.btoa(data)}`;
return <div>Image preview not supported until binary response support is added</div>;

View File

@@ -15,7 +15,7 @@ interface Props {
export function RequestActionsDropdown({ requestId, children }: Props) {
const request = useRequest(requestId ?? null);
const deleteRequest = useDeleteRequest(requestId ?? null);
const deleteRequest = useDeleteRequest(request);
const duplicateRequest = useDuplicateRequest({ id: requestId, navigateAfter: true });
const confirm = useConfirm();

View File

@@ -9,6 +9,7 @@ import { useResponses } from '../hooks/useResponses';
import { useResponseViewMode } from '../hooks/useResponseViewMode';
import { tryFormatJson } from '../lib/formatters';
import type { HttpResponse } from '../lib/models';
import { isResponseLoading } from '../lib/models';
import { pluralize } from '../lib/pluralize';
import { Banner } from './core/Banner';
import { CountBadge } from './core/CountBadge';
@@ -17,7 +18,7 @@ import { Editor } from './core/Editor';
import { Icon } from './core/Icon';
import { IconButton } from './core/IconButton';
import { HStack } from './core/Stacks';
import { StatusColor } from './core/StatusColor';
import { StatusTag } from './core/StatusTag';
import type { TabItem } from './core/Tabs/Tabs';
import { TabContent, Tabs } from './core/Tabs/Tabs';
import { Webview } from './core/Webview';
@@ -93,7 +94,8 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
'shadow shadow-gray-100 dark:shadow-gray-0 relative',
)}
>
{activeResponse && (
{activeResponse?.error && <Banner className="m-2">{activeResponse.error}</Banner>}
{activeResponse && !activeResponse.error && !isResponseLoading(activeResponse) && (
<>
<HStack
alignItems="center"
@@ -106,22 +108,15 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
{activeResponse && (
<HStack alignItems="center" className="w-full">
<div className="whitespace-nowrap px-3">
<StatusColor statusCode={activeResponse.status}>
{activeResponse.status}
{activeResponse.statusReason && ` ${activeResponse.statusReason}`}
</StatusColor>
&nbsp;&bull;&nbsp;
{activeResponse.elapsed}ms &nbsp;&bull;&nbsp;
{(activeResponse.body.length / 1000).toFixed(1)} KB
<StatusTag response={activeResponse} />
{activeResponse.elapsed > 0 && <>&nbsp;&bull;&nbsp;{activeResponse.elapsed}ms</>}
{activeResponse.body.length > 0 && (
<>&nbsp;&bull;&nbsp;{(activeResponse.body.length / 1000).toFixed(1)} KB</>
)}
</div>
<Dropdown
items={[
{
label: viewMode === 'pretty' ? 'View Raw' : 'View Prettified',
onSelect: toggleViewMode,
},
{ type: 'separator', label: 'Actions' },
{
label: 'Clear Response',
onSelect: deleteResponse.mutate,
@@ -153,9 +148,7 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
)}
</HStack>
{activeResponse?.error ? (
<Banner className="m-2">{activeResponse.error}</Banner>
) : (
{
<Tabs
value={activeTab}
onChangeValue={setActiveTab}
@@ -196,7 +189,7 @@ export const ResponsePane = memo(function ResponsePane({ style, className }: Pro
) : null}
</TabContent>
</Tabs>
)}
}
</>
)}
</div>

View File

@@ -4,15 +4,18 @@ import React, { forwardRef, Fragment, memo, useCallback, useMemo, useRef, useSta
import type { XYCoord } from 'react-dnd';
import { useDrag, useDrop } from 'react-dnd';
import { useActiveRequest } from '../hooks/useActiveRequest';
import { useDeleteRequest } from '../hooks/useDeleteRequest';
import { useLatestResponse } from '../hooks/useLatestResponse';
import { useRequests } from '../hooks/useRequests';
import { useUpdateAnyRequest } from '../hooks/useUpdateAnyRequest';
import { useUpdateRequest } from '../hooks/useUpdateRequest';
import type { HttpRequest } from '../lib/models';
import { isResponseLoading } from '../lib/models';
import { Button } from './core/Button';
import { IconButton } from './core/IconButton';
import { Icon } from './core/Icon';
import { HStack, VStack } from './core/Stacks';
import { StatusTag } from './core/StatusTag';
import { DropMarker } from './DropMarker';
import { RequestActionsDropdown } from './RequestActionsDropdown';
import { ToggleThemeButton } from './ToggleThemeButton';
interface Props {
@@ -136,7 +139,9 @@ const _SidebarItem = forwardRef(function SidebarItem(
{ className, requestName, requestId, workspaceId, active }: SidebarItemProps,
ref: ForwardedRef<HTMLLIElement>,
) {
const latestResponse = useLatestResponse(requestId);
const updateRequest = useUpdateRequest(requestId);
const deleteRequest = useDeleteRequest(requestId);
const [editing, setEditing] = useState<boolean>(false);
const handleSubmitNameEdit = useCallback(
@@ -159,12 +164,17 @@ const _SidebarItem = forwardRef(function SidebarItem(
e.preventDefault();
setEditing(true);
}
if (active && (e.key === 'Backspace' || e.key === 'Delete')) {
e.preventDefault();
deleteRequest.mutate();
}
},
[active],
[active, deleteRequest],
);
const handleInputKeyDown = useCallback(
async (e: KeyboardEvent<HTMLInputElement>) => {
e.stopPropagation();
switch (e.key) {
case 'Enter':
e.preventDefault();
@@ -183,12 +193,12 @@ const _SidebarItem = forwardRef(function SidebarItem(
<li ref={ref} className={classnames(className, 'block group/item px-2 pb-0.5')}>
<div className="relative">
<Button
tabIndex={0}
color="custom"
size="sm"
to={`/workspaces/${workspaceId}/requests/${requestId}`}
draggable={false} // Item should drag, not the link
onDoubleClick={() => setEditing(true)}
onClick={active ? () => setEditing(true) : undefined}
justify="start"
onKeyDown={handleKeyDown}
className={classnames(
@@ -196,8 +206,6 @@ const _SidebarItem = forwardRef(function SidebarItem(
active
? 'bg-highlight text-gray-900'
: 'text-gray-600 group-hover/item:text-gray-800 active:bg-highlightSecondary',
// Move out of the way when trash is shown
'group-hover/item:pr-7',
)}
>
{editing ? (
@@ -213,19 +221,20 @@ const _SidebarItem = forwardRef(function SidebarItem(
{requestName || 'New Request'}
</span>
)}
{latestResponse && (
<div className="ml-auto">
{isResponseLoading(latestResponse) ? (
<Icon spin size="sm" icon="update" />
) : (
<StatusTag
asBackground
className="px-0.5 rounded-sm font-mono text-2xs"
response={latestResponse}
/>
)}
</div>
)}
</Button>
<RequestActionsDropdown requestId={requestId}>
<IconButton
color="custom"
size="sm"
title="Request Options"
icon="dotsH"
className={classnames(
'absolute right-0 top-0 transition-opacity !opacity-0',
'group-hover/item:!opacity-100 focus-visible:!opacity-100',
)}
/>
</RequestActionsDropdown>
</div>
</li>
);

View File

@@ -183,19 +183,16 @@
@apply bg-highlight text-gray-900;
}
& > ul > li:hover {
@apply text-gray-800;
}
.cm-completionIcon {
@apply text-sm flex items-center pb-0.5 flex-shrink-0;
}
.cm-completionLabel {
@apply text-gray-700;
}
.cm-completionDetail {
@apply ml-auto pl-4;
@apply ml-auto pl-6;
}
}
}

View File

@@ -1,23 +0,0 @@
import classnames from 'classnames';
import type { ReactNode } from 'react';
interface Props {
statusCode: number;
children: ReactNode;
}
export function StatusColor({ statusCode, children }: Props) {
return (
<span
className={classnames(
statusCode >= 100 && statusCode < 200 && 'text-green-600',
statusCode >= 200 && statusCode < 300 && 'text-green-600',
statusCode >= 300 && statusCode < 400 && 'text-pink-600',
statusCode >= 400 && statusCode < 500 && 'text-orange-600',
statusCode >= 500 && statusCode < 600 && 'text-red-600',
)}
>
{children}
</span>
);
}

View File

@@ -0,0 +1,47 @@
import classnames from 'classnames';
import type { HttpResponse } from '../../lib/models';
interface Props {
response: Pick<HttpResponse, 'status' | 'error'>;
className?: string;
asBackground?: boolean;
}
export function StatusTag({ asBackground, response, className }: Props) {
const { status, error } = response;
const label = error ? 'ERR' : status;
if (asBackground) {
return (
<span
className={classnames(
className,
'text-white bg-opacity-90',
status >= 0 && status < 100 && 'bg-red-600',
status >= 100 && status < 200 && 'bg-yellow-600',
status >= 200 && status < 300 && 'bg-green-600',
status >= 300 && status < 400 && 'bg-pink-600',
status >= 400 && status < 500 && 'bg-orange-600',
status >= 500 && 'bg-red-600',
)}
>
{label}
</span>
);
} else {
return (
<span
className={classnames(
className,
status >= 0 && status < 100 && 'text-red-600',
status >= 100 && status < 200 && 'text-green-600',
status >= 200 && status < 300 && 'text-green-600',
status >= 300 && status < 400 && 'text-pink-600',
status >= 400 && status < 500 && 'text-orange-600',
status >= 500 && 'text-red-600',
)}
>
{label}
</span>
);
}
}