From 5f97c6b2b0b7f79b1db1013cda7cfe15a4ee5f37 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Mon, 27 Jul 2026 11:52:40 +0700 Subject: [PATCH] feat: implement full page components and validation schema; enhance translations and form handling --- .../domain/validators/full-page.validator.ts | 4 +-- .../detail-component/detail-general.tsx | 27 ++++++++++++++++ .../form-component/form-general.tsx | 32 +++++++++++++++++++ .../{ => index-component}/filter-content.tsx | 8 ++--- .../presentation/languages/en/full-page.json | 6 +--- .../presentation/languages/id/full-page.json | 6 +--- .../pages/full-page.page.detail.tsx | 6 +++- .../pages/full-page.page.form.tsx | 24 ++------------ .../pages/full-page.page.index.tsx | 6 ++-- .../components/field-value/field-value.tsx | 25 +++++++++++++++ .../ui/src/components/field-value/index.ts | 2 ++ .../components/field-value/render-date.tsx | 19 +++++++++++ packages/ui/src/components/index.ts | 1 + .../hooks/use-form-page.context.ts | 2 ++ .../foundations/enterprise-module/index.ts | 5 ++- .../providers/form-page.provider.tsx | 1 + 16 files changed, 131 insertions(+), 43 deletions(-) create mode 100644 apps/web/src/apps/modules/example/full-page/presentation/components/detail-component/detail-general.tsx create mode 100644 apps/web/src/apps/modules/example/full-page/presentation/components/form-component/form-general.tsx rename apps/web/src/apps/modules/example/full-page/presentation/components/{ => index-component}/filter-content.tsx (71%) create mode 100644 packages/ui/src/components/field-value/field-value.tsx create mode 100644 packages/ui/src/components/field-value/index.ts create mode 100644 packages/ui/src/components/field-value/render-date.tsx diff --git a/apps/web/src/apps/modules/example/full-page/domain/validators/full-page.validator.ts b/apps/web/src/apps/modules/example/full-page/domain/validators/full-page.validator.ts index 3d52662..7d4bc01 100644 --- a/apps/web/src/apps/modules/example/full-page/domain/validators/full-page.validator.ts +++ b/apps/web/src/apps/modules/example/full-page/domain/validators/full-page.validator.ts @@ -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'))), }); }; diff --git a/apps/web/src/apps/modules/example/full-page/presentation/components/detail-component/detail-general.tsx b/apps/web/src/apps/modules/example/full-page/presentation/components/detail-component/detail-general.tsx new file mode 100644 index 0000000..b64c279 --- /dev/null +++ b/apps/web/src/apps/modules/example/full-page/presentation/components/detail-component/detail-general.tsx @@ -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 ( + + + + + + } + /> + + + + ); +} diff --git a/apps/web/src/apps/modules/example/full-page/presentation/components/form-component/form-general.tsx b/apps/web/src/apps/modules/example/full-page/presentation/components/form-component/form-general.tsx new file mode 100644 index 0000000..7fcab27 --- /dev/null +++ b/apps/web/src/apps/modules/example/full-page/presentation/components/form-component/form-general.tsx @@ -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 ( + + + + + + + + + ); +} diff --git a/apps/web/src/apps/modules/example/full-page/presentation/components/filter-content.tsx b/apps/web/src/apps/modules/example/full-page/presentation/components/index-component/filter-content.tsx similarity index 71% rename from apps/web/src/apps/modules/example/full-page/presentation/components/filter-content.tsx rename to apps/web/src/apps/modules/example/full-page/presentation/components/index-component/filter-content.tsx index 0bc398d..0edbcb4 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/components/filter-content.tsx +++ b/apps/web/src/apps/modules/example/full-page/presentation/components/index-component/filter-content.tsx @@ -8,15 +8,15 @@ export const FilterFormContent = ({ form, t }: { form: UseFormReturn; t: an ); diff --git a/apps/web/src/apps/modules/example/full-page/presentation/languages/en/full-page.json b/apps/web/src/apps/modules/example/full-page/presentation/languages/en/full-page.json index c460661..6c165fd 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/languages/en/full-page.json +++ b/apps/web/src/apps/modules/example/full-page/presentation/languages/en/full-page.json @@ -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." } \ No newline at end of file diff --git a/apps/web/src/apps/modules/example/full-page/presentation/languages/id/full-page.json b/apps/web/src/apps/modules/example/full-page/presentation/languages/id/full-page.json index 8e59d95..3ddd5f1 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/languages/id/full-page.json +++ b/apps/web/src/apps/modules/example/full-page/presentation/languages/id/full-page.json @@ -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." } \ No newline at end of file diff --git a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.detail.tsx b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.detail.tsx index e467660..09d0b0a 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.detail.tsx +++ b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.detail.tsx @@ -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` }, ], }} - > + > + + + ); } diff --git a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.form.tsx b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.form.tsx index bc69850..53731b2 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.form.tsx +++ b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.form.tsx @@ -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 ], }} > - - - - - - - - + ); } diff --git a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.index.tsx b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.index.tsx index 418bd99..5541cba 100644 --- a/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.index.tsx +++ b/apps/web/src/apps/modules/example/full-page/presentation/pages/full-page.page.index.tsx @@ -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[] = 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]); diff --git a/packages/ui/src/components/field-value/field-value.tsx b/packages/ui/src/components/field-value/field-value.tsx new file mode 100644 index 0000000..ac4dfdb --- /dev/null +++ b/packages/ui/src/components/field-value/field-value.tsx @@ -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 ( + + + {label} + + {render ? ( + render(value) + ) : ( + + {value || '-'} + + )} + + ); +} diff --git a/packages/ui/src/components/field-value/index.ts b/packages/ui/src/components/field-value/index.ts new file mode 100644 index 0000000..1e3ed92 --- /dev/null +++ b/packages/ui/src/components/field-value/index.ts @@ -0,0 +1,2 @@ +export * from './field-value'; +export * from './render-date'; diff --git a/packages/ui/src/components/field-value/render-date.tsx b/packages/ui/src/components/field-value/render-date.tsx new file mode 100644 index 0000000..5f23faa --- /dev/null +++ b/packages/ui/src/components/field-value/render-date.tsx @@ -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}; + } +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index ed14b78..5fda50b 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -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'; diff --git a/packages/ui/src/foundations/enterprise-module/hooks/use-form-page.context.ts b/packages/ui/src/foundations/enterprise-module/hooks/use-form-page.context.ts index 3bc9495..b1dce96 100644 --- a/packages/ui/src/foundations/enterprise-module/hooks/use-form-page.context.ts +++ b/packages/ui/src/foundations/enterprise-module/hooks/use-form-page.context.ts @@ -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 { + formControl: UseFormReturn; formType: FormPageType; isCreate: boolean; isEdit: boolean; diff --git a/packages/ui/src/foundations/enterprise-module/index.ts b/packages/ui/src/foundations/enterprise-module/index.ts index dd0f92e..3af3efb 100644 --- a/packages/ui/src/foundations/enterprise-module/index.ts +++ b/packages/ui/src/foundations/enterprise-module/index.ts @@ -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'; diff --git a/packages/ui/src/foundations/enterprise-module/providers/form-page.provider.tsx b/packages/ui/src/foundations/enterprise-module/providers/form-page.provider.tsx index b8ba281..0616fb2 100644 --- a/packages/ui/src/foundations/enterprise-module/providers/form-page.provider.tsx +++ b/packages/ui/src/foundations/enterprise-module/providers/form-page.provider.tsx @@ -336,6 +336,7 @@ export function EnterpriseFormPageProvider(pr const contextValue = useMemo( () => ({ formType: formPageType, + formControl: formControl, isCreate: formPageType === 'CREATE', isEdit: formPageType === 'EDIT', isDuplicate: formPageType === 'DUPLICATE',