feat(i18n): add new translations for refresh, view, toggle columns, and no results

refactor(payment): remove payment API client and related tests, consolidating payment logic

refactor(payment): delete payment modal and associated constants, hooks, and types

fix(ui): enhance AdvancedTable with refresh button and column toggle functionality

fix(ui): implement pagination in useServerTable for better data handling
This commit is contained in:
estifanos
2026-08-03 09:22:02 +00:00
parent 52e2f4c009
commit 599dc4aa83
19 changed files with 452 additions and 1395 deletions

View File

@@ -10,7 +10,6 @@ import {
Loader,
Center,
Paper,
Badge,
} from "@mantine/core";
import { IconRefresh, IconAdjustmentsHorizontal , IconInbox, } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
@@ -24,6 +23,8 @@ export interface AdvancedColumn<T> {
align?: "left" | "center" | "right";
/** Whether column starts visible. Default true. */
enabled?: boolean;
/** Label for the View menu; falls back to `header` when it is a plain string. */
label?: string;
}
interface AdvancedTableProps<T> {
@@ -90,23 +91,23 @@ export function AdvancedTable<T extends { id?: string | number }>({
<Text fw={600}>{""}</Text>
</Group>
<Group gap="xs">
{refresh && (
<Button
variant="default"
size="sm"
leftSection={<IconRefresh size={16} />}
onClick={refresh}
loading={isLoading}
>
{t("common.refresh", "Refresh")}
</Button>
)}
<Menu
closeOnItemClick={false}
shadow="md"
position="bottom-end"
width={220}
>
{refresh && (
<Button
variant="default"
size="sm"
leftSection={<IconRefresh size={16} />}
onClick={refresh}
loading={isLoading}
>
{t("common.refresh", "Refresh")}
</Button>
)}
<Menu.Target>
<Button
variant="default"
@@ -120,24 +121,31 @@ export function AdvancedTable<T extends { id?: string | number }>({
<Menu.Label>
{t("common.toggleColumns", "Toggle columns")}
</Menu.Label>
{columns.map((col, i) => (
<Menu.Item key={i} onClick={() => toggleColumn(i)}>
<Checkbox
label={col.header}
checked={visible[i] ?? true}
disabled={
visible.filter(Boolean).length === 1 &&
(visible[i] ?? true)
}
readOnly
tabIndex={-1}
styles={{
input: { cursor: "pointer" },
label: { cursor: "pointer" },
}}
/>
</Menu.Item>
))}
{columns.map((col, i) => {
// A ReactNode header (e.g. a live checkbox or a clickable sort
// control) can't be reused as a menu-item label — skip it
// rather than nesting interactive markup inside the label.
const label = col.label ?? (typeof col.header === "string" ? col.header : null);
if (label === null) return null;
return (
<Menu.Item key={i} onClick={() => toggleColumn(i)}>
<Checkbox
label={label}
checked={visible[i] ?? true}
disabled={
visible.filter(Boolean).length === 1 &&
(visible[i] ?? true)
}
readOnly
tabIndex={-1}
styles={{
input: { cursor: "pointer" },
label: { cursor: "pointer" },
}}
/>
</Menu.Item>
);
})}
</Menu.Dropdown>
</Menu>
</Group>

View File

@@ -17,6 +17,22 @@ export function useServerTable({ pageSize = 10 }: UseServerTableOptions = {}) {
setPageIndex(0);
}, []);
// Slice an already-fetched array for AdvancedTable when the endpoint has no
// skip/take of its own. Clamps pageIndex so deleting the last row of the
// last page doesn't strand the table on an empty slice.
const paginate = useCallback(
<T,>(rows: T[]) => {
const lastPage = Math.max(0, Math.ceil(rows.length / pageSize) - 1);
const clamped = Math.min(pageIndex, lastPage);
return {
rows: rows.slice(clamped * pageSize, clamped * pageSize + pageSize),
pageIndex: clamped,
itemCount: rows.length,
};
},
[pageIndex, pageSize],
);
return {
pageIndex,
setPageIndex,
@@ -25,5 +41,6 @@ export function useServerTable({ pageSize = 10 }: UseServerTableOptions = {}) {
pageSize,
skip: pageIndex * pageSize,
take: pageSize,
paginate,
};
}