SearchResults.jsx 3.1 KB

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