mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-09-02 23:13:41 +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:
@@ -11,6 +11,7 @@ import {
|
||||
Box,
|
||||
rem,
|
||||
Center,
|
||||
useMantineColorScheme,
|
||||
} from '@mantine/core';
|
||||
import {
|
||||
IconChevronRight,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
import { useGetLocationsQuery } from '../api/location-api';
|
||||
import type { Location } from '../types/location';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useLocalized } from '@ema-platform/api';
|
||||
|
||||
interface LocationTreeProps {
|
||||
selectedId: string | null;
|
||||
@@ -50,10 +52,15 @@ function TreeNode({
|
||||
onSelect: (location: Location) => void;
|
||||
depth: number;
|
||||
}) {
|
||||
const localized = useLocalized();
|
||||
const [opened, setOpened] = useState(depth < 1);
|
||||
const isSelected = selectedId === location.id;
|
||||
const hasChildren =
|
||||
Array.isArray(location.children) && location.children.length > 0;
|
||||
const { colorScheme } = useMantineColorScheme();
|
||||
const hoverBg = colorScheme === 'dark'
|
||||
? 'var(--mantine-color-dark-6)'
|
||||
: 'var(--mantine-color-gray-0)';
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setOpened((prev) => !prev);
|
||||
@@ -88,8 +95,7 @@ function TreeNode({
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
if (!isSelected)
|
||||
e.currentTarget.style.backgroundColor =
|
||||
'var(--mantine-color-gray-0)';
|
||||
e.currentTarget.style.backgroundColor = hoverBg;
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
if (!isSelected)
|
||||
@@ -130,7 +136,7 @@ function TreeNode({
|
||||
style={{ flexShrink: 0, opacity: 0.6 }}
|
||||
/>
|
||||
<Text size="sm" truncate style={{ flex: 1 }}>
|
||||
{location.names.en}
|
||||
{localized(location.names)}
|
||||
</Text>
|
||||
</UnstyledButton>
|
||||
{hasChildren && (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ActionIcon, Group, Tooltip } 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 { LocationType } from '../../types/location';
|
||||
|
||||
export function locationTypeColumnActions(
|
||||
@@ -9,21 +10,34 @@ export function locationTypeColumnActions(
|
||||
onEdit: (type: LocationType) => void;
|
||||
onDelete: (type: LocationType) => void;
|
||||
},
|
||||
): AdvancedTableAction<LocationType>[] {
|
||||
return [
|
||||
{
|
||||
key: 'edit',
|
||||
label: t('location.edit'),
|
||||
color: 'blue',
|
||||
icon: <IconEdit size={14} />,
|
||||
onClick: handlers.onEdit,
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: t('location.delete'),
|
||||
color: 'red',
|
||||
icon: <IconTrash size={14} />,
|
||||
onClick: handlers.onDelete,
|
||||
},
|
||||
];
|
||||
): AdvancedColumn<LocationType> {
|
||||
return {
|
||||
header: '',
|
||||
label: t('location.actions', 'Actions'),
|
||||
align: 'right',
|
||||
cell: ({ row }) => (
|
||||
<Group gap="xs" wrap="nowrap" justify="flex-end">
|
||||
<Tooltip label={t('location.edit')}>
|
||||
<ActionIcon
|
||||
color="blue"
|
||||
variant="subtle"
|
||||
aria-label={t('location.edit')}
|
||||
onClick={() => handlers.onEdit(row.original)}
|
||||
>
|
||||
<IconEdit size={14} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
<Tooltip label={t('location.delete')}>
|
||||
<ActionIcon
|
||||
color="red"
|
||||
variant="subtle"
|
||||
aria-label={t('location.delete')}
|
||||
onClick={() => handlers.onDelete(row.original)}
|
||||
>
|
||||
<IconTrash size={14} />
|
||||
</ActionIcon>
|
||||
</Tooltip>
|
||||
</Group>
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
import { Badge } from '@mantine/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { AdvancedTableColumn } from '@ema-platform/ui';
|
||||
import type { AdvancedColumn } from '@ema-platform/ui';
|
||||
import type { LocationType } from '../../types/location';
|
||||
|
||||
export function locationTypeColumns(
|
||||
t: TFunction,
|
||||
locale: 'en' | 'am',
|
||||
): AdvancedTableColumn<LocationType>[] {
|
||||
): AdvancedColumn<LocationType>[] {
|
||||
return [
|
||||
{
|
||||
key: 'level',
|
||||
header: t('location.level'),
|
||||
render: (type) => (
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color="gray">
|
||||
{type.level}
|
||||
{row.original.level}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'code',
|
||||
header: t('location.code'),
|
||||
render: (type) => (
|
||||
cell: ({ row }) => (
|
||||
<Badge size="sm" variant="light" color="blue">
|
||||
{type.code}
|
||||
{row.original.code}
|
||||
</Badge>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: 'name',
|
||||
header: t('location.name'),
|
||||
render: (type) => type.names[locale],
|
||||
cell: ({ row }) => row.original.names[locale],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
useUpdateLocationTypeMutation,
|
||||
useDeleteLocationTypeMutation,
|
||||
} from '../../api/location-api';
|
||||
import { AdvancedTable, notify } from '@ema-platform/ui';
|
||||
import { AdvancedTable, notify, useErrorHandler, useServerTable } from '@ema-platform/ui';
|
||||
import { locationTypeColumns } from './columns';
|
||||
import { locationTypeColumnActions } from './actions';
|
||||
|
||||
@@ -31,7 +31,9 @@ interface LocationTypeFormValues {
|
||||
export function LocationTypeModal({ opened, onClose }: { opened: boolean; onClose: () => void }) {
|
||||
const { t, i18n } = useTranslation();
|
||||
const locale = i18n.language as 'en' | 'am';
|
||||
const { handleError } = useErrorHandler();
|
||||
const { data: locationTypes, isLoading, refetch } = useGetLocationTypesQuery();
|
||||
const table = useServerTable();
|
||||
const [createType] = useCreateLocationTypeMutation();
|
||||
const [updateType] = useUpdateLocationTypeMutation();
|
||||
const [deleteType] = useDeleteLocationTypeMutation();
|
||||
@@ -81,8 +83,8 @@ export function LocationTypeModal({ opened, onClose }: { opened: boolean; onClos
|
||||
try {
|
||||
await deleteType(id).unwrap();
|
||||
notify.success(t('location.typeDeleted'));
|
||||
} catch {
|
||||
notify.error(t('location.deleteError'));
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,14 +98,15 @@ export function LocationTypeModal({ opened, onClose }: { opened: boolean; onClos
|
||||
notify.success(t('location.typeCreated'));
|
||||
}
|
||||
resetForm();
|
||||
} catch {
|
||||
notify.error(t('location.typeError'));
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
}
|
||||
});
|
||||
|
||||
const sortedTypes = locationTypes?.items
|
||||
? [...locationTypes.items].sort((a, b) => a.level - b.level)
|
||||
: [];
|
||||
const paged = table.paginate(sortedTypes);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -169,16 +172,22 @@ export function LocationTypeModal({ opened, onClose }: { opened: boolean; onClos
|
||||
<Divider mb="md" />
|
||||
|
||||
<AdvancedTable
|
||||
columns={locationTypeColumns(t, locale)}
|
||||
data={sortedTypes}
|
||||
rowKey={(type) => type.id}
|
||||
actions={locationTypeColumnActions(t, {
|
||||
onEdit: handleEdit,
|
||||
onDelete: (type) => handleDelete(type.id),
|
||||
})}
|
||||
loading={isLoading}
|
||||
onRefresh={refetch}
|
||||
emptyTitle={t('location.noTypes')}
|
||||
tableName={t('location.manageTypes')}
|
||||
columns={[
|
||||
...locationTypeColumns(t, locale),
|
||||
locationTypeColumnActions(t, {
|
||||
onEdit: handleEdit,
|
||||
onDelete: (type) => handleDelete(type.id),
|
||||
}),
|
||||
]}
|
||||
data={paged.rows}
|
||||
itemCount={paged.itemCount}
|
||||
pageIndex={paged.pageIndex}
|
||||
onPageChange={table.setPageIndex}
|
||||
pageSize={table.pageSize}
|
||||
isLoading={isLoading}
|
||||
refresh={refetch}
|
||||
emptyText={t('location.noTypes')}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user