fix(dashboard): UI polish — modals, layout, consistency, test fixes

Dashboard UX polish pass — consolidates create forms into modals
triggered from the page header, fixes layout inconsistencies, adds
scroll-to navigation for the Keys page, and aligns the TokenBar with
the design system.

Changes:
- App.tsx: add padding to sidebar header
- resolve-page-title.ts: add missing routes, better fallback title
- en.ts: fix nav labels (Profiles was 'profiles : multi agents')
- ModelsPage: two-col layout, auxiliary tasks modal, TokenBar redesign
- ProfilesPage: create button in header, form in modal, Checkbox component
- CronPage: create button in header, form in modal
- EnvPage: scroll-to sub-nav in header, fix text overflow

Modal and dialog standardization:
- Replace all native confirm()/window.confirm() with ConfirmDialog
  (OAuthProvidersCard, PluginsPage, ModelsPage, ConfigPage)
- Add useModalBehavior hook (Escape-to-close, scroll lock, focus restore)
- Apply hook to ProfilesPage, CronPage, AuxiliaryTasksModal

Component fixes (from PR review):
- Checkbox: fix controlled/uncontrolled mismatch, add focus-visible ring
- TokenBar: add rounded-full to legend dots, remove dead code

CI/test fixes:
- Fix TS unused imports (noUnusedLocals), type-narrow PickerTarget union
- Add windows-footgun suppression on platform-guarded os.killpg
- Fix 19 stale unit tests + 9 e2e tests broken by recent main changes
- Restore minimal example-dashboard plugin for plugin auth test
This commit is contained in:
Austin Pickett 2026-05-12 13:42:14 -04:00
parent dd0923bb89
commit fc3fd6bb6b
27 changed files with 788 additions and 295 deletions

View file

@ -20,6 +20,7 @@ import {
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@nous-research/ui/ui/components/badge";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
import { OAuthLoginModal } from "@/components/OAuthLoginModal";
import { useI18n } from "@/i18n";
@ -55,6 +56,8 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
const [loading, setLoading] = useState(true);
const [busyId, setBusyId] = useState<string | null>(null);
const [loginFor, setLoginFor] = useState<OAuthProvider | null>(null);
const [disconnectTarget, setDisconnectTarget] =
useState<OAuthProvider | null>(null);
const { t } = useI18n();
const onErrorRef = useRef(onError);
@ -74,10 +77,8 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
}, [refresh]);
const handleDisconnect = async (provider: OAuthProvider) => {
if (!confirm(`${t.oauth.disconnect} ${provider.name}?`)) {
return;
}
setBusyId(provider.id);
setDisconnectTarget(null);
try {
await api.disconnectOAuthProvider(provider.id);
onSuccess?.(`${provider.name} ${t.oauth.disconnect.toLowerCase()}ed`);
@ -236,7 +237,7 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
<Button
size="sm"
outlined
onClick={() => handleDisconnect(p)}
onClick={() => setDisconnectTarget(p)}
disabled={isBusy}
prefix={isBusy ? <Spinner /> : <LogOut />}
>
@ -266,6 +267,17 @@ export function OAuthProvidersCard({ onError, onSuccess }: Props) {
onError={(msg) => onError?.(msg)}
/>
)}
<ConfirmDialog
open={disconnectTarget !== null}
onCancel={() => setDisconnectTarget(null)}
onConfirm={() => {
if (disconnectTarget) void handleDisconnect(disconnectTarget);
}}
title={`${t.oauth.disconnect} ${disconnectTarget?.name ?? ""}?`}
description={`This will remove the stored OAuth tokens for ${disconnectTarget?.name ?? "this provider"}. You will need to re-authenticate to use it again.`}
destructive
confirmLabel={t.oauth.disconnect}
/>
</Card>
);
}

View file

@ -0,0 +1,61 @@
import { cn } from "@/lib/utils";
import { Check } from "lucide-react";
interface CheckboxProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "type"> {
label?: React.ReactNode;
}
export function Checkbox({
className,
label,
id,
checked,
defaultChecked,
...props
}: CheckboxProps) {
// Support both controlled (checked prop) and uncontrolled (defaultChecked) usage.
// For visual rendering, prefer `checked` if provided; otherwise fall back to defaultChecked.
const isChecked = checked ?? defaultChecked ?? false;
return (
<label
htmlFor={id}
className={cn(
"group flex items-center gap-2.5 cursor-pointer select-none",
props.disabled && "cursor-not-allowed opacity-50",
)}
>
<span
className={cn(
"flex h-4 w-4 shrink-0 items-center justify-center transition-all",
"border bg-background/40",
// Focus-visible ring for keyboard accessibility
"group-has-[:focus-visible]:ring-2 group-has-[:focus-visible]:ring-ring group-has-[:focus-visible]:ring-offset-1",
isChecked
? "border-foreground bg-foreground/20"
: "border-border group-hover:border-foreground/40",
className,
)}
>
<Check
className={cn(
"h-3 w-3 transition-opacity",
isChecked
? "text-foreground opacity-100"
: "text-foreground opacity-0",
)}
/>
</span>
<input
type="checkbox"
id={id}
checked={checked}
defaultChecked={checked === undefined ? defaultChecked : undefined}
className="sr-only"
{...props}
/>
{label && <span className="text-sm">{label}</span>}
</label>
);
}