diff --git a/src/common/RichTable/RichTable.js b/src/common/RichTable/RichTable.js
new file mode 100644
index 0000000..258ef87
--- /dev/null
+++ b/src/common/RichTable/RichTable.js
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React, { useMemo, useState } from "react";
+
+import {
+ flexRender,
+ getCoreRowModel,
+ getSortedRowModel,
+ getPaginationRowModel,
+ useReactTable,
+} from "@tanstack/react-table";
+
+import RichTableBody from "./RichTableBody";
+import RichTableHeader from "./RichTableHeader";
+import RichTablePagination from "./RichTablePagination";
+
+const fallbackData = [];
+
+const RichTable = ({
+ columns,
+ data,
+ withPagination,
+ pageSize = 5,
+ pageIndex = 0,
+}) => {
+ const tableColumns = useMemo(() => columns, []);
+ const [tableData, _] = useState(data ?? fallbackData);
+ const [sorting, setSorting] = useState([]);
+ const [pagination, setPagination] = useState({
+ pageIndex,
+ pageSize,
+ });
+
+ const table = useReactTable({
+ data: tableData,
+ columns: tableColumns,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ onPaginationChange: setPagination,
+ onSortingChange: setSorting,
+ state: {
+ sorting,
+ pagination,
+ },
+ });
+
+ return (
+
+
+ {withPagination && (
+
+ )}
+
+ );
+};
+
+export default RichTable;
diff --git a/src/common/RichTable/RichTableBody.js b/src/common/RichTable/RichTableBody.js
new file mode 100644
index 0000000..97f6f48
--- /dev/null
+++ b/src/common/RichTable/RichTableBody.js
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+
+const RichTableBody = ({ table, flexRender }) => {
+ return (
+
+ {table.getRowModel().rows.map((row) => {
+ return (
+
+ {row.getVisibleCells().map((cell) => {
+ return (
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+ |
+ );
+ })}
+
+ );
+ })}
+
+ );
+};
+
+export default RichTableBody;
diff --git a/src/common/RichTable/RichTableHeader.js b/src/common/RichTable/RichTableHeader.js
new file mode 100644
index 0000000..feb204e
--- /dev/null
+++ b/src/common/RichTable/RichTableHeader.js
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import {
+ faSquareCaretUp,
+ faSquareCaretDown,
+} from "@fortawesome/free-solid-svg-icons";
+
+const RichTableHeader = ({ table, flexRender }) => {
+ const getThTitle = (header) =>
+ header.column.getCanSort()
+ ? header.column.getNextSortingOrder() === "asc"
+ ? _("Sort ascending")
+ : header.column.getNextSortingOrder() === "desc"
+ ? _("Sort descending")
+ : _("Clear sort")
+ : undefined;
+
+ return (
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => (
+
+ {header.isPlaceholder ||
+ header.column.columnDef.headerIsHidden ? (
+
+ {flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ ) : (
+
+ )}
+ |
+ ))}
+
+ ))}
+
+ );
+};
+
+export default RichTableHeader;
diff --git a/src/common/RichTable/RichTablePagination.js b/src/common/RichTable/RichTablePagination.js
new file mode 100644
index 0000000..c0a5ace
--- /dev/null
+++ b/src/common/RichTable/RichTablePagination.js
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2019-2024 CZ.NIC z.s.p.o. (https://www.nic.cz/)
+ *
+ * This is free software, licensed under the GNU General Public License v3.
+ * See /LICENSE for more information.
+ */
+
+import React, { useMemo } from "react";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import {
+ faAngleLeft,
+ faAnglesLeft,
+ faAngleRight,
+ faAnglesRight,
+} from "@fortawesome/free-solid-svg-icons";
+
+const RichTablePagination = ({ table, tablePageSize, allRows }) => {
+ const { pagination } = table.getState();
+ const prevPagBtnDisabled = !table.getCanPreviousPage();
+ const nextPagBtnDisabled = !table.getCanNextPage();
+
+ const pageSizes = useMemo(() => {
+ return [tablePageSize ?? 5, 10, 25].filter(
+ (value, index, self) => self.indexOf(value) === index
+ );
+ }, [tablePageSize]);
+
+ const renderPaginationButton = (icon, ariaLabel, onClick, disabled) => (
+
+
+
+ );
+
+ return (
+
+ );
+};
+
+export default RichTablePagination;
diff --git a/src/index.js b/src/index.js
index 4e7839f..8052fb1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -43,6 +43,7 @@ export { Modal, ModalBody, ModalFooter, ModalHeader } from "./bootstrap/Modal";
export { default as RebootButton } from "./common/RebootButton";
export { default as WiFiSettings } from "./common/WiFiSettings/WiFiSettings";
export { default as ResetWiFiSettings } from "./common/WiFiSettings/ResetWiFiSettings";
+export { default as RichTable } from "./common/RichTable/RichTable";
// Form
export { default as ForisForm } from "./form/components/ForisForm";
export {