feat: replace Table with AdvancedTable for improved license type management in PaymentConfigPage

This commit is contained in:
estifanos
2026-08-12 07:35:43 +00:00
parent 0633ecc165
commit bbb23f3bbf

View File

@@ -3,7 +3,6 @@ import {
Alert,
Badge,
Button,
Card,
Center,
Group,
Loader,
@@ -12,7 +11,6 @@ import {
Paper,
Stack,
Switch,
Table,
Text,
TextInput,
ThemeIcon,
@@ -26,7 +24,13 @@ import {
IconInfoCircle,
IconLock,
} from '@tabler/icons-react';
import { notify, ModalFooter } from '@ema-platform/ui';
import {
notify,
ModalFooter,
AdvancedTable,
useServerTable,
type AdvancedColumn,
} from '@ema-platform/ui';
import {
extractErrorMessage,
localized,
@@ -58,6 +62,7 @@ export function PaymentConfigPage() {
const { data, isLoading, error } = useGetLicenseTypesQuery();
const { data: capabilities } = useGetPaymentCapabilitiesQuery();
const [editing, setEditing] = useState<LicenseType | null>(null);
const { setPageIndex, pageSize, setPageSize, paginate } = useServerTable();
if (isLoading) {
return (
@@ -82,6 +87,92 @@ export function PaymentConfigPage() {
const types = [...(data?.items ?? [])].sort(
(a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0),
);
const page = paginate(types);
const columns: AdvancedColumn<LicenseType>[] = [
{
header: 'Licence type',
cell: ({ row }) => (
<>
<Text size="sm" fw={600}>
{localized(row.original.name)}
</Text>
<Text size="xs" c="dimmed" ff="monospace">
{row.original.key}
</Text>
</>
),
},
{
header: 'New application',
cell: ({ row }) => (
<Text size="sm" fw={500}>
{feeText(row.original.feeNewApplication, row.original.feeCurrency)}
</Text>
),
},
{
header: 'Renewal',
cell: ({ row }) => {
const type = row.original;
if (type.feeNewApplication === null) {
// No charge at all, so "same as new" would be noise.
return (
<Text size="sm" c="dimmed">
</Text>
);
}
if (type.feeRenewal === null) {
return (
<Tooltip label="No separate renewal fee — renewal is charged at the new-application rate">
<Text size="sm" c="dimmed">
{feeText(type.feeNewApplication, type.feeCurrency)}{' '}
<Text span size="xs" c="dimmed">
(same as new)
</Text>
</Text>
</Tooltip>
);
}
return (
<Text size="sm" fw={500}>
{feeText(type.feeRenewal, type.feeCurrency)}
</Text>
);
},
},
{
header: 'Charged?',
cell: ({ row }) =>
row.original.issuesCertificate ? (
<Badge variant="light" color="teal" size="sm">
On approval
</Badge>
) : (
<Tooltip label="This licence type ends with an EMA decision and never reaches a payment stage">
<Badge variant="light" color="gray" size="sm">
Not charged
</Badge>
</Tooltip>
),
},
{
header: '',
size: 90,
align: 'right',
cell: ({ row }) => (
<Button
size="xs"
variant="light"
leftSection={<IconEdit size={14} />}
onClick={() => setEditing(row.original)}
>
Edit
</Button>
),
},
];
return (
<Stack gap="lg">
@@ -111,84 +202,16 @@ export function PaymentConfigPage() {
</Text>
</Alert>
<Card withBorder radius="md" padding={0}>
<Table.ScrollContainer minWidth={820}>
<Table highlightOnHover verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>Licence type</Table.Th>
<Table.Th>New application</Table.Th>
<Table.Th>Renewal</Table.Th>
<Table.Th>Charged?</Table.Th>
<Table.Th w={90} />
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{types.map((type) => (
<Table.Tr key={type.id}>
<Table.Td>
<Text size="sm" fw={600}>
{localized(type.name)}
</Text>
<Text size="xs" c="dimmed" ff="monospace">
{type.key}
</Text>
</Table.Td>
<Table.Td>
<Text size="sm" fw={500}>
{feeText(type.feeNewApplication, type.feeCurrency)}
</Text>
</Table.Td>
<Table.Td>
{type.feeNewApplication === null ? (
// No charge at all, so "same as new" would be noise.
<Text size="sm" c="dimmed">
</Text>
) : type.feeRenewal === null ? (
<Tooltip label="No separate renewal fee — renewal is charged at the new-application rate">
<Text size="sm" c="dimmed">
{feeText(type.feeNewApplication, type.feeCurrency)}{' '}
<Text span size="xs" c="dimmed">
(same as new)
</Text>
</Text>
</Tooltip>
) : (
<Text size="sm" fw={500}>
{feeText(type.feeRenewal, type.feeCurrency)}
</Text>
)}
</Table.Td>
<Table.Td>
{type.issuesCertificate ? (
<Badge variant="light" color="teal" size="sm">
On approval
</Badge>
) : (
<Tooltip label="This licence type ends with an EMA decision and never reaches a payment stage">
<Badge variant="light" color="gray" size="sm">
Not charged
</Badge>
</Tooltip>
)}
</Table.Td>
<Table.Td align="right">
<Button
size="xs"
variant="light"
leftSection={<IconEdit size={14} />}
onClick={() => setEditing(type)}
>
Edit
</Button>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</Table.ScrollContainer>
</Card>
<AdvancedTable
tableName="payment-config-license-types"
columns={columns}
data={page.rows}
itemCount={page.itemCount}
pageIndex={page.pageIndex}
onPageChange={setPageIndex}
pageSize={pageSize}
onPageSizeChange={setPageSize}
/>
<GatewayPanel bypassEnabled={capabilities?.bypassEnabled} />