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:
gorkem-bwl committed 2026-04-26 00:22:14 -04:00
1 parent 25735df0c6
commit 897204ead3
6 files changed
+57 -109

No files matched your search

+2 -3
View File
@@ -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);
+1 -1
View File
@@ -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": {
+37
View File
@@ -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>
);
};