mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-07 14:15:45 +00:00
Merge branch 'dev' of github.com:Tria-plc/emaui into Refactor
Resolved by unifying on the lib/data AdvancedTable (PR #10) as the canonical table component: kept its API plus teammate i18n/feature work, kept the folder-per-table structure (index.tsx + columns.tsx + actions.tsx) across all 27 tables, removed the parallel lib/table implementation, and fixed pre-existing compile breaks in ExamDetailPage/QuestionAssigner/RecordResultModal. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +1,29 @@
|
||||
import { ActionIcon, Group } from '@mantine/core';
|
||||
import { IconEdit, IconTrash } from '@tabler/icons-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedTableAction } from '@ema-platform/ui';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import type { Certification } from '../../types/certification';
|
||||
|
||||
export function certificationColumnActions(
|
||||
export function certificationActionsColumn(
|
||||
t: TFunction,
|
||||
handlers: {
|
||||
onEdit: (cert: Certification) => void;
|
||||
onDelete: (cert: Certification) => void;
|
||||
},
|
||||
): AdvancedTableAction<Certification>[] {
|
||||
return [
|
||||
{
|
||||
key: 'edit',
|
||||
label: t('certification.update'),
|
||||
color: 'blue',
|
||||
icon: <IconEdit size={14} />,
|
||||
onClick: handlers.onEdit,
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: t('certification.delete'),
|
||||
color: 'red',
|
||||
icon: <IconTrash size={14} />,
|
||||
onClick: handlers.onDelete,
|
||||
},
|
||||
];
|
||||
): AdvancedColumn<Certification> {
|
||||
return {
|
||||
header: '',
|
||||
label: t('certification.columns.actions', 'Actions'),
|
||||
size: 90,
|
||||
cell: ({ row }) => (
|
||||
<Group gap="xs">
|
||||
<ActionIcon variant="subtle" color="blue" size="sm" onClick={() => handlers.onEdit(row.original)}>
|
||||
<IconEdit size={14} />
|
||||
</ActionIcon>
|
||||
<ActionIcon variant="subtle" color="red" size="sm" onClick={() => handlers.onDelete(row.original)}>
|
||||
<IconTrash size={14} />
|
||||
</ActionIcon>
|
||||
</Group>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
import { Badge, Text } from '@mantine/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedTableColumn } from '@ema-platform/ui';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import type { Certification } from '../../types/certification';
|
||||
|
||||
export function certificationColumns(
|
||||
t: TFunction,
|
||||
locale: 'en' | 'am',
|
||||
): AdvancedTableColumn<Certification>[] {
|
||||
): AdvancedColumn<Certification>[] {
|
||||
return [
|
||||
{
|
||||
key: 'name',
|
||||
header: t('certification.columns.name'),
|
||||
render: (cert) => <Text fz="sm" fw={500}>{cert.name[locale]}</Text>,
|
||||
cell: ({ row }) => <Text fz="sm" fw={500}>{row.original.name[locale]}</Text>,
|
||||
},
|
||||
{
|
||||
key: 'description',
|
||||
header: t('certification.columns.description'),
|
||||
render: (cert) => <Text fz="sm" lineClamp={2} maw={250}>{cert.description[locale]}</Text>,
|
||||
cell: ({ row }) => <Text fz="sm" lineClamp={2} maw={250}>{row.original.description[locale]}</Text>,
|
||||
},
|
||||
{
|
||||
key: 'status',
|
||||
header: t('certification.columns.status'),
|
||||
render: (cert) => (
|
||||
<Badge size="sm" variant="light" color={cert.isActive ? 'teal' : 'gray'}>
|
||||
{cert.isActive ? t('certification.status.active') : t('certification.status.inactive')}
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color={row.original.isActive ? 'teal' : 'gray'}>
|
||||
{row.original.isActive ? t('certification.status.active') : t('certification.status.inactive')}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -8,17 +8,13 @@ import {
|
||||
Text,
|
||||
TextInput,
|
||||
Textarea,
|
||||
Paper,
|
||||
Loader,
|
||||
Center,
|
||||
Card,
|
||||
Alert,
|
||||
} from '@mantine/core';
|
||||
import { useDisclosure } from '@mantine/hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { IconPlus, IconInfoCircle } from '@tabler/icons-react';
|
||||
import { AdvancedTable, notify } from '@ema-platform/ui';
|
||||
import { certificationColumns } from './columns';
|
||||
import { certificationColumnActions } from './actions';
|
||||
import { notify, useErrorHandler, AdvancedTable, useServerTable, ModalFooter } from '@ema-platform/ui';
|
||||
import {
|
||||
useGetCertificationsQuery,
|
||||
useCreateCertificationMutation,
|
||||
@@ -26,6 +22,8 @@ import {
|
||||
useDeleteCertificationMutation,
|
||||
} from '../../api/certification-api';
|
||||
import type { Certification } from '../../types/certification';
|
||||
import { certificationColumns } from './columns';
|
||||
import { certificationActionsColumn } from './actions';
|
||||
|
||||
function CertificationForm({
|
||||
editing,
|
||||
@@ -54,27 +52,29 @@ function CertificationForm({
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper p="md" withBorder mb="md" radius="md">
|
||||
<Modal opened onClose={onCancel} title={editing ? t('certification.update') : t('certification.add')} size="md">
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Stack gap="sm">
|
||||
<TextInput label={t('certification.form.nameEn')} placeholder={t('certification.form.nameEnPlaceholder')} value={nameEn} onChange={(e) => setNameEn(e.currentTarget.value)} size="sm" required />
|
||||
<TextInput label={t('certification.form.nameAm')} placeholder={t('certification.form.nameAmPlaceholder')} value={nameAm} onChange={(e) => setNameAm(e.currentTarget.value)} size="sm" required />
|
||||
<Textarea label={t('certification.form.descEn')} placeholder={t('certification.form.descEnPlaceholder')} value={descEn} onChange={(e) => setDescEn(e.currentTarget.value)} size="sm" autosize minRows={2} />
|
||||
<Textarea label={t('certification.form.descAm')} placeholder={t('certification.form.descAmPlaceholder')} value={descAm} onChange={(e) => setDescAm(e.currentTarget.value)} size="sm" autosize minRows={2} />
|
||||
<Group justify="flex-end">
|
||||
<ModalFooter>
|
||||
<Button variant="default" onClick={onCancel} size="sm">{t('certification.cancel')}</Button>
|
||||
<Button type="submit" size="sm" loading={isSubmitting}>{editing ? t('certification.update') : t('certification.create')}</Button>
|
||||
</Group>
|
||||
</ModalFooter>
|
||||
</Stack>
|
||||
</form>
|
||||
</Paper>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export function CertificationPage() {
|
||||
const { t, i18n } = useTranslation();
|
||||
const locale = i18n.language as 'en' | 'am';
|
||||
const { data, isLoading, isError, refetch } = useGetCertificationsQuery();
|
||||
const { handleError } = useErrorHandler();
|
||||
const { data, isFetching, isError, refetch } = useGetCertificationsQuery();
|
||||
const { setPageIndex, pageSize, setPageSize, paginate } = useServerTable();
|
||||
const [createCert, { isLoading: isCreating }] = useCreateCertificationMutation();
|
||||
const [updateCert, { isLoading: isUpdating }] = useUpdateCertificationMutation();
|
||||
const [deleteCert] = useDeleteCertificationMutation();
|
||||
@@ -103,8 +103,8 @@ export function CertificationPage() {
|
||||
notify.success(t('certification.created'));
|
||||
}
|
||||
resetForm();
|
||||
} catch {
|
||||
notify.error(t('certification.error'));
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -115,14 +115,23 @@ export function CertificationPage() {
|
||||
notify.success(t('certification.deleted'));
|
||||
closeDelete();
|
||||
setDeleteTarget(null);
|
||||
} catch {
|
||||
notify.error(t('certification.error'));
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) return <Center py="xl"><Loader /></Center>;
|
||||
if (isError) return <Alert icon={<IconInfoCircle size={16} />} color="red" title={t('certification.loadError')} />;
|
||||
|
||||
const columns = [
|
||||
...certificationColumns(t, locale),
|
||||
certificationActionsColumn(t, {
|
||||
onEdit: (cert) => { setEditing(cert); setShowForm(true); },
|
||||
onDelete: (cert) => { setDeleteTarget(cert); openDelete(); },
|
||||
}),
|
||||
];
|
||||
|
||||
const page = paginate(certifications);
|
||||
|
||||
return (
|
||||
<Stack gap="lg">
|
||||
<Group justify="space-between" align="flex-end">
|
||||
@@ -146,26 +155,28 @@ export function CertificationPage() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<Paper withBorder radius="md">
|
||||
<Card withBorder padding={0}>
|
||||
<AdvancedTable
|
||||
columns={certificationColumns(t, locale)}
|
||||
data={certifications}
|
||||
rowKey={(cert) => cert.id}
|
||||
actions={certificationColumnActions(t, {
|
||||
onEdit: (cert) => { setEditing(cert); setShowForm(true); },
|
||||
onDelete: (cert) => { setDeleteTarget(cert); openDelete(); },
|
||||
})}
|
||||
onRefresh={refetch}
|
||||
emptyTitle={t('certification.noItems')}
|
||||
columns={columns}
|
||||
data={page.rows}
|
||||
tableName={t('certification.title')}
|
||||
itemCount={page.itemCount}
|
||||
pageIndex={page.pageIndex}
|
||||
onPageChange={setPageIndex}
|
||||
pageSize={pageSize}
|
||||
onPageSizeChange={setPageSize}
|
||||
refresh={refetch}
|
||||
isLoading={isFetching}
|
||||
emptyText={t('certification.noItems')}
|
||||
/>
|
||||
</Paper>
|
||||
</Card>
|
||||
|
||||
<Modal opened={deleteOpened} onClose={closeDelete} title={t('certification.confirmDelete')} size="sm">
|
||||
<Text mb="md">{t('certification.deleteConfirmText', { name: deleteTarget?.name?.[locale] })}</Text>
|
||||
<Group justify="flex-end">
|
||||
<ModalFooter>
|
||||
<Button variant="default" onClick={closeDelete} size="sm">{t('certification.cancel')}</Button>
|
||||
<Button color="red" onClick={handleDelete} size="sm">{t('certification.delete')}</Button>
|
||||
</Group>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user