{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "status-bar",
  "title": "Status Bar",
  "description": "Interactive status timeline with hover, keyboard navigation, and event display",
  "dependencies": [
    "date-fns"
  ],
  "registryDependencies": [
    "https://openstatus.dev/r/status-types.json",
    "https://openstatus.dev/r/status-utils.json",
    "https://openstatus.dev/r/status-i18n.json",
    "hover-card",
    "separator",
    "skeleton",
    "https://openstatus.dev/r/use-media-query.json"
  ],
  "files": [
    {
      "path": "src/components/blocks/status-bar.tsx",
      "content": "\"use client\";\n\nimport {\n  HoverCard,\n  HoverCardContent,\n  HoverCardTrigger,\n} from \"@/components/ui/hover-card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { useMediaQuery } from \"@/hooks/use-media-query\";\nimport { cn } from \"@/lib/utils\";\nimport { formatDistanceStrict } from \"date-fns\";\nimport { useCallback, useEffect, useRef, useState, forwardRef } from \"react\";\nimport { statusColors } from \"@/components/blocks/status.utils\";\nimport type {\n  StatusBarData,\n  StatusEventType,\n  StatusType,\n} from \"@/components/blocks/status.types\";\nimport { useStatusBlocksLabels } from \"@/components/blocks/status-i18n\";\n\ninterface StatusBarProps {\n  data: StatusBarData[];\n  renderCard?: (\n    data: StatusBarData[\"card\"][number],\n    index: number,\n  ) => React.ReactNode;\n  renderBar?: (\n    data: StatusBarData[\"bar\"][number],\n    index: number,\n  ) => React.ReactNode;\n  renderEvent?: (\n    data: StatusBarData[\"events\"][number],\n    index: number,\n  ) => React.ReactNode;\n  /**\n   * Optional Radix Portal container for the day hover-card. Defaults to\n   * `document.body`. Pass a ref when the bar is rendered inside a scoped\n   * subtree (e.g. a status-page preview that overrides `--radius` and other\n   * CSS vars on a wrapper) so the portaled card inherits the same context.\n   */\n  container?: HTMLElement | null;\n}\n\ninterface UseStatusBarProps {\n  dataLength: number;\n  isTouch: boolean;\n}\n\ntype InteractionType = \"pin\" | \"hover\" | \"focus\" | null;\n\n/**\n * useStatusBar - Headless hook for managing status bar interactions and keyboard navigation\n *\n * This hook provides the core logic for StatusBar interactions, implementing a\n * headless UI pattern that separates state management from presentation. It handles:\n *\n * **Interaction Modes**:\n * - **hover**: Desktop hover state (shows card on mouse hover, auto-hides on leave)\n * - **pin**: Pinned state (click to pin card open, click again or Esc to close)\n * - **focus**: Keyboard focus state (shows card while focused, hides on blur)\n *\n * **Keyboard Navigation**:\n * - **Arrow Left/Right**: Navigate between bars in the same timeline\n * - **Arrow Up/Down**: Navigate between timelines (different monitors)\n * - **Enter/Space**: Pin/unpin the active bar\n * - **Escape**: Close pinned card and remove focus\n *\n * **Touch Device Support**:\n * - Disables hover state on touch devices (detected via `(hover: none)` media query)\n * - Click interaction works on both touch and non-touch devices\n *\n * **Outside Click Handling**:\n * - Automatically closes pinned cards when clicking outside the component\n *\n * @param dataLength - Total number of bars in the timeline\n * @param isTouch - Whether the device supports touch (no hover capability)\n *\n * @returns Hook state and handlers:\n * - `activeIndex`: Currently active bar index (null if none)\n * - `isOpen`: Whether a card is currently displayed\n * - `interactionType`: Current interaction mode (\"pin\" | \"hover\" | \"focus\" | null)\n * - `containerRef`: Ref for the status bar container (for outside click detection)\n * - `handlers`: Event handler functions for mouse/keyboard/focus interactions\n * - `setButtonRef`: Function to register bar element refs (for keyboard navigation)\n *\n * @example\n * ```tsx\n * function CustomStatusBar({ data }) {\n *   const isTouch = useMediaQuery(\"(hover: none)\");\n *   const { activeIndex, handlers, setButtonRef, containerRef } = useStatusBar({\n *     dataLength: data.length,\n *     isTouch,\n *   });\n *\n *   return (\n *     <div ref={containerRef} role=\"toolbar\">\n *       {data.map((item, index) => (\n *         <button\n *           key={index}\n *           ref={(el) => setButtonRef(index, el)}\n *           onClick={() => handlers.onClick(index)}\n *           onFocus={() => handlers.onFocus(index)}\n *           onKeyDown={(e) => handlers.onKeyDown(e, index)}\n *         >\n *           {// Custom bar rendering}\n *         </button>\n *       ))}\n *     </div>\n *   );\n * }\n * ```\n *\n * @see StatusBar - For the complete implementation using this hook\n */\nfunction useStatusBar({ dataLength, isTouch }: UseStatusBarProps) {\n  const [activeIndex, setActiveIndex] = useState<number | null>(null);\n  const [interactionType, setInteractionType] = useState<InteractionType>(null);\n  const buttonRefs = useRef<(HTMLElement | null)[]>([]);\n  const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n  const containerRef = useRef<HTMLDivElement>(null);\n\n  // Clear hover timeout on unmount\n  useEffect(() => {\n    return () => {\n      if (hoverTimeoutRef.current) {\n        clearTimeout(hoverTimeoutRef.current);\n      }\n    };\n  }, []);\n\n  // Handle clicks outside to close pinned card\n  useEffect(() => {\n    if (interactionType !== \"pin\" || activeIndex === null) return;\n\n    const handleOutsideClick = (e: MouseEvent) => {\n      if (\n        containerRef.current &&\n        !containerRef.current.contains(e.target as Node)\n      ) {\n        setActiveIndex(null);\n        setInteractionType(null);\n      }\n    };\n\n    document.addEventListener(\"mousedown\", handleOutsideClick);\n    return () => document.removeEventListener(\"mousedown\", handleOutsideClick);\n  }, [interactionType, activeIndex]);\n\n  const clearHoverTimeout = useCallback(() => {\n    if (hoverTimeoutRef.current) {\n      clearTimeout(hoverTimeoutRef.current);\n      hoverTimeoutRef.current = null;\n    }\n  }, []);\n\n  const interactionTypeRef = useRef<InteractionType>(null);\n  useEffect(() => {\n    interactionTypeRef.current = interactionType;\n  }, [interactionType]);\n\n  const handleClick = useCallback(\n    (index: number) => {\n      clearHoverTimeout();\n      setActiveIndex((prev) => {\n        if (prev === index && interactionTypeRef.current === \"pin\") {\n          setInteractionType(null);\n          return null;\n        }\n        setInteractionType(\"pin\");\n        return index;\n      });\n    },\n    [clearHoverTimeout],\n  );\n\n  const handleHoverStart = useCallback(\n    (index: number) => {\n      // On touch devices, don't show hover state\n      if (isTouch) return;\n\n      clearHoverTimeout();\n      setActiveIndex(index);\n      setInteractionType(\"hover\");\n    },\n    [isTouch, clearHoverTimeout],\n  );\n\n  const handleHoverEnd = useCallback(() => {\n    // Only clear hover state, not pinned or focused\n    if (interactionType !== \"hover\") return;\n\n    hoverTimeoutRef.current = setTimeout(() => {\n      setActiveIndex(null);\n      setInteractionType(null);\n    }, 100);\n  }, [interactionType]);\n\n  const handleFocus = useCallback((index: number) => {\n    setActiveIndex(index);\n    setInteractionType(\"focus\");\n  }, []);\n\n  const handleBlur = useCallback((e: React.FocusEvent) => {\n    // Only clear if not moving to another bar\n    const relatedTarget = e.relatedTarget as HTMLElement;\n    const isMovingToAnotherBar =\n      relatedTarget &&\n      relatedTarget.closest('[role=\"toolbar\"]') === containerRef.current &&\n      relatedTarget.getAttribute(\"role\") === \"button\";\n\n    if (!isMovingToAnotherBar) {\n      setActiveIndex(null);\n      setInteractionType(null);\n    }\n  }, []);\n\n  const handleKeyDown = useCallback(\n    (e: React.KeyboardEvent, currentIndex: number) => {\n      switch (e.key) {\n        case \"Escape\":\n          e.preventDefault();\n          setActiveIndex(null);\n          setInteractionType(null);\n          clearHoverTimeout();\n          buttonRefs.current[currentIndex]?.blur();\n          break;\n\n        case \"ArrowLeft\":\n          e.preventDefault();\n          {\n            const newIndex =\n              currentIndex > 0 ? currentIndex - 1 : dataLength - 1;\n            buttonRefs.current[newIndex]?.focus();\n          }\n          break;\n\n        case \"ArrowRight\":\n          e.preventDefault();\n          {\n            const newIndex =\n              currentIndex < dataLength - 1 ? currentIndex + 1 : 0;\n            buttonRefs.current[newIndex]?.focus();\n          }\n          break;\n\n        case \"ArrowUp\":\n          e.preventDefault();\n          {\n            // Navigate to previous monitor's status bar\n            const prevMonitor = containerRef.current?.closest(\n              '[data-slot=\"status-component\"]',\n            )?.previousElementSibling;\n            if (prevMonitor) {\n              const prevBar = prevMonitor.querySelector('[role=\"toolbar\"]');\n              if (prevBar) {\n                const prevButtons = prevBar.querySelectorAll('[role=\"button\"]');\n                const targetButton = prevButtons[currentIndex] as HTMLElement;\n                targetButton?.focus();\n              }\n            }\n          }\n          break;\n\n        case \"ArrowDown\":\n          e.preventDefault();\n          {\n            // Navigate to next monitor's status bar\n            const nextMonitor = containerRef.current?.closest(\n              '[data-slot=\"status-component\"]',\n            )?.nextElementSibling;\n            if (nextMonitor) {\n              const nextBar = nextMonitor.querySelector('[role=\"toolbar\"]');\n              if (nextBar) {\n                const nextButtons = nextBar.querySelectorAll('[role=\"button\"]');\n                const targetButton = nextButtons[currentIndex] as HTMLElement;\n                targetButton?.focus();\n              }\n            }\n          }\n          break;\n\n        case \"Enter\":\n        case \" \":\n          e.preventDefault();\n          handleClick(currentIndex);\n          break;\n      }\n    },\n    [dataLength, clearHoverTimeout, handleClick],\n  );\n\n  const setButtonRef = useCallback((index: number, el: HTMLElement | null) => {\n    buttonRefs.current[index] = el;\n  }, []);\n\n  return {\n    activeIndex,\n    isOpen: activeIndex !== null,\n    interactionType,\n    containerRef,\n    handlers: {\n      onClick: handleClick,\n      onHoverStart: handleHoverStart,\n      onHoverEnd: handleHoverEnd,\n      onHoverCardEnter: clearHoverTimeout,\n      onHoverCardLeave: () => {\n        setActiveIndex(null);\n        setInteractionType(null);\n      },\n      onFocus: handleFocus,\n      onBlur: handleBlur,\n      onKeyDown: handleKeyDown,\n    },\n    setButtonRef,\n  };\n}\n\n/**\n * StatusBar - Interactive uptime timeline with keyboard navigation and hover cards\n *\n * Displays a horizontal timeline of status bars, where each bar represents a day's\n * status with color-coded segments. Interactive hover cards show detailed status\n * information, events, and incidents for each day.\n *\n * **Key Features**:\n * - **Visual Timeline**: Vertical bars with color-coded segments showing status over time\n * - **Interactive Cards**: Hover/click to see detailed breakdowns and events\n * - **Keyboard Navigation**: Full keyboard support with arrow keys, Enter, and Escape\n * - **Touch Support**: Optimized interactions for touch devices\n * - **Customizable Rendering**: Override default renderers for bars, cards, and events\n * - **Accessibility**: ARIA roles, labels, and keyboard navigation\n *\n * **Interaction Modes**:\n * - Desktop: Hover to preview, click to pin, Esc to close\n * - Touch: Tap to toggle card open/closed\n * - Keyboard: Arrow keys to navigate, Enter/Space to pin, Esc to close\n *\n * **Keyboard Navigation**:\n * - **Left/Right Arrows**: Move between bars in the timeline\n * - **Up/Down Arrows**: Navigate between different monitor timelines\n * - **Enter/Space**: Pin or unpin the active card\n * - **Escape**: Close card and remove focus\n *\n * **Data Structure**:\n * Each data item represents a day and contains:\n * - `day`: Date string\n * - `bar`: Array of segments with status and height percentage\n * - `card`: Array of status breakdowns to display in hover card\n * - `events`: Array of incidents/maintenance events for that day\n *\n * @param data - Array of status bar data items (one per day)\n * @param renderCard - Optional custom renderer for card content items\n * @param renderBar - Optional custom renderer for bar segments\n * @param renderEvent - Optional custom renderer for event badges\n *\n * @example\n * // Basic usage with default rendering\n * ```tsx\n * const uptimeData = [\n *   {\n *     day: \"2024-01-15\",\n *     bar: [{ status: \"success\", height: 100 }],\n *     card: [{ status: \"success\", value: \"100%\" }],\n *     events: []\n *   },\n *   {\n *     day: \"2024-01-16\",\n *     bar: [\n *       { status: \"success\", height: 80 },\n *       { status: \"error\", height: 20 }\n *     ],\n *     card: [\n *       { status: \"success\", value: \"80%\" },\n *       { status: \"error\", value: \"20%\" }\n *     ],\n *     events: [\n *       {\n *         id: \"inc-1\",\n *         type: \"incident\",\n *         name: \"API Downtime\",\n *         from: new Date(\"2024-01-16T10:00:00Z\"),\n *         to: new Date(\"2024-01-16T10:30:00Z\")\n *       }\n *     ]\n *   }\n * ];\n *\n * <StatusBar data={uptimeData} />\n * ```\n *\n * @example\n * // With custom bar renderer\n * ```tsx\n * <StatusBar\n *   data={uptimeData}\n *   renderBar={(segment, index) => (\n *     <div\n *       key={index}\n *       className=\"w-full transition-all\"\n *       style={{\n *         height: `${segment.height}%`,\n *         background: `linear-gradient(to top, ${statusColors[segment.status]}, transparent)`\n *       }}\n *     />\n *   )}\n * />\n * ```\n *\n * @example\n * // With custom event renderer\n * ```tsx\n * <StatusBar\n *   data={uptimeData}\n *   renderEvent={(event, index) => (\n *     <Link key={index} href={`/incidents/${event.id}`}>\n *       <div className=\"text-sm hover:underline\">\n *         {event.name}\n *       </div>\n *     </Link>\n *   )}\n * />\n * ```\n *\n * @see useStatusBar - For the headless hook powering the interactions\n * @see StatusBarSkeleton - For loading state\n * @see StatusBarEvent - For event badge rendering\n */\nexport function StatusBar({\n  data,\n  renderCard,\n  renderBar,\n  renderEvent,\n  container,\n}: StatusBarProps) {\n  const labels = useStatusBlocksLabels();\n  const isTouch = useMediaQuery(\"(hover: none)\");\n  const { activeIndex, interactionType, containerRef, handlers, setButtonRef } =\n    useStatusBar({\n      dataLength: data.length,\n      isTouch,\n    });\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"flex h-[50px] w-full items-end gap-px\"\n      data-slot=\"status-bar\"\n      role=\"toolbar\"\n      aria-label={labels.ariaStatusTracker}\n    >\n      {data.map((item, index) => {\n        const isActive = activeIndex === index;\n        const isPinned = isActive && interactionType === \"pin\";\n\n        return (\n          <StatusBarItem\n            key={item.day}\n            ref={(el) => setButtonRef(index, el)}\n            index={index}\n            item={item}\n            isActive={isActive}\n            isPinned={isPinned}\n            isTouch={isTouch}\n            isLastItem={index === data.length - 1}\n            handlers={handlers}\n            renderCard={renderCard}\n            renderBar={renderBar}\n            renderEvent={renderEvent}\n            container={container}\n          />\n        );\n      })}\n    </div>\n  );\n}\nStatusBar.displayName = \"StatusBar\";\n\ninterface StatusBarItemProps {\n  index: number;\n  item: StatusBarData;\n  isActive: boolean;\n  isPinned: boolean;\n  isTouch: boolean;\n  isLastItem: boolean;\n  handlers: ReturnType<typeof useStatusBar>[\"handlers\"];\n  renderCard?: StatusBarProps[\"renderCard\"];\n  renderBar?: StatusBarProps[\"renderBar\"];\n  renderEvent?: StatusBarProps[\"renderEvent\"];\n  container?: StatusBarProps[\"container\"];\n}\n\nconst StatusBarItem = forwardRef<HTMLDivElement, StatusBarItemProps>(\n  (\n    {\n      index,\n      item,\n      isActive,\n      isPinned,\n      isTouch,\n      isLastItem,\n      handlers,\n      renderCard,\n      renderBar,\n      renderEvent,\n      container,\n    },\n    ref,\n  ) => {\n    const labels = useStatusBlocksLabels();\n    return (\n      <HoverCard openDelay={0} closeDelay={0} open={isActive}>\n        <HoverCardTrigger asChild>\n          <div\n            ref={ref}\n            className=\"group relative flex h-full flex-1 cursor-pointer flex-col outline-none hover:opacity-80 focus-visible:opacity-80 focus-visible:ring-[2px] focus-visible:ring-ring/50 aria-pressed:opacity-80 rounded-full\"\n            onClick={() => handlers.onClick(index)}\n            onFocus={() => handlers.onFocus(index)}\n            onBlur={handlers.onBlur}\n            onMouseEnter={() => handlers.onHoverStart(index)}\n            onMouseLeave={handlers.onHoverEnd}\n            onKeyDown={(e) => handlers.onKeyDown(e, index)}\n            tabIndex={isLastItem && !isActive ? 0 : isActive ? 0 : -1}\n            role=\"button\"\n            aria-label={labels.ariaDayStatus(index + 1)}\n            aria-pressed={isPinned}\n            aria-expanded={isActive}\n            data-slot=\"status-bar-item\"\n          >\n            <div className=\"flex h-full w-full flex-col overflow-hidden rounded-full\">\n              {/* Render bar segments */}\n              {item.bar.map((segment, segmentIndex) => {\n                if (renderBar) {\n                  return renderBar(segment, segmentIndex);\n                }\n                return (\n                  <div\n                    key={`${item.day}-${segment.status}-${segmentIndex}`}\n                    className={cn(\"w-full transition-all\", {\n                      \"rounded-t-full\": segmentIndex === 0,\n                      \"rounded-b-full\": segmentIndex === item.bar.length - 1,\n                    })}\n                    style={{\n                      height: `${segment.height}%`,\n                      backgroundColor: statusColors[segment.status],\n                    }}\n                  />\n                );\n              })}\n            </div>\n          </div>\n        </HoverCardTrigger>\n        <HoverCardContent\n          side=\"top\"\n          align=\"center\"\n          className=\"w-auto min-w-40 p-0\"\n          container={container}\n          onMouseEnter={handlers.onHoverCardEnter}\n          onMouseLeave={handlers.onHoverCardLeave}\n          onPointerDownOutside={(e) => {\n            // Prevent closing on touch devices when clicking the card\n            if (isTouch) {\n              e.preventDefault();\n            }\n          }}\n        >\n          <StatusBarCard\n            item={item}\n            isPinned={isPinned}\n            isTouch={isTouch}\n            renderCard={renderCard}\n            renderEvent={renderEvent}\n          />\n        </HoverCardContent>\n      </HoverCard>\n    );\n  },\n);\nStatusBarItem.displayName = \"StatusBarItem\";\n\ninterface StatusBarCardProps {\n  item: StatusBarData;\n  isPinned: boolean;\n  isTouch: boolean;\n  renderCard?: StatusBarProps[\"renderCard\"];\n  renderEvent?: StatusBarProps[\"renderEvent\"];\n}\n\n/**\n * StatusBarCard - Internal hover card content component\n *\n * Displays detailed status information for a single day in a hover card, including:\n * - Date header\n * - Status breakdown percentages\n * - Events/incidents for that day\n * - Pin/unpin instructions (when pinned on desktop)\n *\n * The card automatically formats the date and renders status items and events\n * using either custom renderers or default implementations.\n *\n * @param item - The status bar data for this day\n * @param isPinned - Whether the card is currently pinned open\n * @param isTouch - Whether the device supports touch\n * @param renderCard - Optional custom renderer for status items\n * @param renderEvent - Optional custom renderer for events\n */\nfunction StatusBarCard({\n  item,\n  isPinned,\n  isTouch,\n  renderCard,\n  renderEvent,\n}: StatusBarCardProps) {\n  const labels = useStatusBlocksLabels();\n  return (\n    <div data-slot=\"status-bar-card\">\n      <div className=\"p-2 text-xs\">\n        {labels.formatDateShort(new Date(item.day))}\n      </div>\n      <Separator />\n      <div className=\"space-y-1 p-2 text-sm\">\n        {item.card.map((cardItem, cardIndex) => {\n          if (renderCard) {\n            return renderCard(cardItem, cardIndex);\n          }\n          return (\n            <StatusBarContent\n              key={`${item.day}-card-${cardIndex}`}\n              status={cardItem.status}\n              value={cardItem.value}\n            />\n          );\n        })}\n      </div>\n      {item.events.length > 0 && (\n        <>\n          <Separator />\n          <div className=\"p-2\">\n            {item.events.map((event, eventIndex) => {\n              if (renderEvent) {\n                return renderEvent(event, eventIndex);\n              }\n              return (\n                <StatusBarEvent\n                  key={`${event.id}-${event.type}`}\n                  type={event.type}\n                  name={event.name}\n                  from={event.from}\n                  to={event.to}\n                  isAggregated={event.isAggregated}\n                />\n              );\n            })}\n          </div>\n        </>\n      )}\n      {isPinned && !isTouch && (\n        <>\n          <Separator />\n          <div className=\"flex cursor-pointer items-center p-2 text-muted-foreground text-xs\">\n            <span>{labels.clickAgainToUnpin}</span>\n            <kbd className=\"ml-auto inline-flex h-5 max-h-5 min-w-5 items-center justify-center rounded border border-input bg-background px-1.5 font-mono text-[10px] font-medium text-muted-foreground\">\n              Esc\n            </kbd>\n          </div>\n        </>\n      )}\n    </div>\n  );\n}\nStatusBarCard.displayName = \"StatusBarCard\";\n\n/**\n * StatusBarSkeleton - Loading skeleton for StatusBar\n *\n * Displays a skeleton loader matching the height and width of a StatusBar,\n * used while status data is being fetched.\n *\n * @example\n * ```tsx\n * {isLoading ? (\n *   <StatusBarSkeleton />\n * ) : (\n *   <StatusBar data={uptimeData} />\n * )}\n * ```\n *\n * @see StatusBar - For the actual status bar component\n */\nexport function StatusBarSkeleton({\n  className,\n  ...props\n}: React.ComponentProps<typeof Skeleton>) {\n  return (\n    <Skeleton\n      className={cn(\"h-[50px] w-full rounded-none bg-muted\", className)}\n      {...props}\n    />\n  );\n}\nStatusBarSkeleton.displayName = \"StatusBarSkeleton\";\n\n/**\n * StatusBarContent - Internal component for status breakdown rows\n *\n * Displays a single status item in the hover card with a colored indicator,\n * status label, and percentage value. Used by StatusBarCard to show the\n * breakdown of statuses for a day.\n *\n * @param status - The status type (success, degraded, error, info, empty)\n * @param value - The percentage or count value to display\n *\n * @example\n * ```tsx\n * <StatusBarContent status=\"success\" value=\"95.2%\" />\n * <StatusBarContent status=\"error\" value=\"4.8%\" />\n * ```\n */\nfunction StatusBarContent({\n  status,\n  value,\n}: {\n  status: StatusType;\n  value: string;\n}) {\n  const labels = useStatusBlocksLabels();\n  return (\n    <div className=\"flex items-baseline gap-4\" data-slot=\"status-bar-content\">\n      <div className=\"flex items-center gap-2\">\n        <div\n          className=\"h-2.5 w-2.5 rounded-sm\"\n          style={{\n            backgroundColor: statusColors[status],\n          }}\n        />\n        <div className=\"text-sm\">{labels.requestStatus[status]}</div>\n      </div>\n      <div className=\"ml-auto font-mono text-muted-foreground text-xs tracking-tight\">\n        {value}\n      </div>\n    </div>\n  );\n}\nStatusBarContent.displayName = \"StatusBarContent\";\n\n/**\n * StatusBarEvent - Event badge for incidents and maintenance\n *\n * Displays an event within a status bar hover card, showing:\n * - Color-coded indicator (red for incidents, yellow for reports, blue for maintenance)\n * - Event name with truncation for long names\n * - Date range (formatted as \"Since\", \"Until\", or \"Jan 15 - Jan 16\")\n * - Duration (formatted as \"2 hours\", \"ongoing\", or \"across 3 days\" for multiple incidents)\n *\n * The component automatically determines the status color based on the event type:\n * - incident → error (red)\n * - report → degraded (yellow)\n * - maintenance → info (blue)\n *\n * Returns null if no start date is provided.\n *\n * @param name - Event name or title\n * @param from - Event start date\n * @param to - Event end date (null for ongoing events)\n * @param type - Event type (\"incident\", \"report\", or \"maintenance\")\n *\n * @example\n * ```tsx\n * <StatusBarEvent\n *   type=\"incident\"\n *   name=\"API Downtime\"\n *   from={new Date(\"2024-01-15T10:00:00Z\")}\n *   to={new Date(\"2024-01-15T10:30:00Z\")}\n * />\n * // Displays: [●] API Downtime\n * //           Jan 15, 10:00 AM - 10:30 AM  30 minutes\n * ```\n *\n * @example\n * ```tsx\n * // Ongoing incident\n * <StatusBarEvent\n *   type=\"incident\"\n *   name=\"Database connectivity issues\"\n *   from={new Date()}\n *   to={null}\n * />\n * // Displays: [●] Database connectivity issues\n * //           Since Jan 15, 2:30 PM  ongoing\n * ```\n *\n * @see StatusBarCard - For the card that contains event badges\n * @see formatDateRange - For date range formatting\n */\nexport function StatusBarEvent({\n  name,\n  from,\n  to,\n  type,\n  isAggregated,\n}: {\n  name: string;\n  from?: Date | null;\n  to?: Date | null;\n  type: StatusEventType;\n  isAggregated?: boolean;\n}) {\n  const labels = useStatusBlocksLabels();\n  if (!from) return null;\n\n  const status =\n    type === \"incident\" ? \"error\" : type === \"report\" ? \"degraded\" : \"info\";\n\n  return (\n    <div className=\"group relative text-sm\" data-slot=\"status-bar-event\">\n      {/* NOTE: this is to make the text truncate based on the width of the sibling element */}\n      {/* REMINDER: height needs to be equal the text height */}\n      <div className=\"h-4 w-full\" />\n      <div className=\"absolute inset-0 text-muted-foreground hover:text-foreground\">\n        <div className=\"flex items-center gap-2\">\n          <div\n            className=\"h-2.5 w-2.5 shrink-0 rounded-sm\"\n            style={{\n              backgroundColor: statusColors[status],\n            }}\n          />\n          <div className=\"truncate\">{name}</div>\n        </div>\n      </div>\n      <div className=\"mt-1 text-muted-foreground text-xs\">\n        {labels.formatDateRange(from, to ?? undefined)}{\" \"}\n        <span className=\"ml-1.5 font-mono text-muted-foreground/70\">\n          {formatDuration({ from, to, name, type, isAggregated, labels })}\n        </span>\n      </div>\n    </div>\n  );\n}\nStatusBarEvent.displayName = \"StatusBarEvent\";\n\n/**\n * formatDuration - Internal helper for formatting event durations\n *\n * Formats the duration of an event based on start/end dates:\n * - No start date: returns null\n * - No end date: returns \"ongoing\"\n * - Aggregated incidents (isAggregated): returns \"across {duration}\"\n * - Zero seconds duration: returns null (hides duration)\n * - Otherwise: returns formatted duration (e.g., \"2 hours\", \"3 days\")\n *\n * @returns Formatted duration string or null\n */\nconst formatDuration = ({\n  from,\n  to,\n  isAggregated,\n  labels,\n}: React.ComponentProps<typeof StatusBarEvent> & {\n  labels: ReturnType<typeof useStatusBlocksLabels>;\n}) => {\n  if (!from) return null;\n  if (!to) return labels.ongoing;\n  const duration = formatDistanceStrict(from, to);\n  if (isAggregated) return labels.durationAcross(duration);\n  if (duration === \"0 seconds\") return null;\n  return duration;\n};\n",
      "type": "registry:ui",
      "target": "components/blocks/status-bar.tsx"
    }
  ],
  "type": "registry:block"
}