From 897204ead393ccc97d2b46e51bf538fde0e11d24 Mon Sep 17 00:00:00 2001 From: gorkem-bwl Date: Sun, 26 Apr 2026 00:22:14 -0400 Subject: [PATCH] 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 --- client/src/Components/inputs/Select.tsx | 5 +- client/src/Components/sidebar/Authfooter.tsx | 2 +- client/src/Hooks/useClientPagination.ts | 37 +++++++++++++++ .../Pages/Account/components/TeamTable.tsx | 40 +++------------- .../components/NotificationsTable.tsx | 47 +++---------------- .../Components/StatusPagesTable.tsx | 35 ++------------ 6 files changed, 57 insertions(+), 109 deletions(-) create mode 100644 client/src/Hooks/useClientPagination.ts diff --git a/client/src/Components/inputs/Select.tsx b/client/src/Components/inputs/Select.tsx index f4f0ae392..92a57860a 100644 --- a/client/src/Components/inputs/Select.tsx +++ b/client/src/Components/inputs/Select.tsx @@ -11,15 +11,14 @@ interface SelectInputProps extends Omit, "label"> { fieldLabel?: string; required?: boolean; placeholder?: string; - placeholderColor?: string; } const SelectInputInner = ( - { fieldLabel, required, placeholder, placeholderColor, ...props }: SelectInputProps, + { fieldLabel, required, placeholder, ...props }: SelectInputProps, ref: React.ForwardedRef ) => { 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); diff --git a/client/src/Components/sidebar/Authfooter.tsx b/client/src/Components/sidebar/Authfooter.tsx index 9a22f3f33..34f76d7ce 100644 --- a/client/src/Components/sidebar/Authfooter.tsx +++ b/client/src/Components/sidebar/Authfooter.tsx @@ -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": { diff --git a/client/src/Hooks/useClientPagination.ts b/client/src/Hooks/useClientPagination.ts new file mode 100644 index 000000000..93bb5256a --- /dev/null +++ b/client/src/Hooks/useClientPagination.ts @@ -0,0 +1,37 @@ +import { useState } from "react"; + +export const useClientPagination = (rows: T[], initialRowsPerPage = 10) => { + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(initialRowsPerPage); + + const handlePageChange = ( + _e: React.MouseEvent | null, + newPage: number + ) => { + setPage(newPage); + }; + + const handleRowsPerPageChange = ( + e: React.ChangeEvent + ) => { + 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, + }, + }; +}; diff --git a/client/src/Pages/Account/components/TeamTable.tsx b/client/src/Pages/Account/components/TeamTable.tsx index 10b16515b..bceca2313 100644 --- a/client/src/Pages/Account/components/TeamTable.tsx +++ b/client/src/Pages/Account/components/TeamTable.tsx @@ -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[] = [ { @@ -55,40 +53,14 @@ export const TeamTable = ({ users }: TeamTableProps) => { } }; - const handlePageChange = ( - _e: React.MouseEvent | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - e: React.ChangeEvent - ) => { - setPage(0); - setRowsPerPage(Number(e.target.value)); - }; - - const pagedUsers = users.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); - return ( - + <> - {users.length > 0 && ( - - )} - + {users.length > 0 && } + ); }; diff --git a/client/src/Pages/Notifications/components/NotificationsTable.tsx b/client/src/Pages/Notifications/components/NotificationsTable.tsx index 4a42a597d..a1c8d8921 100644 --- a/client/src/Pages/Notifications/components/NotificationsTable.tsx +++ b/client/src/Pages/Notifications/components/NotificationsTable.tsx @@ -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 | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - e: React.ChangeEvent - ) => { - setPage(0); - setRowsPerPage(Number(e.target.value)); - }; - - const pagedNotifications = notifications.slice( - page * rowsPerPage, - page * rowsPerPage + rowsPerPage - ); - return ( - + <>
{ navigate(`/notifications/configure/${row.id}`); }} /> - {notifications.length > 0 && ( - - )} - + {notifications.length > 0 && } + ); }; diff --git a/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx b/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx index 56b1d4411..9c9938c04 100644 --- a/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx +++ b/client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable.tsx @@ -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 | null, - newPage: number - ) => { - setPage(newPage); - }; - - const handleRowsPerPageChange = ( - e: React.ChangeEvent - ) => { - setPage(0); - setRowsPerPage(Number(e.target.value)); - }; - - const pagedData = data.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); - return (
- {data.length > 0 && ( - - )} + {data.length > 0 && } ); };