Spaces:
Running
Running
Download apps/web/src/components/ui/ErrorState.tsx from Benaah/MicProtocol: direct link, hf CLI and curl.
- Browser
- Download file 9.18 kB
-
https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/web/src/components/ui/ErrorState.tsx
- Command line
-
hf download hf://spaces/Benaah/MicProtocol/apps/web/src/components/ui/ErrorState.tsx
-
curl -L -o ErrorState.tsx https://huggingface.co/spaces/Benaah/MicProtocol/resolve/main/apps/web/src/components/ui/ErrorState.tsx
9.18 kB
| "use client"; | |
| import React, { useState } from "react"; | |
| import { | |
| AlertTriangle, | |
| RotateCcw, | |
| ChevronDown, | |
| ChevronUp, | |
| HelpCircle, | |
| WifiOff, | |
| ShieldAlert, | |
| Clock, | |
| } from "lucide-react"; | |
| import { parseError, FormattedError } from "@/lib/error-utils"; | |
| export interface ErrorStateProps { | |
| title?: string; | |
| description?: string; | |
| error?: Error | unknown; | |
| /** Action called when clicking "Try Again" */ | |
| onRetry?: () => void; | |
| /** Custom action slot */ | |
| action?: React.ReactNode; | |
| /** Compact presentation for cards/widgets */ | |
| compact?: boolean; | |
| /** Whether to show technical diagnostic details */ | |
| showDiagnostics?: boolean; | |
| className?: string; | |
| style?: React.CSSProperties; | |
| } | |
| /** | |
| * EasyUI-inspired ErrorState component for widgets, cards, and page sections. | |
| * Features Neo-Industrial dark aesthetics, spring-like pulse icon, and collapsible diagnostic trace. | |
| */ | |
| export function ErrorState({ | |
| title: customTitle, | |
| description: customDescription, | |
| error, | |
| onRetry, | |
| action, | |
| compact = false, | |
| showDiagnostics = true, | |
| className = "", | |
| style, | |
| }: ErrorStateProps) { | |
| const [showDetails, setShowDetails] = useState(false); | |
| const formatted: FormattedError = parseError(error); | |
| const displayTitle = customTitle || formatted.title; | |
| const displayDescription = customDescription || formatted.message; | |
| // Select contextual icon based on error category | |
| let IconComponent = AlertTriangle; | |
| let accentColor = "var(--color-error, #EF4444)"; | |
| if (formatted.isNetworkError) { | |
| IconComponent = WifiOff; | |
| accentColor = "var(--color-warning, #F59E0B)"; | |
| } else if (formatted.isAuthError || formatted.isForbidden) { | |
| IconComponent = ShieldAlert; | |
| accentColor = "var(--color-secondary, #8B5CF6)"; | |
| } else if (formatted.isRateLimit) { | |
| IconComponent = Clock; | |
| accentColor = "var(--color-warning, #F59E0B)"; | |
| } | |
| if (compact) { | |
| return ( | |
| <div | |
| role="alert" | |
| className={`error-state-compact ${className}`} | |
| style={{ | |
| display: "flex", | |
| alignItems: "center", | |
| gap: "var(--space-2, 0.5rem)", | |
| padding: "var(--space-2, 0.5rem) var(--space-3, 0.75rem)", | |
| background: "rgba(239, 68, 68, 0.06)", | |
| border: "1px solid rgba(239, 68, 68, 0.2)", | |
| borderRadius: "var(--radius-sm, 2px)", | |
| fontSize: "var(--text-xs, 0.75rem)", | |
| color: "var(--text-secondary, #A7A3B4)", | |
| ...style, | |
| }} | |
| > | |
| <IconComponent size={14} color={accentColor} style={{ flexShrink: 0 }} /> | |
| <span style={{ flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}> | |
| {displayDescription} | |
| </span> | |
| {onRetry && ( | |
| <button | |
| type="button" | |
| onClick={onRetry} | |
| style={{ | |
| background: "transparent", | |
| border: "none", | |
| color: accentColor, | |
| fontWeight: 600, | |
| cursor: "pointer", | |
| display: "flex", | |
| alignItems: "center", | |
| gap: 3, | |
| padding: 0, | |
| }} | |
| > | |
| <RotateCcw size={11} /> Retry | |
| </button> | |
| )} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div | |
| role="alert" | |
| className={`error-state-container ${className}`} | |
| style={{ | |
| position: "relative", | |
| display: "flex", | |
| flexDirection: "column", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| textAlign: "center", | |
| padding: "var(--space-8, 2rem) var(--space-6, 1.5rem)", | |
| background: "var(--bg-elevated, #17151F)", | |
| border: "1px solid var(--border-color, #272430)", | |
| borderRadius: "var(--radius-md, 4px)", | |
| boxShadow: "0 8px 32px rgba(0, 0, 0, 0.35)", | |
| overflow: "hidden", | |
| ...style, | |
| }} | |
| > | |
| {/* Top accent glow line */} | |
| <div | |
| style={{ | |
| position: "absolute", | |
| top: 0, | |
| left: 0, | |
| right: 0, | |
| height: 2, | |
| background: `linear-gradient(90deg, transparent, ${accentColor}, transparent)`, | |
| opacity: 0.8, | |
| }} | |
| /> | |
| {/* Pulsing Icon Badge */} | |
| <div | |
| style={{ | |
| position: "relative", | |
| display: "flex", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| width: 52, | |
| height: 52, | |
| borderRadius: "var(--radius-md, 4px)", | |
| background: `color-mix(in srgb, ${accentColor} 12%, transparent)`, | |
| border: `1px solid color-mix(in srgb, ${accentColor} 30%, transparent)`, | |
| color: accentColor, | |
| marginBottom: "var(--space-4, 1rem)", | |
| }} | |
| > | |
| <IconComponent size={26} strokeWidth={2.2} /> | |
| </div> | |
| {/* Title */} | |
| <h3 | |
| style={{ | |
| fontFamily: "var(--font-display, inherit)", | |
| fontSize: "var(--text-base, 1rem)", | |
| fontWeight: 700, | |
| color: "var(--text-primary, #F6F4F8)", | |
| marginBottom: "var(--space-1, 0.25rem)", | |
| letterSpacing: "-0.01em", | |
| }} | |
| > | |
| {displayTitle} | |
| </h3> | |
| {/* Description */} | |
| <p | |
| style={{ | |
| fontSize: "var(--text-sm, 0.875rem)", | |
| color: "var(--text-secondary, #A7A3B4)", | |
| maxWidth: 460, | |
| lineHeight: 1.55, | |
| marginBottom: onRetry || action ? "var(--space-5, 1.25rem)" : 0, | |
| }} | |
| > | |
| {displayDescription} | |
| </p> | |
| {/* Status & Code Pill if available */} | |
| {formatted.code && ( | |
| <div | |
| style={{ | |
| display: "inline-flex", | |
| alignItems: "center", | |
| gap: 6, | |
| padding: "2px 8px", | |
| background: "rgba(255, 255, 255, 0.04)", | |
| border: "1px solid var(--border-color-subtle, #1C1924)", | |
| borderRadius: "var(--radius-sm, 2px)", | |
| fontSize: "11px", | |
| fontFamily: "var(--font-mono, monospace)", | |
| color: "var(--text-tertiary, #6D697C)", | |
| marginBottom: "var(--space-4, 1rem)", | |
| }} | |
| > | |
| <span>CODE: {formatted.code}</span> | |
| {formatted.statusCode && <span>({formatted.statusCode})</span>} | |
| </div> | |
| )} | |
| {/* Actions */} | |
| {(onRetry || action) && ( | |
| <div | |
| style={{ | |
| display: "flex", | |
| flexWrap: "wrap", | |
| alignItems: "center", | |
| justifyContent: "center", | |
| gap: "var(--space-3, 0.75rem)", | |
| }} | |
| > | |
| {onRetry && ( | |
| <button | |
| type="button" | |
| onClick={onRetry} | |
| className="btn btn-primary" | |
| style={{ | |
| display: "inline-flex", | |
| alignItems: "center", | |
| gap: "var(--space-2, 0.5rem)", | |
| fontSize: "var(--text-sm, 0.875rem)", | |
| fontWeight: 600, | |
| padding: "var(--space-2, 0.5rem) var(--space-4, 1rem)", | |
| }} | |
| > | |
| <RotateCcw size={14} /> Try Again | |
| </button> | |
| )} | |
| {action} | |
| </div> | |
| )} | |
| {/* Diagnostics / Error details accordion */} | |
| {showDiagnostics && (error || formatted.requestId) && ( | |
| <div style={{ marginTop: "var(--space-4, 1rem)", width: "100%", maxWidth: 500 }}> | |
| <button | |
| type="button" | |
| onClick={() => setShowDetails(!showDetails)} | |
| style={{ | |
| background: "transparent", | |
| border: "none", | |
| color: "var(--text-tertiary, #6D697C)", | |
| fontSize: "var(--text-xs, 0.75rem)", | |
| cursor: "pointer", | |
| display: "inline-flex", | |
| alignItems: "center", | |
| gap: 4, | |
| padding: 4, | |
| }} | |
| > | |
| {showDetails ? ( | |
| <> | |
| Hide diagnostics <ChevronUp size={12} /> | |
| </> | |
| ) : ( | |
| <> | |
| Show diagnostics <ChevronDown size={12} /> | |
| </> | |
| )} | |
| </button> | |
| {showDetails && ( | |
| <div | |
| style={{ | |
| marginTop: "var(--space-2, 0.5rem)", | |
| padding: "var(--space-3, 0.75rem)", | |
| background: "rgba(0, 0, 0, 0.45)", | |
| border: "1px solid var(--border-color-subtle, #1C1924)", | |
| borderRadius: "var(--radius-sm, 2px)", | |
| fontFamily: "var(--font-mono, monospace)", | |
| fontSize: "11px", | |
| textAlign: "left", | |
| color: "var(--text-secondary, #A7A3B4)", | |
| maxHeight: 180, | |
| overflowY: "auto", | |
| whiteSpace: "pre-wrap", | |
| wordBreak: "break-all", | |
| }} | |
| > | |
| {formatted.requestId && ( | |
| <div style={{ marginBottom: 4, color: "var(--color-primary, #FF9F0A)" }}> | |
| Request ID: {formatted.requestId} | |
| </div> | |
| )} | |
| {error instanceof Error ? error.stack || error.message : JSON.stringify(error, null, 2)} | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |