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) => {
|
export const createFullPageSchema = (t: any) => {
|
||||||
return z.object({
|
return z.object({
|
||||||
// Code: Required, length between 3 and 10 characters.
|
// 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: 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
|
<FieldTextInput
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="code"
|
name="code"
|
||||||
label={t('fields.code')}
|
label={t('common:fields.code')}
|
||||||
placeholder={`Enter ${t('fields.code')}`}
|
placeholder={`Enter ${t('common:fields.code')}`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FieldTextInput
|
<FieldTextInput
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="name"
|
name="name"
|
||||||
label={t('fields.name')}
|
label={t('common:fields.name')}
|
||||||
placeholder={`Enter ${t('fields.name')}`}
|
placeholder={`Enter ${t('common:fields.name')}`}
|
||||||
/>
|
/>
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
);
|
);
|
||||||
+1
-5
@@ -8,9 +8,5 @@
|
|||||||
"detail_page_description": "View and manage detailed information for this record.",
|
"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.",
|
"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.",
|
"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.",
|
"duplicate_page_description": "Copy and modify information from an existing record to quickly create a new one."
|
||||||
"fields": {
|
|
||||||
"code": "Code",
|
|
||||||
"name": "Name"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+1
-5
@@ -8,9 +8,5 @@
|
|||||||
"detail_page_description": "Lihat dan kelola informasi terperinci terkait data ini.",
|
"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.",
|
"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.",
|
"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.",
|
"duplicate_page_description": "Salin dan sesuaikan informasi dari data yang sudah ada untuk mempercepat pembuatan data baru."
|
||||||
"fields": {
|
|
||||||
"code": "Kode",
|
|
||||||
"name": "Nama"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+5
-1
@@ -1,5 +1,6 @@
|
|||||||
import { EnterpriseDetailPageProvider, useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
|
import { EnterpriseDetailPageProvider, useEnterpriseModuleTranslationContext } from '@repo/ui/foundations';
|
||||||
import { fullPageModuleConfig } from '../../domain/constants';
|
import { fullPageModuleConfig } from '../../domain/constants';
|
||||||
|
import { DetailGeneral } from '../components/detail-component/detail-general';
|
||||||
|
|
||||||
export default function FullPagePageDetail() {
|
export default function FullPagePageDetail() {
|
||||||
const { t } = useEnterpriseModuleTranslationContext();
|
const { t } = useEnterpriseModuleTranslationContext();
|
||||||
@@ -15,6 +16,9 @@ export default function FullPagePageDetail() {
|
|||||||
{ label: t('nav:example-full-page'), type: 'link', href: `${fullPageModuleConfig.webUrl}/index` },
|
{ 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 { createFullPageSchema } from '../../domain/validators/full-page.validator';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { FormGeneral } from '../components/form-component/form-general';
|
||||||
|
|
||||||
export default function FullPagePageForm({ formPageType }: { formPageType: FormPageType }) {
|
export default function FullPagePageForm({ formPageType }: { formPageType: FormPageType }) {
|
||||||
const { t } = useEnterpriseModuleTranslationContext();
|
const { t } = useEnterpriseModuleTranslationContext();
|
||||||
@@ -42,28 +43,7 @@ export default function FullPagePageForm({ formPageType }: { formPageType: FormP
|
|||||||
],
|
],
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Paper withBorder shadow="sm" radius="md" p="xl">
|
<FormGeneral />
|
||||||
<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>
|
|
||||||
</EnterpriseFormPageProvider>
|
</EnterpriseFormPageProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -8,15 +8,15 @@ 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/index-component/filter-content';
|
||||||
|
|
||||||
export default function FullPagePageIndex() {
|
export default function FullPagePageIndex() {
|
||||||
const { t } = useEnterpriseModuleTranslationContext();
|
const { t } = useEnterpriseModuleTranslationContext();
|
||||||
|
|
||||||
const columnDefs: ColDef<any>[] = useMemo(() => {
|
const columnDefs: ColDef<any>[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{ field: 'code', headerName: t('fields.code'), minWidth: 150 },
|
{ field: 'code', headerName: t('common:fields.code'), minWidth: 150 },
|
||||||
{ field: 'name', headerName: t('fields.name'), minWidth: 150 },
|
{ field: 'name', headerName: t('common:fields.name'), minWidth: 150 },
|
||||||
];
|
];
|
||||||
}, [t]);
|
}, [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 './actions-tools';
|
||||||
export * from './status-badge';
|
export * from './status-badge';
|
||||||
export * from './ag-grid';
|
export * from './ag-grid';
|
||||||
|
export * from './field-value';
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { createContext, useContext } from 'react';
|
import { createContext, useContext } from 'react';
|
||||||
import type { BaseEntity } from '@repo/core-api/data-services';
|
import type { BaseEntity } from '@repo/core-api/data-services';
|
||||||
import { FormPageType } from '../entities/entity';
|
import { FormPageType } from '../entities/entity';
|
||||||
|
import { UseFormReturn } from 'react-hook-form';
|
||||||
export interface FormPageContextValue<E extends BaseEntity = BaseEntity> {
|
export interface FormPageContextValue<E extends BaseEntity = BaseEntity> {
|
||||||
|
formControl: UseFormReturn<any>;
|
||||||
formType: FormPageType;
|
formType: FormPageType;
|
||||||
isCreate: boolean;
|
isCreate: boolean;
|
||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ export * from './constant/';
|
|||||||
|
|
||||||
export * from './entities/entity';
|
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-form-page.context';
|
||||||
|
export * from './hooks/use-index-page.context';
|
||||||
|
export * from './hooks/use-module.context';
|
||||||
|
|
||||||
export * from './providers/module.provider';
|
export * from './providers/module.provider';
|
||||||
export * from './providers/index-page.provider';
|
export * from './providers/index-page.provider';
|
||||||
|
|||||||
@@ -336,6 +336,7 @@ export function EnterpriseFormPageProvider<E extends BaseEntity = BaseEntity>(pr
|
|||||||
const contextValue = useMemo(
|
const contextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
formType: formPageType,
|
formType: formPageType,
|
||||||
|
formControl: formControl,
|
||||||
isCreate: formPageType === 'CREATE',
|
isCreate: formPageType === 'CREATE',
|
||||||
isEdit: formPageType === 'EDIT',
|
isEdit: formPageType === 'EDIT',
|
||||||
isDuplicate: formPageType === 'DUPLICATE',
|
isDuplicate: formPageType === 'DUPLICATE',
|
||||||
|
|||||||
Reference in New Issue
Block a user