Table.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import { IconDotsVertical, IconDownload, IconRefresh, IconTrash } from "@tabler/icons-react";
  2. import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
  3. import { useMemo } from "react";
  4. import type { Certificate } from "src/api/backend";
  5. import {
  6. CertificateInUseFormatter,
  7. DateFormatter,
  8. DomainsFormatter,
  9. EmptyData,
  10. GravatarFormatter,
  11. HasPermission,
  12. } from "src/components";
  13. import { TableLayout } from "src/components/Table/TableLayout";
  14. import { intl, T } from "src/locale";
  15. import { showCustomCertificateModal, showDNSCertificateModal, showHTTPCertificateModal } from "src/modals";
  16. import { CERTIFICATES, MANAGE } from "src/modules/Permissions";
  17. interface Props {
  18. data: Certificate[];
  19. isFiltered?: boolean;
  20. isFetching?: boolean;
  21. onDelete?: (id: number) => void;
  22. onRenew?: (id: number) => void;
  23. onDownload?: (id: number) => void;
  24. }
  25. export default function Table({ data, isFetching, onDelete, onRenew, onDownload, isFiltered }: Props) {
  26. const columnHelper = createColumnHelper<Certificate>();
  27. const columns = useMemo(
  28. () => [
  29. columnHelper.accessor((row: any) => row.owner, {
  30. id: "owner",
  31. cell: (info: any) => {
  32. const value = info.getValue();
  33. return <GravatarFormatter url={value ? value.avatar : ""} name={value ? value.name : ""} />;
  34. },
  35. meta: {
  36. className: "w-1",
  37. },
  38. }),
  39. columnHelper.accessor((row: any) => row, {
  40. id: "domainNames",
  41. header: intl.formatMessage({ id: "column.name" }),
  42. cell: (info: any) => {
  43. const value = info.getValue();
  44. return (
  45. <DomainsFormatter
  46. domains={value.domainNames}
  47. createdOn={value.createdOn}
  48. niceName={value.niceName}
  49. provider={value.provider || ""}
  50. />
  51. );
  52. },
  53. }),
  54. columnHelper.accessor((row: any) => row, {
  55. id: "provider",
  56. header: intl.formatMessage({ id: "column.provider" }),
  57. cell: (info: any) => {
  58. const r = info.getValue();
  59. if (r.provider === "letsencrypt") {
  60. if (r.meta?.dnsChallenge && r.meta?.dnsProvider) {
  61. return (
  62. <>
  63. <T id="lets-encrypt" /> &ndash; {r.meta?.dnsProvider}
  64. </>
  65. );
  66. }
  67. return <T id="lets-encrypt" />;
  68. }
  69. if (r.provider === "other") {
  70. return <T id="certificates.custom" />;
  71. }
  72. return <T id={r.provider} />;
  73. },
  74. }),
  75. columnHelper.accessor((row: any) => row.expiresOn, {
  76. id: "expiresOn",
  77. header: intl.formatMessage({ id: "column.expires" }),
  78. cell: (info: any) => {
  79. return <DateFormatter value={info.getValue()} highlightPast />;
  80. },
  81. }),
  82. columnHelper.accessor((row: any) => row, {
  83. id: "proxyHosts",
  84. header: intl.formatMessage({ id: "column.status" }),
  85. cell: (info: any) => {
  86. const r = info.getValue();
  87. return (
  88. <CertificateInUseFormatter
  89. proxyHosts={r.proxyHosts}
  90. redirectionHosts={r.redirectionHosts}
  91. deadHosts={r.deadHosts}
  92. streams={r.streams}
  93. />
  94. );
  95. },
  96. }),
  97. columnHelper.display({
  98. id: "id",
  99. cell: (info: any) => {
  100. return (
  101. <span className="dropdown">
  102. <button
  103. type="button"
  104. className="btn dropdown-toggle btn-action btn-sm px-1"
  105. data-bs-boundary="viewport"
  106. data-bs-toggle="dropdown"
  107. >
  108. <IconDotsVertical />
  109. </button>
  110. <div className="dropdown-menu dropdown-menu-end">
  111. <span className="dropdown-header">
  112. <T
  113. id="object.actions-title"
  114. tData={{ object: "certificate" }}
  115. data={{ id: info.row.original.id }}
  116. />
  117. </span>
  118. <a
  119. className="dropdown-item"
  120. href="#"
  121. onClick={(e) => {
  122. e.preventDefault();
  123. onRenew?.(info.row.original.id);
  124. }}
  125. >
  126. <IconRefresh size={16} />
  127. <T id="action.renew" />
  128. </a>
  129. <HasPermission section={CERTIFICATES} permission={MANAGE} hideError>
  130. <a
  131. className="dropdown-item"
  132. href="#"
  133. onClick={(e) => {
  134. e.preventDefault();
  135. onDownload?.(info.row.original.id);
  136. }}
  137. >
  138. <IconDownload size={16} />
  139. <T id="action.download" />
  140. </a>
  141. <div className="dropdown-divider" />
  142. <a
  143. className="dropdown-item"
  144. href="#"
  145. onClick={(e) => {
  146. e.preventDefault();
  147. onDelete?.(info.row.original.id);
  148. }}
  149. >
  150. <IconTrash size={16} />
  151. <T id="action.delete" />
  152. </a>
  153. </HasPermission>
  154. </div>
  155. </span>
  156. );
  157. },
  158. meta: {
  159. className: "text-end w-1",
  160. },
  161. }),
  162. ],
  163. [columnHelper, onDelete, onRenew, onDownload],
  164. );
  165. const tableInstance = useReactTable<Certificate>({
  166. columns,
  167. data,
  168. getCoreRowModel: getCoreRowModel(),
  169. rowCount: data.length,
  170. meta: {
  171. isFetching,
  172. },
  173. enableSortingRemoval: false,
  174. });
  175. const customAddBtn = (
  176. <div className="dropdown">
  177. <button type="button" className="btn dropdown-toggle btn-pink my-3" data-bs-toggle="dropdown">
  178. <T id="object.add" tData={{ object: "certificate" }} />
  179. </button>
  180. <div className="dropdown-menu">
  181. <a
  182. className="dropdown-item"
  183. href="#"
  184. onClick={(e) => {
  185. e.preventDefault();
  186. showHTTPCertificateModal();
  187. }}
  188. >
  189. <T id="lets-encrypt-via-http" />
  190. </a>
  191. <a
  192. className="dropdown-item"
  193. href="#"
  194. onClick={(e) => {
  195. e.preventDefault();
  196. showDNSCertificateModal();
  197. }}
  198. >
  199. <T id="lets-encrypt-via-dns" />
  200. </a>
  201. <div className="dropdown-divider" />
  202. <a
  203. className="dropdown-item"
  204. href="#"
  205. onClick={(e) => {
  206. e.preventDefault();
  207. showCustomCertificateModal();
  208. }}
  209. >
  210. <T id="certificates.custom" />
  211. </a>
  212. </div>
  213. </div>
  214. );
  215. return (
  216. <TableLayout
  217. tableInstance={tableInstance}
  218. emptyState={
  219. <EmptyData
  220. object="certificate"
  221. objects="certificates"
  222. tableInstance={tableInstance}
  223. isFiltered={isFiltered}
  224. color="pink"
  225. customAddBtn={customAddBtn}
  226. permissionSection={CERTIFICATES}
  227. />
  228. }
  229. />
  230. );
  231. }