SearchResults.jsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. "use client";
  2. import React from "react";
  3. import { Loader2 } from "lucide-react";
  4. import ExplorerLoading from "@/components/explorer/states/ExplorerLoading";
  5. import ExplorerEmpty from "@/components/explorer/states/ExplorerEmpty";
  6. import ExplorerError from "@/components/explorer/states/ExplorerError";
  7. import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
  8. import { Button } from "@/components/ui/button";
  9. import {
  10. sortSearchItems,
  11. SEARCH_RESULTS_SORT,
  12. } from "@/lib/frontend/search/resultsSorting";
  13. import SearchResultsToolbar from "@/components/search/SearchResultsToolbar";
  14. import SearchResultsTable from "@/components/search/SearchResultsTable";
  15. export default function SearchResults({
  16. branch,
  17. status,
  18. items,
  19. total,
  20. error,
  21. onRetry,
  22. nextCursor,
  23. onLoadMore,
  24. isLoadingMore,
  25. loadMoreError,
  26. needsBranchSelection = false,
  27. }) {
  28. const [sortMode, setSortMode] = React.useState(SEARCH_RESULTS_SORT.RELEVANCE);
  29. const sortedItems = React.useMemo(() => {
  30. return sortSearchItems(items, sortMode);
  31. }, [items, sortMode]);
  32. if (status === "idle") {
  33. if (needsBranchSelection) {
  34. return (
  35. <ExplorerEmpty
  36. title="Niederlassungen auswählen"
  37. description="Bitte wählen Sie mindestens eine Niederlassung aus, um die Suche zu starten."
  38. upHref={null}
  39. />
  40. );
  41. }
  42. return (
  43. <ExplorerEmpty
  44. title="Suche starten"
  45. description="Bitte geben Sie einen Suchbegriff ein und klicken Sie auf „Suchen“."
  46. upHref={null}
  47. />
  48. );
  49. }
  50. if (status === "loading") {
  51. return <ExplorerLoading variant="table" count={8} />;
  52. }
  53. if (status === "error" && error) {
  54. // Validation errors are rendered in the SearchForm (near the inputs).
  55. // We avoid showing a second alert in the results.
  56. if (error.kind === "validation") {
  57. return (
  58. <ExplorerEmpty
  59. title="Eingaben prüfen"
  60. description="Bitte prüfen Sie Ihre Filter oben."
  61. upHref={null}
  62. />
  63. );
  64. }
  65. return (
  66. <ExplorerError
  67. title={error.title}
  68. description={error.description}
  69. onRetry={onRetry}
  70. />
  71. );
  72. }
  73. const list = Array.isArray(sortedItems) ? sortedItems : [];
  74. if (list.length === 0) {
  75. return (
  76. <ExplorerEmpty
  77. title="Keine Treffer"
  78. description="Für Ihre Suche wurden keine Treffer gefunden."
  79. upHref={null}
  80. />
  81. );
  82. }
  83. return (
  84. <div className="space-y-4">
  85. <SearchResultsToolbar
  86. countLoaded={list.length}
  87. total={total}
  88. sortMode={sortMode}
  89. onSortModeChange={setSortMode}
  90. />
  91. <SearchResultsTable routeBranch={branch} items={list} />
  92. {loadMoreError ? (
  93. <Alert variant="destructive">
  94. <AlertTitle>{loadMoreError.title}</AlertTitle>
  95. <AlertDescription>{loadMoreError.description}</AlertDescription>
  96. </Alert>
  97. ) : null}
  98. {nextCursor ? (
  99. <div className="flex justify-center">
  100. <Button
  101. type="button"
  102. variant="outline"
  103. onClick={onLoadMore}
  104. disabled={isLoadingMore}
  105. title="Weitere Ergebnisse laden"
  106. >
  107. {isLoadingMore ? (
  108. <>
  109. <Loader2 className="h-4 w-4 animate-spin" />
  110. Lädt…
  111. </>
  112. ) : (
  113. "Mehr laden"
  114. )}
  115. </Button>
  116. </div>
  117. ) : null}
  118. </div>
  119. );
  120. }