{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "status-feed",
  "title": "Status Feed",
  "description": "Unified feed component combining status reports and maintenance events",
  "registryDependencies": [
    "https://openstatus.dev/r/status-blank.json",
    "https://openstatus.dev/r/status-events.json",
    "https://openstatus.dev/r/status-types.json",
    "https://openstatus.dev/r/status-i18n.json"
  ],
  "files": [
    {
      "path": "src/components/blocks/status-feed.tsx",
      "content": "\"use client\";\nimport { useMemo } from \"react\";\nimport { StatusBlankEvents } from \"@/components/blocks/status-blank\";\nimport {\n  StatusEvent,\n  StatusEventAffected,\n  StatusEventAffectedBadge,\n  StatusEventAside,\n  StatusEventContent,\n  StatusEventDate,\n  StatusEventGroup,\n  StatusEventTimelineMaintenance,\n  StatusEventTimelineReport,\n  StatusEventTitle,\n} from \"@/components/blocks/status-events\";\nimport { useStatusBlocksLabels } from \"@/components/blocks/status-i18n\";\nimport type {\n  StatusReport,\n  Maintenance,\n} from \"@/components/blocks/status.types\";\n\ntype UnifiedEvent = {\n  id: number;\n  title: string;\n  startDate: Date;\n} & (\n  | { type: \"report\"; data: StatusReport }\n  | { type: \"maintenance\"; data: Maintenance }\n);\n\n/**\n * isStatusReport - Type guard for discriminating status reports\n *\n * Type guard function that narrows a UnifiedEvent to a status report event.\n * Used internally by StatusFeed to distinguish between reports and maintenance.\n *\n * @param event - The unified event to check\n * @returns True if the event is a status report\n *\n * @example\n * ```tsx\n * if (isStatusReport(event)) {\n *   // TypeScript knows event.data is StatusReport\n *   console.log(event.data.updates);\n * }\n * ```\n */\nfunction isStatusReport(\n  event: UnifiedEvent,\n): event is UnifiedEvent & { type: \"report\"; data: StatusReport } {\n  return event.type === \"report\";\n}\n\n/**\n * isMaintenance - Type guard for discriminating maintenance events\n *\n * Type guard function that narrows a UnifiedEvent to a maintenance event.\n * Used internally by StatusFeed to distinguish between reports and maintenance.\n *\n * @param event - The unified event to check\n * @returns True if the event is a maintenance\n *\n * @example\n * ```tsx\n * if (isMaintenance(event)) {\n *   // TypeScript knows event.data is Maintenance\n *   console.log(event.data.from, event.data.to);\n * }\n * ```\n */\nfunction isMaintenance(\n  event: UnifiedEvent,\n): event is UnifiedEvent & { type: \"maintenance\"; data: Maintenance } {\n  return event.type === \"maintenance\";\n}\n\n/**\n * StatusFeed - Unified feed of incident reports and maintenance events\n *\n * Displays a chronological feed combining both status reports (incidents) and\n * scheduled maintenance, sorted by date from newest to oldest. The component\n * intelligently merges the two event types and renders them using a discriminated\n * union pattern for type safety.\n *\n * **Key Features**:\n * - **Unified Timeline**: Combines reports and maintenance in chronological order\n * - **Discriminated Union**: Type-safe rendering using TypeScript discriminated unions\n * - **Memoization**: Uses `useMemo` to prevent flicker on re-renders\n * - **Empty State**: Shows a styled empty state when no events are present\n * - **Automatic Rendering**: Each event type is rendered with appropriate components\n *\n * **Data Structure**:\n * - **StatusReport**: Incident reports with updates timeline\n *   - id, title, affected services\n *   - updates array with status, message, date\n * - **Maintenance**: Scheduled maintenance windows\n *   - id, title, message, affected services\n *   - from/to date range\n *\n * The component creates a unified event array internally, using stable keys\n * (`${type}-${id}`) to prevent React flicker when data updates.\n *\n * @param statusReports - Array of incident reports to display\n * @param maintenances - Array of maintenance events to display\n *\n * @example\n * // Feed with both reports and maintenance\n * ```tsx\n * <StatusFeed\n *   statusReports={[\n *     {\n *       id: 1,\n *       title: \"API Outage\",\n *       affected: [\"API\", \"Database\"],\n *       updates: [\n *         {\n *           status: \"resolved\",\n *           message: \"All systems operational\",\n *           date: new Date(\"2024-01-15T12:00:00Z\")\n *         },\n *         {\n *           status: \"investigating\",\n *           message: \"Investigating API timeouts\",\n *           date: new Date(\"2024-01-15T11:00:00Z\")\n *         }\n *       ]\n *     }\n *   ]}\n *   maintenances={[\n *     {\n *       id: 2,\n *       title: \"Database Upgrade\",\n *       message: \"Upgrading to PostgreSQL 15\",\n *       affected: [\"Database\"],\n *       from: new Date(\"2024-01-20T02:00:00Z\"),\n *       to: new Date(\"2024-01-20T04:00:00Z\")\n *     }\n *   ]}\n * />\n * ```\n *\n * @example\n * // Empty state (no events)\n * ```tsx\n * <StatusFeed statusReports={[]} maintenances={[]} />\n * // Displays: \"No recent notifications\" empty state\n * ```\n *\n * @example\n * // Only incident reports\n * ```tsx\n * <StatusFeed\n *   statusReports={incidentReports}\n *   maintenances={[]}\n * />\n * ```\n *\n * @see StatusEventGroup - For the feed container\n * @see StatusEventTimelineReport - For incident rendering\n * @see StatusEventTimelineMaintenance - For maintenance rendering\n * @see isStatusReport - For type guard discrimination\n * @see isMaintenance - For type guard discrimination\n */\nexport function StatusFeed({\n  statusReports = [],\n  maintenances = [],\n  footer,\n  emptyAction,\n  renderReportMessage,\n  renderMaintenanceMessage,\n  renderEvent,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  statusReports?: StatusReport[];\n  maintenances?: Maintenance[];\n  footer?: React.ReactNode;\n  emptyAction?: React.ReactNode;\n  renderReportMessage?: (message: string) => React.ReactNode;\n  renderMaintenanceMessage?: (message: string) => React.ReactNode;\n  renderEvent?: (\n    event:\n      | { type: \"report\"; data: StatusReport }\n      | { type: \"maintenance\"; data: Maintenance },\n    children: React.ReactNode,\n  ) => React.ReactNode;\n}) {\n  const labels = useStatusBlocksLabels();\n  // Memoize the unified events array to prevent flicker on re-renders\n  const unifiedEvents = useMemo<UnifiedEvent[]>(() => {\n    return [\n      ...statusReports.map(\n        (report): UnifiedEvent => ({\n          id: report.id,\n          title: report.title,\n          type: \"report\",\n          startDate:\n            report.updates[report.updates.length - 1]?.date || new Date(),\n          data: report,\n        }),\n      ),\n      ...maintenances.map(\n        (maintenance): UnifiedEvent => ({\n          id: maintenance.id,\n          title: maintenance.title,\n          type: \"maintenance\",\n          startDate: maintenance.from,\n          data: maintenance,\n        }),\n      ),\n    ].sort((a, b) => b.startDate.getTime() - a.startDate.getTime());\n  }, [statusReports, maintenances]);\n\n  if (unifiedEvents.length === 0) {\n    return (\n      <StatusBlankEvents\n        title={labels.noRecentNotifications}\n        description={labels.noRecentNotificationsDescription}\n        action={emptyAction}\n      />\n    );\n  }\n\n  return (\n    <>\n      <StatusEventGroup data-slot=\"status-feed\" {...props}>\n        {unifiedEvents.map((event) => {\n          // Use stable key to prevent flicker\n          const key = `${event.type}-${event.id}`;\n\n          if (isStatusReport(event)) {\n            const report = event.data;\n            const node = (\n              <StatusEventContent>\n                <StatusEventTitle>{report.title}</StatusEventTitle>\n                {report.affected.length > 0 && (\n                  <StatusEventAffected>\n                    {report.affected.map((affected, index) => (\n                      <StatusEventAffectedBadge key={index}>\n                        {affected}\n                      </StatusEventAffectedBadge>\n                    ))}\n                  </StatusEventAffected>\n                )}\n                <StatusEventTimelineReport\n                  updates={report.updates}\n                  renderMessage={renderReportMessage}\n                />\n              </StatusEventContent>\n            );\n            return (\n              <StatusEvent key={key}>\n                <StatusEventAside>\n                  <StatusEventDate date={event.startDate} />\n                </StatusEventAside>\n                {renderEvent\n                  ? renderEvent({ type: \"report\", data: report }, node)\n                  : node}\n              </StatusEvent>\n            );\n          }\n\n          if (isMaintenance(event)) {\n            const maintenance = event.data;\n            const node = (\n              <StatusEventContent>\n                <StatusEventTitle>{maintenance.title}</StatusEventTitle>\n                {maintenance.affected.length > 0 && (\n                  <StatusEventAffected>\n                    {maintenance.affected.map((affected, index) => (\n                      <StatusEventAffectedBadge key={index}>\n                        {affected}\n                      </StatusEventAffectedBadge>\n                    ))}\n                  </StatusEventAffected>\n                )}\n                <StatusEventTimelineMaintenance\n                  maintenance={{\n                    title: maintenance.title,\n                    message: maintenance.message,\n                    from: maintenance.from,\n                    to: maintenance.to,\n                  }}\n                  renderMessage={renderMaintenanceMessage}\n                />\n              </StatusEventContent>\n            );\n            return (\n              <StatusEvent key={key}>\n                <StatusEventAside>\n                  <StatusEventDate date={event.startDate} />\n                </StatusEventAside>\n                {renderEvent\n                  ? renderEvent({ type: \"maintenance\", data: maintenance }, node)\n                  : node}\n              </StatusEvent>\n            );\n          }\n          return null;\n        })}\n      </StatusEventGroup>\n      {footer}\n    </>\n  );\n}\nStatusFeed.displayName = \"StatusFeed\";\n",
      "type": "registry:ui",
      "target": "components/blocks/status-feed.tsx"
    }
  ],
  "type": "registry:block"
}