refactor: remove unused DataTable component and clean up filter logic in FullPagePageIndex
This commit is contained in:
@@ -1,261 +0,0 @@
|
|||||||
import { useState, useCallback } from 'react';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
Box,
|
|
||||||
Paper,
|
|
||||||
Badge,
|
|
||||||
Text,
|
|
||||||
Group,
|
|
||||||
Button,
|
|
||||||
TextInput,
|
|
||||||
Pagination,
|
|
||||||
Select,
|
|
||||||
ScrollArea,
|
|
||||||
} from '@repo/ui/components';
|
|
||||||
import { PageActions } from '@repo/ui/components';
|
|
||||||
import { useEnterpriseModuleTranslationContext, useEnterpriseModuleNavigationContext } from '@repo/ui/foundations';
|
|
||||||
import { FullPageEntity } from '../../domain/entities';
|
|
||||||
import { Edit2, Eye, Copy, Trash2, Search, Filter, CheckCircle2, Ban } from 'lucide-react';
|
|
||||||
|
|
||||||
// TODO: Replace this mock data with real API data loaded from DataService via useEnterpriseModuleDataServiceContext
|
|
||||||
const MOCK_DATA: FullPageEntity[] = Array.from({ length: 50 }).map((_, i) => ({
|
|
||||||
id: String(i + 1),
|
|
||||||
name: `Database Cluster ${i + 1}`,
|
|
||||||
code: `DB-PROD-${String(i + 1).padStart(3, '0')}`,
|
|
||||||
status: i % 7 === 0 ? 'MAINTENANCE' : i % 4 === 0 ? 'INACTIVE' : 'ACTIVE',
|
|
||||||
description: `Managed PostgreSQL cluster instance in region us-east-${(i % 3) + 1}`,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export function DataTable() {
|
|
||||||
const { t } = useEnterpriseModuleTranslationContext();
|
|
||||||
const { navigateToDetail, navigateToEdit, navigateToDuplicate } = useEnterpriseModuleNavigationContext();
|
|
||||||
|
|
||||||
const [page, setPage] = useState(1);
|
|
||||||
const [pageSize, setPageSize] = useState(10);
|
|
||||||
|
|
||||||
const totalRecords = MOCK_DATA.length;
|
|
||||||
const totalPages = Math.ceil(totalRecords / pageSize);
|
|
||||||
const paginatedData = MOCK_DATA.slice((page - 1) * pageSize, page * pageSize);
|
|
||||||
|
|
||||||
const getRowActions = useCallback(
|
|
||||||
(row: FullPageEntity) => [
|
|
||||||
{
|
|
||||||
key: 'view',
|
|
||||||
label: t('common:view'),
|
|
||||||
icon: <Eye size={14} />,
|
|
||||||
onClick: () => navigateToDetail(row.id as string),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'edit',
|
|
||||||
label: t('common:edit'),
|
|
||||||
icon: <Edit2 size={14} />,
|
|
||||||
onClick: () => navigateToEdit(row.id as string),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'duplicate',
|
|
||||||
label: t('common:duplicate'),
|
|
||||||
icon: <Copy size={14} />,
|
|
||||||
onClick: () => navigateToDuplicate(row.id as string),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'divider' as const,
|
|
||||||
key: 'div-1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'delete',
|
|
||||||
label: t('common:delete'),
|
|
||||||
icon: <Trash2 size={14} />,
|
|
||||||
intent: 'destructive' as const,
|
|
||||||
onClick: () => {
|
|
||||||
// Mock delete action
|
|
||||||
alert(`Delete ${row.name}`);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[t, navigateToDetail, navigateToEdit, navigateToDuplicate],
|
|
||||||
);
|
|
||||||
|
|
||||||
const rows = paginatedData.map((item) => (
|
|
||||||
<Table.Tr key={item.id}>
|
|
||||||
<Table.Td>
|
|
||||||
<Text
|
|
||||||
size="sm"
|
|
||||||
fw={600}
|
|
||||||
style={{ color: 'light-dark(var(--mantine-color-gray-8), var(--mantine-color-dark-0))' }}
|
|
||||||
>
|
|
||||||
{item.code}
|
|
||||||
</Text>
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<Text size="sm" fw={500}>
|
|
||||||
{item.name}
|
|
||||||
</Text>
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<Badge
|
|
||||||
variant={item.status === 'ACTIVE' ? 'light' : item.status === 'MAINTENANCE' ? 'outline' : 'dot'}
|
|
||||||
color={item.status === 'ACTIVE' ? 'teal' : item.status === 'MAINTENANCE' ? 'orange' : 'gray'}
|
|
||||||
size="sm"
|
|
||||||
radius="sm"
|
|
||||||
fw={600}
|
|
||||||
>
|
|
||||||
{item.status}
|
|
||||||
</Badge>
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<Text size="sm" c="dimmed">
|
|
||||||
{item.description}
|
|
||||||
</Text>
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td ta="right">
|
|
||||||
<PageActions actions={getRowActions(item)} />
|
|
||||||
</Table.Td>
|
|
||||||
</Table.Tr>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper withBorder shadow="sm" radius="md" style={{ overflow: 'hidden' }}>
|
|
||||||
{/* --- TABLE TOOLBAR --- */}
|
|
||||||
<Box
|
|
||||||
p="md"
|
|
||||||
style={{ borderBottom: '1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-7))' }}
|
|
||||||
>
|
|
||||||
<Group justify="space-between" wrap="nowrap">
|
|
||||||
<Group gap="sm" wrap="nowrap">
|
|
||||||
<TextInput
|
|
||||||
placeholder="Search items..."
|
|
||||||
leftSection={<Search size={16} strokeWidth={2} style={{ color: 'var(--mantine-color-dimmed)' }} />}
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
w={280}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="default"
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
leftSection={<Filter size={16} strokeWidth={2} style={{ color: 'var(--mantine-color-dimmed)' }} />}
|
|
||||||
>
|
|
||||||
Filters
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
<Group gap="sm" wrap="nowrap">
|
|
||||||
<Group gap="xs">
|
|
||||||
<Button
|
|
||||||
variant="light"
|
|
||||||
color="teal"
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
leftSection={<CheckCircle2 size={14} strokeWidth={2.5} />}
|
|
||||||
>
|
|
||||||
Activate
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="light"
|
|
||||||
color="gray"
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
leftSection={<Ban size={14} strokeWidth={2.5} />}
|
|
||||||
>
|
|
||||||
Deactivate
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="light"
|
|
||||||
color="red"
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
leftSection={<Trash2 size={14} strokeWidth={2.5} />}
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
{/* --- END TABLE TOOLBAR --- */}
|
|
||||||
|
|
||||||
<ScrollArea.Autosize mah="calc(100vh - 350px)" offsetScrollbars>
|
|
||||||
<Table
|
|
||||||
stickyHeader
|
|
||||||
stickyHeaderOffset={-1}
|
|
||||||
striped
|
|
||||||
highlightOnHover
|
|
||||||
verticalSpacing="sm"
|
|
||||||
horizontalSpacing="md"
|
|
||||||
>
|
|
||||||
<Table.Thead bg="light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8))">
|
|
||||||
<Table.Tr>
|
|
||||||
<Table.Th fw={700} style={{ letterSpacing: '0.5px', textTransform: 'uppercase' }} fz="xs" c="dimmed">
|
|
||||||
{t('fields.code')}
|
|
||||||
</Table.Th>
|
|
||||||
<Table.Th fw={700} style={{ letterSpacing: '0.5px', textTransform: 'uppercase' }} fz="xs" c="dimmed">
|
|
||||||
{t('fields.name')}
|
|
||||||
</Table.Th>
|
|
||||||
<Table.Th fw={700} style={{ letterSpacing: '0.5px', textTransform: 'uppercase' }} fz="xs" c="dimmed">
|
|
||||||
{t('fields.status')}
|
|
||||||
</Table.Th>
|
|
||||||
<Table.Th fw={700} style={{ letterSpacing: '0.5px', textTransform: 'uppercase' }} fz="xs" c="dimmed">
|
|
||||||
{t('fields.description')}
|
|
||||||
</Table.Th>
|
|
||||||
<Table.Th
|
|
||||||
fw={700}
|
|
||||||
style={{ letterSpacing: '0.5px', textTransform: 'uppercase' }}
|
|
||||||
fz="xs"
|
|
||||||
c="dimmed"
|
|
||||||
w={80}
|
|
||||||
ta="right"
|
|
||||||
>
|
|
||||||
Actions
|
|
||||||
</Table.Th>
|
|
||||||
</Table.Tr>
|
|
||||||
</Table.Thead>
|
|
||||||
<Table.Tbody>{rows}</Table.Tbody>
|
|
||||||
</Table>
|
|
||||||
</ScrollArea.Autosize>
|
|
||||||
|
|
||||||
{/* --- PAGINATION FOOTER --- */}
|
|
||||||
<Box
|
|
||||||
p="md"
|
|
||||||
style={{ borderTop: '1px solid light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-7))' }}
|
|
||||||
>
|
|
||||||
<Group justify="space-between" align="center">
|
|
||||||
<Text size="sm" c="dimmed" fw={500}>
|
|
||||||
Showing{' '}
|
|
||||||
<Text span fw={600} c="var(--mantine-color-text)">
|
|
||||||
{(page - 1) * pageSize + 1}
|
|
||||||
</Text>{' '}
|
|
||||||
to{' '}
|
|
||||||
<Text span fw={600} c="var(--mantine-color-text)">
|
|
||||||
{Math.min(page * pageSize, totalRecords)}
|
|
||||||
</Text>{' '}
|
|
||||||
of{' '}
|
|
||||||
<Text span fw={600} c="var(--mantine-color-text)">
|
|
||||||
{totalRecords}
|
|
||||||
</Text>{' '}
|
|
||||||
entries
|
|
||||||
</Text>
|
|
||||||
<Group gap="md">
|
|
||||||
<Group gap="xs">
|
|
||||||
<Text size="sm" c="dimmed" fw={500}>
|
|
||||||
Rows per page:
|
|
||||||
</Text>
|
|
||||||
<Select
|
|
||||||
size="sm"
|
|
||||||
radius="md"
|
|
||||||
data={['10', '20', '50']}
|
|
||||||
value={String(pageSize)}
|
|
||||||
onChange={(val) => {
|
|
||||||
setPageSize(Number(val));
|
|
||||||
setPage(1);
|
|
||||||
}}
|
|
||||||
w={75}
|
|
||||||
/>
|
|
||||||
</Group>
|
|
||||||
<Pagination total={totalPages} value={page} onChange={setPage} size="sm" radius="md" color="indigo" />
|
|
||||||
</Group>
|
|
||||||
</Group>
|
|
||||||
</Box>
|
|
||||||
{/* --- END PAGINATION FOOTER --- */}
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
+1
-15
@@ -1,21 +1,8 @@
|
|||||||
import { useEffect } from 'react';
|
|
||||||
import { SimpleGrid } from '@repo/ui/components';
|
import { SimpleGrid } from '@repo/ui/components';
|
||||||
import { FieldTextInput } from '@repo/ui/form';
|
import { FieldTextInput } from '@repo/ui/form';
|
||||||
import { UseFormReturn, useWatch } from 'react-hook-form';
|
import { UseFormReturn } from 'react-hook-form';
|
||||||
|
|
||||||
export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: any }) => {
|
export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: any }) => {
|
||||||
const codeValue = useWatch({
|
|
||||||
control: form.control,
|
|
||||||
name: 'code',
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!codeValue) {
|
|
||||||
form.setValue('name', '');
|
|
||||||
form.clearErrors('name');
|
|
||||||
}
|
|
||||||
}, [codeValue, form]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
<SimpleGrid cols={{ base: 1, sm: 2 }} spacing="md">
|
||||||
<FieldTextInput
|
<FieldTextInput
|
||||||
@@ -30,7 +17,6 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: an
|
|||||||
name="name"
|
name="name"
|
||||||
label={t('fields.name')}
|
label={t('fields.name')}
|
||||||
placeholder={`Enter ${t('fields.name')}`}
|
placeholder={`Enter ${t('fields.name')}`}
|
||||||
disabled={!codeValue}
|
|
||||||
/>
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
);
|
);
|
||||||
|
|||||||
-17
@@ -4,28 +4,12 @@ import {
|
|||||||
EnterpriseDataTable,
|
EnterpriseDataTable,
|
||||||
} from '@repo/ui/foundations';
|
} from '@repo/ui/foundations';
|
||||||
import { ColDef } from '@repo/ui/components';
|
import { ColDef } from '@repo/ui/components';
|
||||||
import { z } from 'zod';
|
|
||||||
import { Trans } from '@repo/core-i18n';
|
import { Trans } from '@repo/core-i18n';
|
||||||
import { Text } from '@repo/ui/components';
|
import { Text } from '@repo/ui/components';
|
||||||
import { LayoutDashboard } from 'lucide-react';
|
import { LayoutDashboard } from 'lucide-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { FilterFormContent } from '../components/filter-content';
|
import { FilterFormContent } from '../components/filter-content';
|
||||||
|
|
||||||
const filterSchema = z
|
|
||||||
.object({
|
|
||||||
code: z.string().optional(),
|
|
||||||
name: z.string().optional(),
|
|
||||||
})
|
|
||||||
.superRefine((data, ctx) => {
|
|
||||||
if (data.code && !data.name) {
|
|
||||||
ctx.addIssue({
|
|
||||||
path: ['name'],
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: 'Name is required when code is provided',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function FullPagePageIndex() {
|
export default function FullPagePageIndex() {
|
||||||
const { t } = useEnterpriseModuleTranslationContext();
|
const { t } = useEnterpriseModuleTranslationContext();
|
||||||
|
|
||||||
@@ -38,7 +22,6 @@ export default function FullPagePageIndex() {
|
|||||||
|
|
||||||
const filterConfig = useMemo(() => {
|
const filterConfig = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
schema: filterSchema,
|
|
||||||
renderBody: (form: any) => {
|
renderBody: (form: any) => {
|
||||||
if (!form) return null;
|
if (!form) return null;
|
||||||
return <FilterFormContent form={form} t={t} />;
|
return <FilterFormContent form={form} t={t} />;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"collapseAll": "Collapse all menu",
|
"collapseAll": "Collapse all menu",
|
||||||
"searchMenu": "Search menu",
|
"searchMenu": "Search menu",
|
||||||
"searchData": "Search data",
|
"searchData": "Search data",
|
||||||
"searchPlaceholder": "Type to search & press Enter...",
|
"searchPlaceholder": "Type to search & press enter...",
|
||||||
"collapse": "Collapse",
|
"collapse": "Collapse",
|
||||||
"expandSidebar": "Expand Sidebar",
|
"expandSidebar": "Expand Sidebar",
|
||||||
"filterTitle": "Filter {{module}}",
|
"filterTitle": "Filter {{module}}",
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
"collapseAll": "Tutup semua menu",
|
"collapseAll": "Tutup semua menu",
|
||||||
"searchMenu": "Cari menu",
|
"searchMenu": "Cari menu",
|
||||||
"searchData": "Cari data",
|
"searchData": "Cari data",
|
||||||
"searchPlaceholder": "Ketik pencarian & tekan Enter...",
|
"searchPlaceholder": "Ketik pencarian & tekan enter...",
|
||||||
"collapse": "Tutup",
|
"collapse": "Tutup",
|
||||||
"expandSidebar": "Perluas Bilah Sisi",
|
"expandSidebar": "Perluas Bilah Sisi",
|
||||||
"filterTitle": "Filter {{module}}",
|
"filterTitle": "Filter {{module}}",
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ import {
|
|||||||
} from 'ag-grid-community';
|
} from 'ag-grid-community';
|
||||||
import { Box, Group, TextInput, Indicator, CloseButton } from '@mantine/core';
|
import { Box, Group, TextInput, Indicator, CloseButton } from '@mantine/core';
|
||||||
import { useDisclosure } from '@mantine/hooks';
|
import { useDisclosure } from '@mantine/hooks';
|
||||||
import { Search, Filter, Settings } from 'lucide-react';
|
import {
|
||||||
|
Search,
|
||||||
|
Filter,
|
||||||
|
// Settings
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
import { DataGrid, StatusBadge, PageActions } from '../../../../components';
|
import { DataGrid, StatusBadge, PageActions } from '../../../../components';
|
||||||
import type { PageActionProps } from '../../../../components';
|
import type { PageActionProps } from '../../../../components';
|
||||||
@@ -205,7 +209,7 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
|
|||||||
// Local UI State
|
// Local UI State
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
const [openedFilter, { open: openFilter, close: closeFilter }] = useDisclosure(false);
|
const [openedFilter, { open: openFilter, close: closeFilter }] = useDisclosure(false);
|
||||||
const [openedSetting, { open: openSetting, close: closeSetting }] = useDisclosure(false);
|
// const [openedSetting, { open: openSetting, close: closeSetting }] = useDisclosure(false);
|
||||||
const moduleTitle = config.tabTitle || t(`${config.translationNamespace}:title`);
|
const moduleTitle = config.tabTitle || t(`${config.translationNamespace}:title`);
|
||||||
|
|
||||||
// Reference to the AG Grid API for programmatic interaction
|
// Reference to the AG Grid API for programmatic interaction
|
||||||
@@ -829,14 +833,14 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
|
|||||||
tooltipLabel: t('common:actions.filter'),
|
tooltipLabel: t('common:actions.filter'),
|
||||||
onClick: openFilter,
|
onClick: openFilter,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
key: 'setting',
|
// key: 'setting',
|
||||||
icon: <Settings size={16} />,
|
// icon: <Settings size={16} />,
|
||||||
variant: 'default',
|
// variant: 'default',
|
||||||
showLabel: false,
|
// showLabel: false,
|
||||||
tooltipLabel: t('common:actions.setting'),
|
// tooltipLabel: t('common:actions.setting'),
|
||||||
onClick: openSetting,
|
// onClick: openSetting,
|
||||||
},
|
// },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
@@ -907,11 +911,11 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
|
|||||||
onFilter={handleFilterApply}
|
onFilter={handleFilterApply}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TableSettingDrawer
|
{/* <TableSettingDrawer
|
||||||
opened={openedSetting}
|
opened={openedSetting}
|
||||||
onClose={closeSetting}
|
onClose={closeSetting}
|
||||||
title={t('common:tableSettingTitle', { module: moduleTitle })}
|
title={t('common:tableSettingTitle', { module: moduleTitle })}
|
||||||
/>
|
/> */}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -631,7 +631,7 @@ export function EnterpriseDetailPageProvider<E extends BaseEntity = BaseEntity>(
|
|||||||
style: { fontSize: 12 },
|
style: { fontSize: 12 },
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
actions={pageActions}
|
actions={detailData ? pageActions : []}
|
||||||
{...pageHeaderPropsValue}
|
{...pageHeaderPropsValue}
|
||||||
moduleKey={moduleKey}
|
moduleKey={moduleKey}
|
||||||
titleProps={{
|
titleProps={{
|
||||||
|
|||||||
Reference in New Issue
Block a user