feat: implement full page components and validation schema; enhance translations and form handling

This commit is contained in:
Firman Ramdhani
2026-07-27 11:52:40 +07:00
parent db1abcbdd4
commit 5f97c6b2b0
16 changed files with 131 additions and 43 deletions
@@ -12,9 +12,9 @@ import { compose, required, rangeLength } from '@repo/ui/validators';
export const createFullPageSchema = (t: any) => {
return z.object({
// Code: Required, length between 3 and 10 characters.
code: compose(z.string(), required(t('fields.code'))),
code: compose(z.string(), required(t('common:fields.code'))),
// Name: Required, length between 3 and 50 characters.
name: compose(z.string(), required(t('fields.name')), rangeLength(3, 50, t('fields.name'))),
name: compose(z.string(), required(t('common:fields.name')), rangeLength(3, 50, t('common:fields.name'))),
});
};
@@ -0,0 +1,27 @@
import { Box, Paper, SimpleGrid, FieldValue, RenderDate } from '@repo/ui/components';
import { useDetailPageContext, useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
import { FullPageEntity } from '../../../domain/entities';
export function DetailGeneral() {
const { detailData } = useDetailPageContext();
const { t } = useEnterpriseModuleTranslationContext();
// Using mock data as fallback for UI demonstration
const data: FullPageEntity = detailData as any;
return (
<Paper withBorder shadow="sm" radius="md" p="xl">
<Box>
<SimpleGrid cols={{ base: 1, sm: 2, md: 8 }} spacing="md" verticalSpacing="xl">
<FieldValue label={t('common:fields.code')} value={data?.code} />
<FieldValue label={t('common:fields.name')} value={data?.name} />
<FieldValue
label={t('common:fields.createdAt')}
value={data?.created_at}
render={(val) => <RenderDate value={val as any} />}
/>
</SimpleGrid>
</Box>
</Paper>
);
}
@@ -0,0 +1,32 @@
import { Box, FieldTextInput, Paper, SimpleGrid } from '@repo/ui/components';
import { useEnterpriseModuleTranslationContext, useFormPageContext } from '@repo/ui/foundations';
export function FormGeneral() {
const { formControl } = useFormPageContext();
const { t } = useEnterpriseModuleTranslationContext();
return (
<Paper withBorder shadow="sm" radius="md" p="xl">
<Box>
<SimpleGrid cols={{ base: 1, sm: 2, md: 4 }} spacing="md">
<FieldTextInput
control={formControl.control}
name="code"
label={t('common:fields.code')}
placeholder="e.g. WID-001"
required
radius="md"
/>
<FieldTextInput
name="name"
control={formControl.control}
label={t('common:fields.name')}
placeholder="e.g. Dashboard Widget"
required
radius="md"
/>
</SimpleGrid>
</Box>
</Paper>
);
}
@@ -8,15 +8,15 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn<any>; t: an
<FieldTextInput
control={form.control}
name="code"
label={t('fields.code')}
placeholder={`Enter ${t('fields.code')}`}
label={t('common:fields.code')}
placeholder={`Enter ${t('common:fields.code')}`}
/>
<FieldTextInput
control={form.control}
name="name"
label={t('fields.name')}
placeholder={`Enter ${t('fields.name')}`}
label={t('common:fields.name')}
placeholder={`Enter ${t('common:fields.name')}`}
/>
</SimpleGrid>
);
@@ -8,9 +8,5 @@
"detail_page_description": "View and manage detailed information for this record.",
"create_page_description": "Fill out the form below to add a new record to the system.",
"edit_page_description": "Update and modify the information of the selected record.",
"duplicate_page_description": "Copy and modify information from an existing record to quickly create a new one.",
"fields": {
"code": "Code",
"name": "Name"
}
"duplicate_page_description": "Copy and modify information from an existing record to quickly create a new one."
}
@@ -8,9 +8,5 @@
"detail_page_description": "Lihat dan kelola informasi terperinci terkait data ini.",
"create_page_description": "Lengkapi formulir di bawah ini untuk menambahkan data baru ke dalam sistem.",
"edit_page_description": "Perbarui dan ubah informasi pada data yang dipilih.",
"duplicate_page_description": "Salin dan sesuaikan informasi dari data yang sudah ada untuk mempercepat pembuatan data baru.",
"fields": {
"code": "Kode",
"name": "Nama"
}
"duplicate_page_description": "Salin dan sesuaikan informasi dari data yang sudah ada untuk mempercepat pembuatan data baru."
}
@@ -1,5 +1,6 @@
import { EnterpriseDetailPageProvider, useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
import { fullPageModuleConfig } from '../../domain/constants';
import { DetailGeneral } from '../components/detail-component/detail-general';
export default function FullPagePageDetail() {
const { t } = useEnterpriseModuleTranslationContext();
@@ -15,6 +16,9 @@ export default function FullPagePageDetail() {
{ label: t('nav:example-full-page'), type: 'link', href: `${fullPageModuleConfig.webUrl}/index` },
],
}}
></EnterpriseDetailPageProvider>
>
<DetailGeneral/>
</EnterpriseDetailPageProvider>
);
}
@@ -6,6 +6,7 @@ import { fullPageModuleConfig } from '../../domain/constants';
import { createFullPageSchema } from '../../domain/validators/full-page.validator';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { FormGeneral } from '../components/form-component/form-general';
export default function FullPagePageForm({ formPageType }: { formPageType: FormPageType }) {
const { t } = useEnterpriseModuleTranslationContext();
@@ -42,28 +43,7 @@ export default function FullPagePageForm({ formPageType }: { formPageType: FormP
],
}}
>
<Paper withBorder shadow="sm" radius="md" p="xl">
<Box maw={600}>
<Stack gap="lg">
<FieldTextInput
control={formControl.control}
name="code"
label={t('fields.code')}
placeholder="e.g. WID-001"
required
radius="md"
/>
<FieldTextInput
name="name"
control={formControl.control}
label={t('fields.name')}
placeholder="e.g. Dashboard Widget"
required
radius="md"
/>
</Stack>
</Box>
</Paper>
<FormGeneral />
</EnterpriseFormPageProvider>
);
}
@@ -8,15 +8,15 @@ import { Trans } from '@repo/core-i18n';
import { Text } from '@repo/ui/components';
import { LayoutDashboard } from 'lucide-react';
import { useMemo } from 'react';
import { FilterFormContent } from '../components/filter-content';
import { FilterFormContent } from '../components/index-component/filter-content';
export default function FullPagePageIndex() {
const { t } = useEnterpriseModuleTranslationContext();
const columnDefs: ColDef<any>[] = useMemo(() => {
return [
{ field: 'code', headerName: t('fields.code'), minWidth: 150 },
{ field: 'name', headerName: t('fields.name'), minWidth: 150 },
{ field: 'code', headerName: t('common:fields.code'), minWidth: 150 },
{ field: 'name', headerName: t('common:fields.name'), minWidth: 150 },
];
}, [t]);
@@ -0,0 +1,25 @@
import { Stack, Text, StackProps } from '@mantine/core';
import React from 'react';
export interface FieldValueProps extends StackProps {
label: React.ReactNode;
value?: React.ReactNode;
render?: (value?: React.ReactNode) => React.ReactNode;
}
export function FieldValue({ label, value, render, ...stackProps }: FieldValueProps) {
return (
<Stack gap={4} {...stackProps}>
<Text size="xs" fw={600}>
{label}
</Text>
{render ? (
render(value)
) : (
<Text size="sm" fw={500}>
{value || '-'}
</Text>
)}
</Stack>
);
}
@@ -0,0 +1,2 @@
export * from './field-value';
export * from './render-date';
@@ -0,0 +1,19 @@
import React from 'react';
import { DateUtils } from '@repo/utils';
export interface RenderDateProps {
value?: string | number | Date | DateUtils | null;
format?: string;
fallback?: string;
}
export function RenderDate({ value, format = 'DD-MM-YYYY, HH:mm', fallback = '-' }: RenderDateProps) {
if (!value) return <>{fallback}</>;
try {
const parsedValue = typeof value === 'string' && !isNaN(Number(value)) ? Number(value) : value;
return <>{new DateUtils(parsedValue as any).format(format)}</>;
} catch (error) {
return <>{fallback}</>;
}
}
+1
View File
@@ -13,3 +13,4 @@ export * from './core-app-shell';
export * from './actions-tools';
export * from './status-badge';
export * from './ag-grid';
export * from './field-value';
@@ -1,7 +1,9 @@
import { createContext, useContext } from 'react';
import type { BaseEntity } from '@repo/core-api/data-services';
import { FormPageType } from '../entities/entity';
import { UseFormReturn } from 'react-hook-form';
export interface FormPageContextValue<E extends BaseEntity = BaseEntity> {
formControl: UseFormReturn<any>;
formType: FormPageType;
isCreate: boolean;
isEdit: boolean;
@@ -2,8 +2,11 @@ export * from './constant/';
export * from './entities/entity';
export * from './hooks/use-module.context';
export * from './hooks/use-detail-page.context';
export * from './hooks/use-form-draft.context';
export * from './hooks/use-form-page.context';
export * from './hooks/use-index-page.context';
export * from './hooks/use-module.context';
export * from './providers/module.provider';
export * from './providers/index-page.provider';
@@ -336,6 +336,7 @@ export function EnterpriseFormPageProvider<E extends BaseEntity = BaseEntity>(pr
const contextValue = useMemo(
() => ({
formType: formPageType,
formControl: formControl,
isCreate: formPageType === 'CREATE',
isEdit: formPageType === 'EDIT',
isDuplicate: formPageType === 'DUPLICATE',