feat: implement full page components and validation schema; enhance translations and form handling
This commit is contained in:
+2
-2
@@ -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'))),
|
||||
});
|
||||
};
|
||||
|
||||
+27
@@ -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>
|
||||
);
|
||||
}
|
||||
+32
@@ -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>
|
||||
);
|
||||
}
|
||||
+4
-4
@@ -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>
|
||||
);
|
||||
+1
-5
@@ -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."
|
||||
}
|
||||
+1
-5
@@ -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."
|
||||
}
|
||||
+5
-1
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-22
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
+3
-3
@@ -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}</>;
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user