{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "status-utils",
  "title": "Status Utilities",
  "description": "Utility functions for status formatting and display",
  "dependencies": [
    "date-fns"
  ],
  "files": [
    {
      "path": "src/components/blocks/status.utils.ts",
      "content": "import { UTCDate } from \"@date-fns/utc\";\nimport type { StatusBlocksLabels } from \"@/components/blocks/status-i18n\";\nimport { endOfDay, isSameDay, startOfDay } from \"date-fns\";\n\n/**\n * Formats a date range in a human-readable format.\n *\n * NOTE: While Intl.DateTimeFormat.formatRange() is available in modern browsers,\n * we use a custom implementation to have fine-grained control over the output format\n * and to handle edge cases like \"Since\", \"Until\", and \"All time\" consistently.\n * See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/formatRange\n *\n * @param from - Start date of the range (optional)\n * @param to - End date of the range (optional)\n * @param locale - Locale string for formatting (default: \"en-US\")\n * @returns A formatted date range string\n *\n * @example\n * formatDateRange(new Date('2024-01-01'), new Date('2024-01-05'))\n * // => \"January 1, 2024 - January 5, 2024\"\n *\n * @example\n * formatDateRange(new Date('2024-01-01 10:00'), new Date('2024-01-01 15:00'))\n * // => \"January 1, 10:00 AM - 3:00 PM\"\n */\nexport function formatDateRange(from?: Date, to?: Date) {\n  const sameDay = from && to && isSameDay(new UTCDate(from), new UTCDate(to));\n  const isFromStartDay =\n    from && startOfDay(new UTCDate(from)).getTime() === from.getTime();\n  const isToEndDay = to && endOfDay(new UTCDate(to)).getTime() === to.getTime();\n\n  if (sameDay) {\n    if (from && to && from.getTime() === to.getTime()) {\n      return formatDateTime(from);\n    }\n    if (from && to) {\n      return `${formatDateTime(from)} - ${formatTime(to)}`;\n    }\n  }\n\n  if (from && to) {\n    if (isFromStartDay && isToEndDay) {\n      return `${formatDate(from)} - ${formatDate(to)}`;\n    }\n    return `${formatDateTime(from)} - ${formatDateTime(to)}`;\n  }\n\n  if (to) {\n    return `Until ${formatDateTime(to)}`;\n  }\n\n  if (from) {\n    return `Since ${formatDateTime(from)}`;\n  }\n\n  return \"All time\";\n}\n\n/**\n * Formats a date with locale support.\n *\n * @param date - The date to format\n * @param options - Intl.DateTimeFormatOptions to customize the output\n * @param locale - Locale string for formatting (default: \"en-US\")\n * @returns A formatted date string\n */\nexport function formatDate(\n  date: Date,\n  options?: Intl.DateTimeFormatOptions,\n  locale = \"en-US\",\n) {\n  return date.toLocaleDateString(locale, {\n    year: \"numeric\",\n    month: \"long\",\n    day: \"numeric\",\n    ...options,\n    // last so callers can't override away from UTC (the \"(UTC)\" label depends on it)\n    timeZone: \"UTC\",\n  });\n}\n\n/**\n * Formats a date with abbreviated month (e.g. \"Jan 15, 2024\").\n *\n * @param date - The date to format\n * @param locale - Locale string for formatting (default: \"en-US\")\n * @returns A formatted date string with abbreviated month\n */\nexport function formatDateShort(date: Date, locale = \"en-US\") {\n  return date.toLocaleDateString(locale, {\n    year: \"numeric\",\n    month: \"short\",\n    day: \"numeric\",\n    timeZone: \"UTC\",\n  });\n}\n\n/**\n * Formats a date with time, with locale support.\n *\n * @param date - The date to format\n * @param locale - Locale string for formatting (default: \"en-US\")\n * @returns A formatted date and time string\n */\nexport function formatDateTime(date: Date, locale = \"en-US\") {\n  return date.toLocaleDateString(locale, {\n    month: \"long\",\n    day: \"numeric\",\n    hour: \"numeric\",\n    minute: \"numeric\",\n    timeZone: \"UTC\",\n  });\n}\n\n/**\n * Formats a time with locale support.\n *\n * @param date - The date to format\n * @param locale - Locale string for formatting (default: \"en-US\")\n * @returns A formatted time string\n */\nexport function formatTime(date: Date, locale = \"en-US\") {\n  return date.toLocaleTimeString(locale, {\n    hour: \"numeric\",\n    minute: \"numeric\",\n    timeZone: \"UTC\",\n  });\n}\n\n/**\n * Returns the start/end of a closed range as separate strings, collapsing the\n * `to` side to a time-only render when `from` and `to` fall on the same UTC day.\n * Use `formatDateRange` for open-ended cases (`Until …` / `Since …`).\n */\nexport function formatDateRangeParts(\n  from: Date,\n  to: Date,\n): { from: string; to: string } {\n  if (isSameDay(new UTCDate(from), new UTCDate(to))) {\n    return { from: formatDateTime(from), to: formatTime(to) };\n  }\n  const isFromStartDay =\n    startOfDay(new UTCDate(from)).getTime() === from.getTime();\n  const isToEndDay = endOfDay(new UTCDate(to)).getTime() === to.getTime();\n  if (isFromStartDay && isToEndDay) {\n    return { from: formatDate(from), to: formatDate(to) };\n  }\n  return { from: formatDateTime(from), to: formatDateTime(to) };\n}\n\nimport type {\n  StatusReportImpact,\n  StatusReportUpdateType,\n  StatusType,\n} from \"@/components/blocks/status.types\";\n\n/**\n * System status display messages\n * Used for displaying status banner and component statuses\n */\nexport const systemStatusLabels: Record<\n  StatusType,\n  { long: string; short: string }\n> = {\n  success: {\n    long: \"All Systems Operational\",\n    short: \"Operational\",\n  },\n  degraded: {\n    long: \"Degraded Performance\",\n    short: \"Degraded\",\n  },\n  error: {\n    long: \"Partial Outage\",\n    short: \"Outage\",\n  },\n  info: {\n    long: \"Maintenance\",\n    short: \"Maintenance\",\n  },\n  empty: {\n    long: \"No Data\",\n    short: \"No Data\",\n  },\n} as const;\n\n/**\n * Legacy messages object for backwards compatibility\n * @deprecated Use systemStatusLabels instead\n */\nexport const messages = {\n  long: {\n    success: systemStatusLabels.success.long,\n    degraded: systemStatusLabels.degraded.long,\n    error: systemStatusLabels.error.long,\n    info: systemStatusLabels.info.long,\n    empty: systemStatusLabels.empty.long,\n  },\n  short: {\n    success: systemStatusLabels.success.short,\n    degraded: systemStatusLabels.degraded.short,\n    error: systemStatusLabels.error.short,\n    info: systemStatusLabels.info.short,\n    empty: systemStatusLabels.empty.short,\n  },\n} as const;\n\n/**\n * Request status labels\n * Used for displaying individual request statuses\n */\nexport const requestStatusLabels: Record<StatusType, string> = {\n  success: \"Normal\",\n  degraded: \"Degraded\",\n  error: \"Error\",\n  info: \"Maintenance\",\n  empty: \"No Data\",\n} as const;\n\n/**\n * Legacy requests object for backwards compatibility\n * @deprecated Use requestStatusLabels instead\n */\nexport const requests = requestStatusLabels;\n\n/**\n * Component impact labels\n * Used for displaying per-component report impacts\n */\nexport const componentImpactLabels: Record<StatusReportImpact, string> = {\n  operational: \"Operational\",\n  degraded_performance: \"Degraded performance\",\n  partial_outage: \"Partial outage\",\n  major_outage: \"Major outage\",\n} as const;\n\n// ordered worst-last so worstImpact can compare by index (mirrors the db's `pageComponentImpact`)\nexport const statusReportImpacts: readonly StatusReportImpact[] = [\n  \"operational\",\n  \"degraded_performance\",\n  \"partial_outage\",\n  \"major_outage\",\n] as const;\n\nexport function worstStatusReportImpact(\n  impacts: Iterable<StatusReportImpact>,\n): StatusReportImpact {\n  let worst: StatusReportImpact = \"operational\";\n  for (const impact of impacts) {\n    if (\n      statusReportImpacts.indexOf(impact) > statusReportImpacts.indexOf(worst)\n    ) {\n      worst = impact;\n    }\n  }\n  return worst;\n}\n\n/**\n * Incident status labels\n * Used for displaying incident report update statuses\n */\nexport const incidentStatusLabels: Record<StatusReportUpdateType, string> = {\n  resolved: \"Resolved\",\n  monitoring: \"Monitoring\",\n  identified: \"Identified\",\n  investigating: \"Investigating\",\n} as const;\n\n/**\n * Legacy status object for backwards compatibility\n * @deprecated Use incidentStatusLabels instead\n */\nexport const status = incidentStatusLabels;\n\n/**\n * CSS variable mappings for status colors\n * Maps StatusType to corresponding CSS custom properties\n */\nexport const statusColors: Record<StatusType, string> = {\n  success: \"var(--success)\",\n  degraded: \"var(--warning)\",\n  error: \"var(--destructive)\",\n  info: \"var(--info)\",\n  empty: \"var(--muted)\",\n} as const;\n\n/**\n * Legacy colors object for backwards compatibility\n * @deprecated Use statusColors instead\n */\nexport const colors = statusColors;\n\n// Timestamps render in UTC; the suffix tells viewers which zone.\nconst withUTC = (value: string) => `${value} (UTC)`;\n\n/**\n * Default labels consumed by blocks when no StatusBlocksI18nProvider is mounted.\n * Registry/web preview render with this set; the status-page app overrides via the provider.\n */\nexport const defaultStatusBlocksLabels = {\n  systemStatus: systemStatusLabels,\n  incidentStatus: incidentStatusLabels,\n  requestStatus: requestStatusLabels,\n  componentImpact: componentImpactLabels,\n\n  today: \"Today\",\n  ongoing: \"Ongoing\",\n  reportResolved: \"Report resolved\",\n  noRecentNotifications: \"No recent notifications\",\n  noRecentNotificationsDescription:\n    \"There have been no reports within the last 7 days.\",\n  noReports: \"No reports\",\n  noReportsDescription: \"There are no reports to display.\",\n  noPublicMonitors: \"No public monitors\",\n  noPublicMonitorsDescription: \"There are no public monitors to display.\",\n\n  themeNames: {\n    light: \"Light\",\n    dark: \"Dark\",\n    system: \"System\",\n  },\n  ariaToggleTheme: \"Toggle theme\",\n\n  subscribe: \"Get updates\",\n  subscribeRssDescription: \"Get the RSS feed\",\n  subscribeAtomDescription: \"Get the Atom feed\",\n  subscribeJsonDescription: \"Get the JSON updates\",\n  subscribeSlackDescription:\n    \"For status updates in Slack, paste the text below into any channel.\",\n  subscribeSshDescription: \"Get status via SSH\",\n  linkCopiedToClipboard: \"Link copied to clipboard\",\n  ariaCopyLink: \"Copy Link\",\n\n  poweredBy: \"powered by\",\n  getInTouch: \"Get in touch\",\n\n  ariaStatusTracker: \"Status tracker\",\n  ariaDayStatus: (n: number) => `Day ${n} status`,\n  clickAgainToUnpin: \"Click again to unpin\",\n\n  calendarTitle: \"Calendar\",\n\n  durationIn: (s: string) => `(in ${s})`,\n  durationEarlier: (s: string) => `(${s} earlier)`,\n  durationFor: (s: string) => `(for ${s})`,\n  durationAcross: (s: string) => `across ${s}`,\n\n  formatDate: (d: Date) => withUTC(formatDate(d)),\n  formatDateShort: (d: Date) => formatDateShort(d),\n  formatDateTime: (d: Date) => withUTC(formatDateTime(d)),\n  formatDateRange: (from?: Date, to?: Date) => {\n    const range = formatDateRange(from, to);\n    return from || to ? withUTC(range) : range;\n  },\n  formatDateRangeParts: (from: Date, to: Date) => {\n    const { from: start, to: end } = formatDateRangeParts(from, to);\n    return { from: start, to: withUTC(end) };\n  },\n} as const satisfies StatusBlocksLabels;\n",
      "type": "registry:lib",
      "target": "components/blocks/status.utils.ts"
    }
  ],
  "cssVars": {
    "theme": {
      "color-success": "var(--success)",
      "color-warning": "var(--warning)",
      "color-info": "var(--info)"
    },
    "light": {
      "success": "oklch(0.72 0.19 150)",
      "warning": "oklch(0.77 0.16 70)",
      "info": "oklch(0.62 0.19 260)"
    },
    "dark": {
      "success": "oklch(0.72 0.19 150)",
      "warning": "oklch(0.77 0.16 70)",
      "info": "oklch(0.62 0.19 260)"
    }
  },
  "type": "registry:lib"
}