mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-28 09:00:58 +00:00
feat: enhance professions management with server-side pagination and advanced table component
This commit is contained in:
@@ -14,8 +14,11 @@ const configurationApi = baseApi.injectEndpoints({
|
||||
providesTags: ['Api'],
|
||||
}),
|
||||
|
||||
getProfessions: builder.query<ListResponse<Profession>, void>({
|
||||
query: () => '/professions',
|
||||
getProfessions: builder.query<
|
||||
ListResponse<Profession>,
|
||||
{ skip?: number; take?: number; q?: string } | void
|
||||
>({
|
||||
query: (arg) => ({ url: '/professions', params: arg ?? {} }),
|
||||
providesTags: ['Api'],
|
||||
}),
|
||||
createProfession: builder.mutation<Profession, CreateProfessionPayload>({
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
Button,
|
||||
TextInput,
|
||||
Textarea,
|
||||
Table,
|
||||
ActionIcon,
|
||||
Modal,
|
||||
Text,
|
||||
@@ -21,7 +20,13 @@ import { useForm } from '@mantine/form';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { IconEdit, IconTrash, IconPlus, IconBriefcase, IconMap, IconCertificate, IconInfoCircle } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { notify, useErrorHandler } from '@ema-platform/ui';
|
||||
import {
|
||||
notify,
|
||||
useErrorHandler,
|
||||
AdvancedTable,
|
||||
useServerTable,
|
||||
type AdvancedColumn,
|
||||
} from '@ema-platform/ui';
|
||||
import { LocationPage } from '../../location/pages/LocationPage';
|
||||
import { CertificationPage } from '../../certification/pages/CertificationPage';
|
||||
import {
|
||||
@@ -133,13 +138,24 @@ function ProfessionTab() {
|
||||
const locale = i18n.language as 'en' | 'am';
|
||||
const { handleError } = useErrorHandler();
|
||||
const { data: deptRes } = useGetOrganizationsQuery();
|
||||
const { data: profRes, isLoading, isError } = useGetProfessionsQuery();
|
||||
const { pageIndex, setPageIndex, q, setQ, skip, take } = useServerTable({ pageSize: 10 });
|
||||
const {
|
||||
data: profRes,
|
||||
isLoading,
|
||||
isFetching,
|
||||
isError,
|
||||
refetch,
|
||||
} = useGetProfessionsQuery({ skip, take, q: q || undefined });
|
||||
const [createProfession, { isLoading: isCreating }] = useCreateProfessionMutation();
|
||||
const [updateProfession, { isLoading: isUpdating }] = useUpdateProfessionMutation();
|
||||
const [deleteProfession] = useDeleteProfessionMutation();
|
||||
|
||||
const departments = Array.isArray(deptRes) ? deptRes : (deptRes?.items ?? []);
|
||||
// Server now paginates, so the previous client-side `isActive` filter is
|
||||
// dropped (it would hide rows outside just this page). Restore it via a
|
||||
// server-side `q` filter once the backend field name is confirmed.
|
||||
const professions = profRes?.items ?? [];
|
||||
const totalCount = profRes?.total ?? profRes?.count ?? professions.length;
|
||||
|
||||
const [editingProf, setEditingProf] = useState<Profession | null>(null);
|
||||
const [showProfForm, setShowProfForm] = useState(false);
|
||||
@@ -210,6 +226,37 @@ function ProfessionTab() {
|
||||
return dept ? (dept.name?.[locale] ?? dept.name?.en ?? '-') : '-';
|
||||
}, [departments, locale]);
|
||||
|
||||
const professionColumns: AdvancedColumn<Profession>[] = [
|
||||
{
|
||||
header: t('configuration.name'),
|
||||
cell: ({ row }) => row.original.name[locale],
|
||||
},
|
||||
{
|
||||
header: t('configuration.description'),
|
||||
cell: ({ row }) => (
|
||||
<Text size="sm" lineClamp={2} maw={200}>{row.original.description[locale]}</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: t('configuration.department'),
|
||||
cell: ({ row }) => getDeptName(row.original.departmentId),
|
||||
},
|
||||
{
|
||||
header: '',
|
||||
size: 90,
|
||||
cell: ({ row }) => (
|
||||
<Group gap="xs">
|
||||
<ActionIcon variant="subtle" color="blue" size="sm" onClick={() => handleEditProf(row.original)}>
|
||||
<IconEdit size={14} />
|
||||
</ActionIcon>
|
||||
<ActionIcon variant="subtle" color="red" size="sm" onClick={() => handleDeleteProf(row.original)}>
|
||||
<IconTrash size={14} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
if (isLoading) {
|
||||
return <Center py="xl"><Loader /></Center>;
|
||||
}
|
||||
@@ -244,46 +291,19 @@ function ProfessionTab() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Table striped highlightOnHover>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{t('configuration.name')}</Table.Th>
|
||||
<Table.Th>{t('configuration.description')}</Table.Th>
|
||||
<Table.Th>{t('configuration.department')}</Table.Th>
|
||||
<Table.Th />
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{professions.filter((p) => p.isActive).map((prof) => (
|
||||
<Table.Tr key={prof.id}>
|
||||
<Table.Td>{prof.name[locale]}</Table.Td>
|
||||
<Table.Td>
|
||||
<Text size="sm" lineClamp={2} maw={200}>{prof.description[locale]}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>{getDeptName(prof.departmentId)}</Table.Td>
|
||||
<Table.Td>
|
||||
<Group gap="xs">
|
||||
<ActionIcon variant="subtle" color="blue" size="sm" onClick={() => handleEditProf(prof)}>
|
||||
<IconEdit size={14} />
|
||||
</ActionIcon>
|
||||
<ActionIcon variant="subtle" color="red" size="sm" onClick={() => handleDeleteProf(prof)}>
|
||||
<IconTrash size={14} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
{professions.length === 0 && (
|
||||
<Table.Tr>
|
||||
<Table.Td colSpan={4}>
|
||||
<Text c="dimmed" ta="center" py="xl">
|
||||
{t('configuration.noProfessions')}
|
||||
</Text>
|
||||
</Table.Td>
|
||||
</Table.Tr>
|
||||
)}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
<AdvancedTable
|
||||
columns={professionColumns}
|
||||
data={professions}
|
||||
tableName={t('configuration.professionsList')}
|
||||
itemCount={totalCount}
|
||||
pageIndex={pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={10}
|
||||
refresh={refetch}
|
||||
onSearchChange={setQ}
|
||||
isLoading={isFetching}
|
||||
emptyText={t('configuration.noProfessions')}
|
||||
/>
|
||||
|
||||
<Modal opened={deleteOpened} onClose={closeDelete} title={t('configuration.confirmDelete')} size="sm">
|
||||
<Text mb="md">
|
||||
|
||||
@@ -28,7 +28,8 @@ export interface Profession {
|
||||
}
|
||||
|
||||
export interface ListResponse<T> {
|
||||
count: number;
|
||||
count?: number;
|
||||
total?: number;
|
||||
items: T[];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user