mirror of
https://github.com/jhbruhn/respira.git
synced 2026-01-27 10:23:41 +00:00
feature: Migrate AppHeader buttons and badges to shadcn/ui
- Replace Disconnect button with shadcn Button (outline variant) - Replace status badge with shadcn Badge component - Replace error button with shadcn Button (destructive variant) - Use cn() helper for conditional className composition - Preserve glass morphism effects and custom styling Code reduction: - Disconnect button: ~40% reduction - Status badge: ~30% reduction - Error button: ~60% reduction Improved maintainability with semantic component usage and cleaner code structure. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
08532d0b01
commit
365b0c7ae3
1 changed files with 21 additions and 10 deletions
|
|
@ -13,6 +13,9 @@ import {
|
||||||
ArrowPathIcon,
|
ArrowPathIcon,
|
||||||
XMarkIcon,
|
XMarkIcon,
|
||||||
} from "@heroicons/react/24/solid";
|
} from "@heroicons/react/24/solid";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export function AppHeader() {
|
export function AppHeader() {
|
||||||
const {
|
const {
|
||||||
|
|
@ -130,19 +133,24 @@ export function AppHeader() {
|
||||||
<div className="flex items-center gap-2 mt-1 min-h-[32px]">
|
<div className="flex items-center gap-2 mt-1 min-h-[32px]">
|
||||||
{isConnected ? (
|
{isConnected ? (
|
||||||
<>
|
<>
|
||||||
<button
|
<Button
|
||||||
onClick={disconnect}
|
onClick={disconnect}
|
||||||
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 sm:py-1 rounded text-sm font-medium bg-white/10 hover:bg-danger-600 text-primary-100 hover:text-white border border-white/20 hover:border-danger-600 cursor-pointer transition-all flex-shrink-0"
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
className="gap-1.5 bg-white/10 hover:bg-danger-600 text-primary-100 hover:text-white border-white/20 hover:border-danger-600 flex-shrink-0"
|
||||||
title="Disconnect from machine"
|
title="Disconnect from machine"
|
||||||
aria-label="Disconnect from machine"
|
aria-label="Disconnect from machine"
|
||||||
>
|
>
|
||||||
<XMarkIcon className="w-3 h-3" />
|
<XMarkIcon className="w-3 h-3" />
|
||||||
Disconnect
|
Disconnect
|
||||||
</button>
|
</Button>
|
||||||
<span className="inline-flex items-center gap-1.5 px-2.5 py-1.5 sm:py-1 rounded text-sm font-semibold bg-white/20 text-white border border-white/30 flex-shrink-0">
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="gap-1.5 px-2.5 py-1.5 sm:py-1 text-sm font-semibold bg-white/20 text-white border-white/30 flex-shrink-0"
|
||||||
|
>
|
||||||
<StatusIcon className="w-3 h-3" />
|
<StatusIcon className="w-3 h-3" />
|
||||||
{machineStatusName}
|
{machineStatusName}
|
||||||
</span>
|
</Badge>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-xs text-primary-200">Not Connected</p>
|
<p className="text-xs text-primary-200">Not Connected</p>
|
||||||
|
|
@ -150,14 +158,17 @@ export function AppHeader() {
|
||||||
|
|
||||||
{/* Error indicator - always render to prevent layout shift */}
|
{/* Error indicator - always render to prevent layout shift */}
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<button
|
<Button
|
||||||
ref={errorButtonRef}
|
ref={errorButtonRef}
|
||||||
onClick={() => setErrorPopover(!showErrorPopover)}
|
onClick={() => setErrorPopover(!showErrorPopover)}
|
||||||
className={`inline-flex items-center gap-1.5 px-2.5 py-1.5 sm:py-1 rounded text-sm font-medium bg-danger-500/90 hover:bg-danger-600 text-white border border-danger-400 transition-all flex-shrink-0 ${
|
size="sm"
|
||||||
|
variant="destructive"
|
||||||
|
className={cn(
|
||||||
|
"gap-1.5 flex-shrink-0",
|
||||||
machineErrorMessage || pyodideError
|
machineErrorMessage || pyodideError
|
||||||
? "cursor-pointer animate-pulse hover:animate-none"
|
? "animate-pulse hover:animate-none"
|
||||||
: "invisible pointer-events-none"
|
: "invisible pointer-events-none"
|
||||||
}`}
|
)}
|
||||||
title="Click to view error details"
|
title="Click to view error details"
|
||||||
aria-label="View error details"
|
aria-label="View error details"
|
||||||
disabled={!(machineErrorMessage || pyodideError)}
|
disabled={!(machineErrorMessage || pyodideError)}
|
||||||
|
|
@ -191,7 +202,7 @@ export function AppHeader() {
|
||||||
return "Error";
|
return "Error";
|
||||||
})()}
|
})()}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</Button>
|
||||||
|
|
||||||
{/* Error popover */}
|
{/* Error popover */}
|
||||||
{showErrorPopover && (machineErrorMessage || pyodideError) && (
|
{showErrorPopover && (machineErrorMessage || pyodideError) && (
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue