From fd110992cab5dbb7c07cc53f912453079c8b7556 Mon Sep 17 00:00:00 2001 From: Jefferson Silva Date: Tue, 3 Sep 2024 19:32:38 -0300 Subject: [PATCH 1/5] feat: implement page as crudmodule form container variation --- packages/react-material-ui/src/index.ts | 1 + .../src/modules/crud/index.tsx | 12 +- .../src/modules/form/index.tsx | 234 ++++++++++++++++++ .../src/components/AppBarContainer.tsx | 2 +- .../src/components/DefaultRoute.tsx | 31 ++- .../src/components/Resource.tsx | 5 +- 6 files changed, 271 insertions(+), 14 deletions(-) create mode 100644 packages/react-material-ui/src/modules/form/index.tsx diff --git a/packages/react-material-ui/src/index.ts b/packages/react-material-ui/src/index.ts index 60e76bf8..6d767b15 100644 --- a/packages/react-material-ui/src/index.ts +++ b/packages/react-material-ui/src/index.ts @@ -81,6 +81,7 @@ export { export { AuthModule, AuthModuleProps } from './modules/auth'; export { default as CrudModule } from './modules/crud'; +export { default as FormModule } from './modules/form'; export { default as UsersModule } from './modules/users'; export { default as OtpInput } from './components/OtpInput'; diff --git a/packages/react-material-ui/src/modules/crud/index.tsx b/packages/react-material-ui/src/modules/crud/index.tsx index bf098038..1f068271 100644 --- a/packages/react-material-ui/src/modules/crud/index.tsx +++ b/packages/react-material-ui/src/modules/crud/index.tsx @@ -64,7 +64,7 @@ export interface ModuleProps { title?: string; resource: string; tableProps: TableProps; - formContainerVariation?: 'drawer' | 'modal'; + formContainerVariation?: 'drawer' | 'modal' | 'page'; detailsFormProps?: PropsWithChildren; createFormProps?: PropsWithChildren; editFormProps?: PropsWithChildren; @@ -221,11 +221,21 @@ const CrudModule = (props: ModuleProps) => { { + if (props.formContainerVariation === 'page') { + props.navigate( + `/${props.resource}/${payload.action}/${payload.row.id}`, + ); + } + setSelectedRow(payload.row); setDrawerViewMode(payload.action); setCurrentViewIndex(payload.index); }} onAddNew={() => { + if (props.formContainerVariation === 'page') { + props.navigate(`/${props.resource}/new`); + } + setSelectedRow(null); setDrawerViewMode('creation'); setCurrentViewIndex(0); diff --git a/packages/react-material-ui/src/modules/form/index.tsx b/packages/react-material-ui/src/modules/form/index.tsx new file mode 100644 index 00000000..bf13aa62 --- /dev/null +++ b/packages/react-material-ui/src/modules/form/index.tsx @@ -0,0 +1,234 @@ +import React, { useMemo, useState } from 'react'; + +import { Box, Button, CircularProgress } from '@mui/material'; +import useDataProvider, { useQuery } from '@concepta/react-data-provider'; +import validator from '@rjsf/validator-ajv6'; + +import type { IChangeEvent } from '@rjsf/core'; + +import Text from '../../components/Text'; +import Breadcrumbs from '../../components/Breadcrumbs/Breadcrumbs'; +import { SchemaForm } from '../../components/SchemaForm'; +import { CustomTextFieldWidget } from '../../styles/CustomWidgets'; + +import { ModuleProps } from '../crud'; + +const FormModule = (props: ModuleProps) => { + const [formData, setFormData] = useState | null>( + null, + ); + + const [, resource, viewMode, id] = window.location.pathname.split('/'); + + const formProps = useMemo(() => { + switch (viewMode) { + case 'new': + return props.createFormProps; + case 'edit': + return props.editFormProps; + case 'details': + return props.detailsFormProps; + default: + return props.createFormProps; + } + }, [ + viewMode, + props.createFormProps, + props.detailsFormProps, + props.editFormProps, + ]); + + const { + formSchema, + formUiSchema, + customValidate, + submitButtonTitle, + cancelButtonTitle, + children, + customFooterContent, + hideCancelButton, + onSuccess, + onError, + ...otherProps + } = formProps; + + const { get, post, patch, del } = useDataProvider(); + + useQuery( + () => + get({ + uri: `/${resource}/${id}`, + }), + Boolean(id), + { + onSuccess: (data) => setFormData(data as Record), + }, + ); + + const { execute: createItem, isPending: isLoadingCreation } = useQuery( + (data: Record) => + post({ + uri: `/${resource}`, + body: data, + }), + false, + { + onSuccess: onSuccess, + onError: onError, + }, + ); + + const { execute: editItem, isPending: isLoadingEdit } = useQuery( + (data: Record) => + patch({ + uri: `/${resource}/${id}`, + body: data, + }), + false, + { + onSuccess: onSuccess, + onError: onError, + }, + ); + + const { execute: deleteItem, isPending: isLoadingDelete } = useQuery( + () => + del({ + uri: `/${resource}/${id}`, + }), + false, + { + onSuccess: onSuccess, + onError: onError, + }, + ); + + const handleFormSubmit = async ( + values: IChangeEvent>, + ) => { + const fields = values.formData || {}; + + if (viewMode === 'new') { + await createItem(fields); + } + + if (viewMode === 'edit') { + await editItem(fields); + } + }; + + const _widgets = { + TextWidget: CustomTextFieldWidget, + }; + + return ( + + + + + + {props.title ? ( + + {props.title} + + ) : null} + + + <> + {children} + + + {customFooterContent} + {viewMode === 'creation' && !hideCancelButton && ( + + )} + {viewMode === 'edit' && !hideCancelButton && ( + + )} + {viewMode === 'details' && !hideCancelButton && ( + + )} + {viewMode !== 'details' && ( + + )} + + + + + + ); +}; + +export default FormModule; diff --git a/packages/react-navigation/src/components/AppBarContainer.tsx b/packages/react-navigation/src/components/AppBarContainer.tsx index 0b15e7e7..5a663492 100644 --- a/packages/react-navigation/src/components/AppBarContainer.tsx +++ b/packages/react-navigation/src/components/AppBarContainer.tsx @@ -49,7 +49,7 @@ export default function AppBarContainer({ /> ( onLogoutClick(handleClose)}> Sign Out diff --git a/packages/react-navigation/src/components/DefaultRoute.tsx b/packages/react-navigation/src/components/DefaultRoute.tsx index 92d146b7..f9a73e03 100644 --- a/packages/react-navigation/src/components/DefaultRoute.tsx +++ b/packages/react-navigation/src/components/DefaultRoute.tsx @@ -7,6 +7,7 @@ import { DrawerItemProps, DrawerProps, NavbarProps, + FormModule, } from '@concepta/react-material-ui/'; import { ModuleProps } from '@concepta/react-material-ui/dist/modules/crud'; @@ -18,6 +19,7 @@ type DefaultRouteProps = { showAppBar?: boolean; module?: ModuleProps; page?: ReactNode; + isFormPage?: boolean; items: DrawerItemProps[]; drawerProps?: DrawerProps; navbarProps?: NavbarProps; @@ -35,6 +37,7 @@ const DefaultRoute = ({ showAppBar = true, module, page, + isFormPage = false, items, drawerProps, navbarProps, @@ -48,16 +51,24 @@ const DefaultRoute = ({ onClick: () => item?.id && navigate(item.id), })); - const content = module ? ( - - ) : ( - page - ); + const content = + module && !isFormPage ? ( + + ) : isFormPage ? ( + + ) : ( + page + ); const wrappedContent = showAppBar ? ( renderAppBar ? ( diff --git a/packages/react-navigation/src/components/Resource.tsx b/packages/react-navigation/src/components/Resource.tsx index 45994ca7..edbb20f1 100644 --- a/packages/react-navigation/src/components/Resource.tsx +++ b/packages/react-navigation/src/components/Resource.tsx @@ -4,13 +4,14 @@ import { ModuleProps } from '@concepta/react-material-ui/dist/modules/crud'; type ResourceProps = { id: string; - name: string; - icon: ReactNode; + name?: string; + icon?: ReactNode; showDrawerItem?: boolean; isUnprotected?: boolean; showAppBar?: boolean; module?: Partial; page?: ReactNode; + isFormPage?: boolean; }; const Resource = ({ id }: ResourceProps) => { From 5033023bc93d22ff690114394273dd63d0777366 Mon Sep 17 00:00:00 2001 From: Jefferson Silva Date: Tue, 3 Sep 2024 22:04:44 -0300 Subject: [PATCH 2/5] feat: implement confirmation modal component --- .../submodules/ConfirmationModal/index.tsx | 43 ++++++ .../src/modules/form/index.tsx | 133 +++++++++--------- 2 files changed, 112 insertions(+), 64 deletions(-) create mode 100644 packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx diff --git a/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx b/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx new file mode 100644 index 00000000..5e1a96b2 --- /dev/null +++ b/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Box, Button, Dialog, DialogContent, Typography } from '@mui/material'; +import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined'; + +type Props = { + isOpen: boolean; + onClose: () => void; + onConfirm: () => void; +}; + +const ConfirmationModal = ({ isOpen, onClose, onConfirm }: Props) => { + return ( + + + + + + Alert Title + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ut + lectus posuere, rutrum arcu non, blandit ipsum. + + + + + + + + + ); +}; + +export default ConfirmationModal; diff --git a/packages/react-material-ui/src/modules/form/index.tsx b/packages/react-material-ui/src/modules/form/index.tsx index bf13aa62..947e3ecb 100644 --- a/packages/react-material-ui/src/modules/form/index.tsx +++ b/packages/react-material-ui/src/modules/form/index.tsx @@ -8,6 +8,7 @@ import type { IChangeEvent } from '@rjsf/core'; import Text from '../../components/Text'; import Breadcrumbs from '../../components/Breadcrumbs/Breadcrumbs'; +import ConfirmationModal from '../../components/submodules/ConfirmationModal'; import { SchemaForm } from '../../components/SchemaForm'; import { CustomTextFieldWidget } from '../../styles/CustomWidgets'; @@ -17,6 +18,8 @@ const FormModule = (props: ModuleProps) => { const [formData, setFormData] = useState | null>( null, ); + const [isConfirmationModalOpen, setConfirmationModalOpen] = + useState(false); const [, resource, viewMode, id] = window.location.pathname.split('/'); @@ -156,77 +159,79 @@ const FormModule = (props: ModuleProps) => { readonly={viewMode === 'details'} {...otherProps} > - <> - {children} + {children} + - - {customFooterContent} - {viewMode === 'creation' && !hideCancelButton && ( - - )} - {viewMode === 'edit' && !hideCancelButton && ( - - )} - {viewMode === 'details' && !hideCancelButton && ( - - )} - {viewMode !== 'details' && ( - - )} - + {customFooterContent} + {viewMode === 'new' && !hideCancelButton && ( + + )} + {viewMode === 'edit' && !hideCancelButton && ( + + )} + {viewMode === 'details' && !hideCancelButton && ( + + )} + {viewMode !== 'details' && ( + + )} - + + setConfirmationModalOpen(false)} + onConfirm={() => { + setConfirmationModalOpen(false); + deleteItem(formData); + }} + /> ); }; From 33566d559133be98c65dccb12af2add9d26b3b66 Mon Sep 17 00:00:00 2001 From: Jefferson Silva Date: Wed, 4 Sep 2024 14:07:56 -0300 Subject: [PATCH 3/5] refactor: modify confirmation modal styles --- .../submodules/ConfirmationModal/index.tsx | 26 ++++++++++++++++--- .../submodules/DrawerForm/index.tsx | 15 +++++++++-- .../components/submodules/ModalForm/index.tsx | 16 ++++++++++-- .../src/modules/form/index.tsx | 7 ++++- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx b/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx index 5e1a96b2..7454b021 100644 --- a/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx +++ b/packages/react-material-ui/src/components/submodules/ConfirmationModal/index.tsx @@ -18,7 +18,22 @@ const ConfirmationModal = ({ isOpen, onClose, onConfirm }: Props) => { alignItems="center" textAlign="center" > - + + + Alert Title @@ -28,10 +43,15 @@ const ConfirmationModal = ({ isOpen, onClose, onConfirm }: Props) => { - - diff --git a/packages/react-material-ui/src/components/submodules/DrawerForm/index.tsx b/packages/react-material-ui/src/components/submodules/DrawerForm/index.tsx index db862d7e..6899a284 100644 --- a/packages/react-material-ui/src/components/submodules/DrawerForm/index.tsx +++ b/packages/react-material-ui/src/components/submodules/DrawerForm/index.tsx @@ -18,7 +18,7 @@ import useDataProvider, { useQuery } from '@concepta/react-data-provider'; import validator from '@rjsf/validator-ajv6'; import { SchemaForm, SchemaFormProps } from '../../../components/SchemaForm'; - +import ConfirmationModal from '../ConfirmationModal'; import { CustomTextFieldWidget } from '../../../styles/CustomWidgets'; type Action = 'creation' | 'edit' | 'details' | null; @@ -64,6 +64,9 @@ type DrawerFormSubmoduleProps = PropsWithChildren< }; const DrawerFormSubmodule = (props: DrawerFormSubmoduleProps) => { + const [isConfirmationModalOpen, setConfirmationModalOpen] = + useState(false); + const { queryResource, viewMode, @@ -257,7 +260,7 @@ const DrawerFormSubmodule = (props: DrawerFormSubmoduleProps) => {