SearchResultsTable.jsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. "use client";
  2. import React from "react";
  3. import Link from "next/link";
  4. import { Eye, FolderOpen } from "lucide-react";
  5. import { dayPath } from "@/lib/frontend/routes";
  6. import { buildPdfUrl } from "@/lib/frontend/explorer/pdfUrl";
  7. import { formatSearchItemDateDe } from "@/lib/frontend/search/resultsSorting";
  8. import { SEARCH_SCOPE } from "@/lib/frontend/search/urlState";
  9. import { Button } from "@/components/ui/button";
  10. import {
  11. Table,
  12. TableBody,
  13. TableCaption,
  14. TableCell,
  15. TableHead,
  16. TableHeader,
  17. TableRow,
  18. } from "@/components/ui/table";
  19. export default function SearchResultsTable({ routeBranch, scope, items }) {
  20. const showBranchColumn =
  21. scope === SEARCH_SCOPE.ALL || scope === SEARCH_SCOPE.MULTI;
  22. const list = Array.isArray(items) ? items : [];
  23. return (
  24. <Table>
  25. <TableCaption>
  26. Hinweis: PDFs werden in einem neuen Tab geöffnet.
  27. </TableCaption>
  28. <TableHeader>
  29. <TableRow>
  30. {showBranchColumn ? <TableHead>Niederlassung</TableHead> : null}
  31. <TableHead>Datum</TableHead>
  32. <TableHead>Datei</TableHead>
  33. <TableHead className="hidden md:table-cell">Pfad</TableHead>
  34. <TableHead className="text-right">Aktion</TableHead>
  35. </TableRow>
  36. </TableHeader>
  37. <TableBody>
  38. {list.map((it) => {
  39. const itemBranch = String(it?.branch || routeBranch);
  40. const year = String(it?.year || "");
  41. const month = String(it?.month || "");
  42. const day = String(it?.day || "");
  43. const filename = String(it?.filename || "");
  44. const relativePath = String(it?.relativePath || "");
  45. const snippet =
  46. typeof it?.snippet === "string" && it.snippet.trim()
  47. ? it.snippet.trim()
  48. : null;
  49. const pdfUrl = buildPdfUrl({
  50. branch: itemBranch,
  51. year,
  52. month,
  53. day,
  54. filename,
  55. });
  56. const dayHref = dayPath(itemBranch, year, month, day);
  57. const key =
  58. relativePath || `${itemBranch}/${year}/${month}/${day}/${filename}`;
  59. return (
  60. <TableRow key={key}>
  61. {showBranchColumn ? (
  62. <TableCell>
  63. <span className="text-sm">{itemBranch}</span>
  64. </TableCell>
  65. ) : null}
  66. <TableCell>
  67. <span className="text-sm">{formatSearchItemDateDe(it)}</span>
  68. </TableCell>
  69. <TableCell className="min-w-0">
  70. <div className="min-w-0">
  71. <p className="truncate font-medium">{filename}</p>
  72. {snippet ? (
  73. <p className="mt-0.5 line-clamp-2 text-xs text-muted-foreground">
  74. {snippet}
  75. </p>
  76. ) : null}
  77. <p className="truncate text-xs text-muted-foreground md:hidden">
  78. {relativePath}
  79. </p>
  80. </div>
  81. </TableCell>
  82. <TableCell className="hidden md:table-cell">
  83. <span className="text-xs text-muted-foreground">
  84. {relativePath}
  85. </span>
  86. </TableCell>
  87. <TableCell className="text-right">
  88. <div className="flex justify-end gap-2">
  89. <Button variant="outline" size="sm" asChild>
  90. <a
  91. href={pdfUrl}
  92. target="_blank"
  93. rel="noopener noreferrer"
  94. aria-label={`PDF öffnen: ${filename}`}
  95. title={`PDF öffnen: ${filename}`}
  96. >
  97. <Eye className="h-4 w-4" />
  98. Öffnen
  99. </a>
  100. </Button>
  101. <Button variant="outline" size="sm" asChild>
  102. <Link href={dayHref} title="Zum Tag">
  103. <FolderOpen className="h-4 w-4" />
  104. Zum Tag
  105. </Link>
  106. </Button>
  107. </div>
  108. </TableCell>
  109. </TableRow>
  110. );
  111. })}
  112. </TableBody>
  113. </Table>
  114. );
  115. }