Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/web/src/components/icon-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,24 @@ type Props = {
label: string;
children: ReactNode;
disabled?: boolean;
size?: 'xs' | 'sm';
} & ({onClick: () => void; to?: never} | {to: string; onClick?: never});

// A header's icon action: a ghost icon button with its label in a tooltip.
export function IconButton({label, children, disabled = false, onClick, to}: Props) {
export function IconButton({label, children, disabled = false, size = 'sm', onClick, to}: Props) {
const button =
to === undefined ? (
<Button
variant="ghost"
size="icon-sm"
size={size === 'xs' ? 'icon-xs' : 'icon-sm'}
aria-label={label}
disabled={disabled}
onClick={onClick}
/>
) : (
<Button
variant="ghost"
size="icon-sm"
size={size === 'xs' ? 'icon-xs' : 'icon-sm'}
aria-label={label}
nativeButton={false}
render={<Link to={to} />}
Expand Down
18 changes: 11 additions & 7 deletions packages/web/src/components/section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {ReactNode} from 'react';

type Props = {
title: ReactNode;
titleAction?: ReactNode;
detail?: ReactNode;
action?: ReactNode;
children: ReactNode;
Expand All @@ -10,16 +11,19 @@ type Props = {
// A titled part of a page. The heading is indented like a row's text, so it lines up with the
// list beneath it. A flex column rather than a grid: a grid's column would grow to its widest
// truncated row on a phone instead of letting the row truncate.
export function Section({title, detail, action, children}: Props) {
export function Section({title, titleAction, detail, action, children}: Props) {
return (
<section className="flex flex-col gap-2">
<div className="flex min-h-8 items-center justify-between gap-4 md:px-3">
<h2 className="text-sm font-medium">
{title}
{detail !== undefined && (
<span className="font-normal text-muted-foreground"> · {detail}</span>
)}
</h2>
<div className="flex min-w-0 items-center gap-1">
<h2 className="text-sm font-medium">
{title}
{detail !== undefined && (
<span className="font-normal text-muted-foreground"> · {detail}</span>
)}
</h2>
{titleAction}
</div>
{action}
</div>
{children}
Expand Down
14 changes: 13 additions & 1 deletion packages/web/src/pages/connection-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {CircleCheckIcon, UnplugIcon} from 'lucide-react';
import {CircleCheckIcon, RefreshCwIcon, UnplugIcon} from 'lucide-react';
import {useState} from 'react';
import {Link, useLoaderData, useNavigate, useRevalidator, useSearchParams} from 'react-router';
import {deleteConnection, setConnectionDefaults} from '@/client/connections-client';
Expand Down Expand Up @@ -103,6 +103,18 @@ export function ConnectionPage() {
</Section>
<Section
title="Recent activity"
titleAction={
<IconButton
label={revalidator.state === 'loading' ? 'Refreshing activity' : 'Refresh activity'}
size="xs"
disabled={revalidator.state === 'loading'}
onClick={() => void revalidator.revalidate()}
>
<RefreshCwIcon
className={revalidator.state === 'loading' ? 'motion-safe:animate-spin' : undefined}
/>
</IconButton>
}
detail={
entries.length > RECENT ? (
<Link
Expand Down
Loading