ExplorerBreadcrumbs.jsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. "use client";
  2. import React from "react";
  3. import Link from "next/link";
  4. import {
  5. Breadcrumb,
  6. BreadcrumbItem,
  7. BreadcrumbLink,
  8. BreadcrumbList,
  9. BreadcrumbPage,
  10. BreadcrumbSeparator,
  11. } from "@/components/ui/breadcrumb";
  12. import { yearPath, monthPath } from "@/lib/frontend/routes";
  13. import { formatMonthLabel } from "@/lib/frontend/explorer/formatters";
  14. import {
  15. buildExplorerDropdownItems,
  16. buildBranchCrumbHref,
  17. } from "@/lib/frontend/explorer/breadcrumbDropdowns";
  18. import SegmentDropdown from "@/components/explorer/breadcrumbs/SegmentDropdown";
  19. /**
  20. * ExplorerBreadcrumbs
  21. *
  22. * Breadcrumb navigation for the Explorer drill-down using shadcn/ui Breadcrumb.
  23. * Optional dropdowns allow switching between existing years/months/days.
  24. *
  25. * UX rules:
  26. * - All user-facing strings must be German.
  27. * - Dropdowns are only rendered when options are available (non-empty lists).
  28. *
  29. * @param {{
  30. * branch: string,
  31. * year?: string|null,
  32. * month?: string|null,
  33. * day?: string|null,
  34. * yearOptions?: string[]|null,
  35. * monthOptions?: string[]|null,
  36. * dayOptions?: string[]|null
  37. * }} props
  38. */
  39. export default function ExplorerBreadcrumbs({
  40. branch,
  41. year = null,
  42. month = null,
  43. day = null,
  44. yearOptions = null,
  45. monthOptions = null,
  46. dayOptions = null,
  47. }) {
  48. const { yearItems, monthItems, dayItems } = buildExplorerDropdownItems({
  49. branch,
  50. year,
  51. month,
  52. day,
  53. yearOptions,
  54. monthOptions,
  55. dayOptions,
  56. });
  57. const showYear = Boolean(year);
  58. const showMonth = Boolean(year && month);
  59. const showDay = Boolean(year && month && day);
  60. const isBranchCurrent = !year;
  61. const isYearCurrent = Boolean(year && !month);
  62. const isMonthCurrent = Boolean(year && month && !day);
  63. return (
  64. <Breadcrumb>
  65. <BreadcrumbList>
  66. {/* Branch */}
  67. <BreadcrumbItem>
  68. {isBranchCurrent ? (
  69. <BreadcrumbPage>{branch}</BreadcrumbPage>
  70. ) : (
  71. <BreadcrumbLink asChild>
  72. <Link href={buildBranchCrumbHref(branch)}>{branch}</Link>
  73. </BreadcrumbLink>
  74. )}
  75. </BreadcrumbItem>
  76. {/* Year */}
  77. {showYear ? (
  78. <>
  79. <BreadcrumbSeparator />
  80. <BreadcrumbItem>
  81. <div className="flex items-center">
  82. {isYearCurrent ? (
  83. <BreadcrumbPage>{year}</BreadcrumbPage>
  84. ) : (
  85. <BreadcrumbLink asChild>
  86. <Link href={yearPath(branch, year)}>{year}</Link>
  87. </BreadcrumbLink>
  88. )}
  89. {yearItems ? (
  90. <SegmentDropdown
  91. items={yearItems}
  92. currentValue={year}
  93. menuLabel="Jahr wechseln"
  94. triggerAriaLabel="Jahr auswählen"
  95. />
  96. ) : null}
  97. </div>
  98. </BreadcrumbItem>
  99. </>
  100. ) : null}
  101. {/* Month */}
  102. {showMonth ? (
  103. <>
  104. <BreadcrumbSeparator />
  105. <BreadcrumbItem>
  106. <div className="flex items-center">
  107. {isMonthCurrent ? (
  108. <BreadcrumbPage>{formatMonthLabel(month)}</BreadcrumbPage>
  109. ) : (
  110. <BreadcrumbLink asChild>
  111. <Link href={monthPath(branch, year, month)}>
  112. {formatMonthLabel(month)}
  113. </Link>
  114. </BreadcrumbLink>
  115. )}
  116. {monthItems ? (
  117. <SegmentDropdown
  118. items={monthItems}
  119. currentValue={month}
  120. menuLabel="Monat wechseln"
  121. triggerAriaLabel="Monat auswählen"
  122. />
  123. ) : null}
  124. </div>
  125. </BreadcrumbItem>
  126. </>
  127. ) : null}
  128. {/* Day */}
  129. {showDay ? (
  130. <>
  131. <BreadcrumbSeparator />
  132. <BreadcrumbItem>
  133. <div className="flex items-center">
  134. {/* Day is always current on the leaf route */}
  135. <BreadcrumbPage>{day}</BreadcrumbPage>
  136. {dayItems ? (
  137. <SegmentDropdown
  138. items={dayItems}
  139. currentValue={day}
  140. menuLabel="Tag wechseln"
  141. triggerAriaLabel="Tag auswählen"
  142. />
  143. ) : null}
  144. </div>
  145. </BreadcrumbItem>
  146. </>
  147. ) : null}
  148. </BreadcrumbList>
  149. </Breadcrumb>
  150. );
  151. }