refactor(client): extract useClientPagination hook and clean up Select prop
- Replace duplicated page/rowsPerPage state in Team, Notifications and Status Pages tables with a shared useClientPagination hook - Drop unused placeholderColor prop from Select; placeholder always uses secondary text - Restore LAYOUT.MD constant for the account menu item gap
This commit is contained in:
1 parent
25735df0c6
commit
897204ead3
6 files changed
+57
-109
No files matched your search
@@ -11,15 +11,14 @@ interface SelectInputProps<T> extends Omit<SelectProps<T>, "label"> {
|
||||
fieldLabel?: string;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
placeholderColor?: string;
|
||||
}
|
||||
|
||||
const SelectInputInner = <T,>(
|
||||
{ fieldLabel, required, placeholder, placeholderColor, ...props }: SelectInputProps<T>,
|
||||
{ fieldLabel, required, placeholder, ...props }: SelectInputProps<T>,
|
||||
ref: React.ForwardedRef<HTMLDivElement>
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
const emptyPlaceholderColor = placeholderColor || theme.palette.text.secondary;
|
||||
const emptyPlaceholderColor = theme.palette.text.secondary;
|
||||
|
||||
const renderValue = (selected: unknown) => {
|
||||
const isMultiple = Boolean((props as { multiple?: boolean }).multiple);
|
||||
|
||||
@@ -65,7 +65,7 @@ export const AuthFooter = ({ collapsed, accountMenuItems }: AuthFooterProps) =>
|
||||
});
|
||||
|
||||
const menuItemSx = {
|
||||
gap: theme.spacing(4),
|
||||
gap: theme.spacing(LAYOUT.MD),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(LAYOUT.XS),
|
||||
"& svg": {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export const useClientPagination = <T>(rows: T[], initialRowsPerPage = 10) => {
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(initialRowsPerPage);
|
||||
|
||||
const handlePageChange = (
|
||||
_e: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleRowsPerPageChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(Number(e.target.value));
|
||||
};
|
||||
|
||||
const pagedRows = rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
||||
|
||||
return {
|
||||
page,
|
||||
rowsPerPage,
|
||||
pagedRows,
|
||||
paginationProps: {
|
||||
component: "div" as const,
|
||||
count: rows.length,
|
||||
page,
|
||||
rowsPerPage,
|
||||
onPageChange: handlePageChange,
|
||||
onRowsPerPageChange: handleRowsPerPageChange,
|
||||
itemsOnPage: pagedRows.length,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,12 +1,11 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useState } from "react";
|
||||
import { Table } from "@/Components/design-elements";
|
||||
import { Pagination } from "@/Components/design-elements/Table";
|
||||
import type { Header } from "@/Components/design-elements/Table";
|
||||
import { useIsAdmin } from "@/Hooks/useIsAdmin";
|
||||
import { useClientPagination } from "@/Hooks/useClientPagination";
|
||||
import type { User } from "@/Types/User";
|
||||
|
||||
interface TeamTableProps {
|
||||
@@ -17,8 +16,7 @@ export const TeamTable = ({ users }: TeamTableProps) => {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = useIsAdmin();
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const { pagedRows, paginationProps } = useClientPagination(users);
|
||||
|
||||
const headers: Header<User>[] = [
|
||||
{
|
||||
@@ -55,40 +53,14 @@ export const TeamTable = ({ users }: TeamTableProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePageChange = (
|
||||
_e: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleRowsPerPageChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(Number(e.target.value));
|
||||
};
|
||||
|
||||
const pagedUsers = users.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<>
|
||||
<Table
|
||||
headers={headers}
|
||||
data={pagedUsers}
|
||||
data={pagedRows}
|
||||
onRowClick={isAdmin ? handleRowClick : undefined}
|
||||
/>
|
||||
{users.length > 0 && (
|
||||
<Pagination
|
||||
component="div"
|
||||
count={users.length}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={handlePageChange}
|
||||
onRowsPerPageChange={handleRowsPerPageChange}
|
||||
itemsOnPage={pagedUsers.length}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{users.length > 0 && <Pagination {...paginationProps} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -1,11 +1,10 @@
|
||||
import { ActionsMenu, type ActionMenuItem } from "@/Components/actions-menu";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import type { Header } from "@/Components/design-elements/Table";
|
||||
import { Table } from "@/Components/design-elements";
|
||||
import { Pagination } from "@/Components/design-elements/Table";
|
||||
import { useClientPagination } from "@/Hooks/useClientPagination";
|
||||
|
||||
import { useState } from "react";
|
||||
import type { Notification } from "@/Types/Notification";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -23,8 +22,7 @@ export const NotificationsTable = ({
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const { pagedRows, paginationProps } = useClientPagination(notifications);
|
||||
|
||||
const getActions = (channel: Notification): ActionMenuItem[] => {
|
||||
return [
|
||||
@@ -87,47 +85,16 @@ export const NotificationsTable = ({
|
||||
return headers;
|
||||
};
|
||||
|
||||
const headers = getHeaders();
|
||||
|
||||
const handlePageChange = (
|
||||
_e: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleRowsPerPageChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(Number(e.target.value));
|
||||
};
|
||||
|
||||
const pagedNotifications = notifications.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<>
|
||||
<Table
|
||||
headers={headers}
|
||||
data={pagedNotifications}
|
||||
headers={getHeaders()}
|
||||
data={pagedRows}
|
||||
onRowClick={(row) => {
|
||||
navigate(`/notifications/configure/${row.id}`);
|
||||
}}
|
||||
/>
|
||||
{notifications.length > 0 && (
|
||||
<Pagination
|
||||
component="div"
|
||||
count={notifications.length}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={handlePageChange}
|
||||
onRowsPerPageChange={handleRowsPerPageChange}
|
||||
itemsOnPage={pagedNotifications.length}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
{notifications.length > 0 && <Pagination {...paginationProps} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -4,9 +4,9 @@ import Typography from "@mui/material/Typography";
|
||||
import { Table, type Header, ValueLabel } from "@/Components/design-elements";
|
||||
import { Pagination } from "@/Components/design-elements/Table";
|
||||
import { ActionsMenu, type ActionMenuItem } from "@/Components/actions-menu";
|
||||
import { useClientPagination } from "@/Hooks/useClientPagination";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -25,8 +25,7 @@ export const StatusPagesTable = ({
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const [page, setPage] = useState(0);
|
||||
const [rowsPerPage, setRowsPerPage] = useState(10);
|
||||
const { pagedRows, paginationProps } = useClientPagination(data);
|
||||
|
||||
const getActions = (row: StatusPage): ActionMenuItem[] => {
|
||||
return [
|
||||
@@ -133,41 +132,15 @@ export const StatusPagesTable = ({
|
||||
navigate(`/status/${statusPage.url}`);
|
||||
};
|
||||
|
||||
const handlePageChange = (
|
||||
_e: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
|
||||
const handleRowsPerPageChange = (
|
||||
e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
|
||||
) => {
|
||||
setPage(0);
|
||||
setRowsPerPage(Number(e.target.value));
|
||||
};
|
||||
|
||||
const pagedData = data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Table
|
||||
headers={getHeaders()}
|
||||
data={pagedData}
|
||||
data={pagedRows}
|
||||
onRowClick={handleRowClick}
|
||||
emptyViewText={t("common.table.empty")}
|
||||
/>
|
||||
{data.length > 0 && (
|
||||
<Pagination
|
||||
component="div"
|
||||
count={data.length}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
onPageChange={handlePageChange}
|
||||
onRowsPerPageChange={handleRowsPerPageChange}
|
||||
itemsOnPage={pagedData.length}
|
||||
/>
|
||||
)}
|
||||
{data.length > 0 && <Pagination {...paginationProps} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user