From 19aa42d0c645687b7147129b62f47de20a26d1ee Mon Sep 17 00:00:00 2001 From: Ayobami Akingbade Date: Tue, 26 Dec 2023 06:19:30 +0100 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(data-hooks):=20ce?= =?UTF-8?q?ntralize=20is=20field=20mutabale=20logic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SchemaForm/_RenderFormInput.tsx | 47 ++++++------------- src/frontend/components/SchemaForm/index.tsx | 3 +- src/frontend/components/SchemaForm/types.ts | 24 ++++++++++ .../Widget/useWidgetNavigationLink.ts | 2 +- .../views/data/Details/DetailsView.tsx | 2 +- .../views/data/Details/RelationsDetails.tsx | 2 +- src/frontend/views/data/Details/_Layout.tsx | 2 +- src/frontend/views/data/Details/index.tsx | 2 +- .../views/data/Table/useTableColumns.tsx | 2 +- .../views/data/Table/useTableMenuItems.ts | 2 +- src/frontend/views/data/_BaseEntityForm.tsx | 22 +++------ .../useCanUserPerformCrudAction.ts | 0 .../{ => hooks}/useEntityActionButtons.ts | 0 .../{ => hooks}/useEntityViewStateMachine.ts | 0 .../data/hooks/useIsEntityFieldMutatable.ts | 26 ++++++++++ src/frontend/views/data/portal/index.ts | 1 + src/frontend/views/data/portal/main.ts | 5 ++ .../Crud/EntityFieldsSelectionSettings.tsx | 12 ++--- src/shared/form-schemas/types.ts | 3 +- 19 files changed, 90 insertions(+), 67 deletions(-) rename src/frontend/views/data/{ => hooks}/useCanUserPerformCrudAction.ts (100%) rename src/frontend/views/data/{ => hooks}/useEntityActionButtons.ts (100%) rename src/frontend/views/data/{ => hooks}/useEntityViewStateMachine.ts (100%) create mode 100644 src/frontend/views/data/hooks/useIsEntityFieldMutatable.ts diff --git a/src/frontend/components/SchemaForm/_RenderFormInput.tsx b/src/frontend/components/SchemaForm/_RenderFormInput.tsx index d99551ed8..4aab89d42 100644 --- a/src/frontend/components/SchemaForm/_RenderFormInput.tsx +++ b/src/frontend/components/SchemaForm/_RenderFormInput.tsx @@ -1,7 +1,4 @@ import { sluggify } from "shared/lib/strings"; -import { ISchemaFormConfig } from "shared/form-schemas/types"; -import { IColorableSelection } from "shared/types/ui"; -import { FIELD_TYPES_CONFIG_MAP } from "shared/validations"; import { FormInput } from "frontend/design-system/components/Form/FormInput"; import { FormNumberInput } from "frontend/design-system/components/Form/FormNumberInput"; import { FormSelect } from "frontend/design-system/components/Form/FormSelect"; @@ -13,37 +10,23 @@ import { FormTextArea } from "frontend/design-system/components/Form/FormTextAre import { FormFileInput } from "frontend/design-system/components/Form/FormFileInput"; import { FormSelectButton } from "frontend/design-system/components/Form/FormSelectButton"; import { FormRichTextArea } from "frontend/design-system/components/Form/FormRichTextArea"; -import { FieldInputProps, FieldMetaState } from "react-final-form"; -import { ISharedFormInput } from "frontend/design-system/components/Form/_types"; +import { useExtendRenderFormInputProps } from "frontend/views/data/portal"; +import { IRenderFormInputProps } from "./types"; -interface IProps { - type: keyof typeof FIELD_TYPES_CONFIG_MAP; - renderProps: { - input: FieldInputProps; - meta: FieldMetaState; - }; - apiSelections?: ISchemaFormConfig["apiSelections"]; - entityFieldSelections?: IColorableSelection[]; - required: boolean; - disabled: boolean; - label: string; - placeholder?: string; - description?: string; - rightActions?: ISharedFormInput["rightActions"]; -} +export function RenderFormInput(props: IRenderFormInputProps) { + const { + renderProps, + label, + type, + entityFieldSelections = [], + apiSelections, + required, + disabled, + description, + placeholder, + rightActions, + } = useExtendRenderFormInputProps(props); -export function RenderFormInput({ - renderProps, - label, - type, - entityFieldSelections = [], - apiSelections, - required, - disabled, - description, - placeholder, - rightActions, -}: IProps) { const formProps = { label, required, diff --git a/src/frontend/components/SchemaForm/index.tsx b/src/frontend/components/SchemaForm/index.tsx index 4cfd2ce11..aab0093ff 100644 --- a/src/frontend/components/SchemaForm/index.tsx +++ b/src/frontend/components/SchemaForm/index.tsx @@ -115,7 +115,8 @@ export function SchemaForm>({ (validation) => validation.validationType === "required" )} - rightActions={bag?.rightActions?.(form)} + form={form} + rightActions={bag?.rightActions} placeholder={bag.placeholder} description={bag.description} apiSelections={bag.apiSelections} diff --git a/src/frontend/components/SchemaForm/types.ts b/src/frontend/components/SchemaForm/types.ts index 7a5aaa9ab..763caf19a 100644 --- a/src/frontend/components/SchemaForm/types.ts +++ b/src/frontend/components/SchemaForm/types.ts @@ -1,4 +1,28 @@ +import { FormApi } from "final-form"; +import { ISharedFormInput } from "frontend/design-system/components/Form/_types"; +import { FieldInputProps, FieldMetaState } from "react-final-form"; +import { ISchemaFormConfig } from "shared/form-schemas/types"; +import { IColorableSelection } from "shared/types/ui"; +import { FIELD_TYPES_CONFIG_MAP } from "shared/validations"; + export interface IFormExtension { fieldsState: string; beforeSubmit: string; } + +export interface IRenderFormInputProps { + type: keyof typeof FIELD_TYPES_CONFIG_MAP; + renderProps: { + input: FieldInputProps; + meta: FieldMetaState; + }; + apiSelections?: ISchemaFormConfig["apiSelections"]; + entityFieldSelections?: IColorableSelection[]; + required: boolean; + disabled: boolean; + form: FormApi; + label: string; + placeholder?: string; + description?: string; + rightActions?: ISharedFormInput["rightActions"]; +} diff --git a/src/frontend/views/Dashboard/Widget/useWidgetNavigationLink.ts b/src/frontend/views/Dashboard/Widget/useWidgetNavigationLink.ts index 8fdb7e6f4..4967d1ec1 100644 --- a/src/frontend/views/Dashboard/Widget/useWidgetNavigationLink.ts +++ b/src/frontend/views/Dashboard/Widget/useWidgetNavigationLink.ts @@ -1,7 +1,7 @@ import { SLUG_LOADING_VALUE } from "frontend/lib/routing/constants"; import { useEntityConfiguration } from "frontend/hooks/configuration/configuration.store"; import { NAVIGATION_LINKS } from "frontend/lib/routing/links"; -import { useCanUserPerformCrudAction } from "frontend/views/data/useCanUserPerformCrudAction"; +import { useCanUserPerformCrudAction } from "frontend/views/data/hooks/useCanUserPerformCrudAction"; export const useWidgetNavigationLink = (entity?: string, queryId?: string) => { const canUserPerformCrudAction = useCanUserPerformCrudAction(entity); diff --git a/src/frontend/views/data/Details/DetailsView.tsx b/src/frontend/views/data/Details/DetailsView.tsx index e699a103b..c9e4c36c2 100644 --- a/src/frontend/views/data/Details/DetailsView.tsx +++ b/src/frontend/views/data/Details/DetailsView.tsx @@ -22,7 +22,7 @@ import { import { DataStates } from "frontend/lib/data/types"; import styled from "styled-components"; import { filterOutHiddenScalarColumns } from "../utils"; -import { useEntityViewStateMachine } from "../useEntityViewStateMachine"; +import { useEntityViewStateMachine } from "../hooks/useEntityViewStateMachine"; import { viewSpecialDataTypes } from "../viewSpecialDataTypes"; import { evalutePresentationScript } from "../evaluatePresentationScript"; import { PreDataDetails } from "./portal"; diff --git a/src/frontend/views/data/Details/RelationsDetails.tsx b/src/frontend/views/data/Details/RelationsDetails.tsx index d1e37935e..bcd3f2fa7 100644 --- a/src/frontend/views/data/Details/RelationsDetails.tsx +++ b/src/frontend/views/data/Details/RelationsDetails.tsx @@ -26,7 +26,7 @@ import { ENTITY_DETAILS_VIEW_KEY } from "./constants"; import { EntityDetailsView } from "./DetailsView"; import { DetailsLayout } from "./_Layout"; import { DetailsCanvas } from "../Table/_WholeEntityTable/DetailsCanvas"; -import { useEntityActionButtons } from "../useEntityActionButtons"; +import { useEntityActionButtons } from "../hooks/useEntityActionButtons"; import { usePortalActionButtons } from "./portal"; export function EntityRelationDetails() { diff --git a/src/frontend/views/data/Details/_Layout.tsx b/src/frontend/views/data/Details/_Layout.tsx index 40cb66c44..6a2a567db 100644 --- a/src/frontend/views/data/Details/_Layout.tsx +++ b/src/frontend/views/data/Details/_Layout.tsx @@ -16,7 +16,7 @@ import { ListSkeleton } from "frontend/design-system/components/Skeleton/List"; import { SectionListItem } from "frontend/design-system/components/Section/SectionList"; import { IDropDownMenuItem } from "frontend/design-system/components/DropdownMenu"; import { DataStates } from "frontend/lib/data/types"; -import { useEntityViewStateMachine } from "../useEntityViewStateMachine"; +import { useEntityViewStateMachine } from "../hooks/useEntityViewStateMachine"; import { getEntitiesRelationsCount } from "./utils"; import { EntityActionTypes, diff --git a/src/frontend/views/data/Details/index.tsx b/src/frontend/views/data/Details/index.tsx index d8f95f16d..c3a0bad7f 100644 --- a/src/frontend/views/data/Details/index.tsx +++ b/src/frontend/views/data/Details/index.tsx @@ -13,7 +13,7 @@ import { EntityDetailsView } from "./DetailsView"; import { DetailsLayout, DETAILS_LAYOUT_KEY } from "./_Layout"; import { DetailsCanvas } from "../Table/_WholeEntityTable/DetailsCanvas"; import { useDetailsViewMenuItems, usePortalActionButtons } from "./portal"; -import { useEntityActionButtons } from "../useEntityActionButtons"; +import { useEntityActionButtons } from "../hooks/useEntityActionButtons"; export function EntityDetails() { const entityId = useEntityId(); diff --git a/src/frontend/views/data/Table/useTableColumns.tsx b/src/frontend/views/data/Table/useTableColumns.tsx index 9ca3d3eee..6a5a1a8dd 100644 --- a/src/frontend/views/data/Table/useTableColumns.tsx +++ b/src/frontend/views/data/Table/useTableColumns.tsx @@ -26,7 +26,7 @@ import { filterOutHiddenScalarColumns } from "../utils"; import { viewSpecialDataTypes } from "../viewSpecialDataTypes"; import { usePortalTableColumns } from "./portal"; import { evalutePresentationScript } from "../evaluatePresentationScript"; -import { useEntityActionButtons } from "../useEntityActionButtons"; +import { useEntityActionButtons } from "../hooks/useEntityActionButtons"; import { usePortalActionButtons } from "../Details/portal"; export const ACTIONS_ACCESSOR = "__actions__"; diff --git a/src/frontend/views/data/Table/useTableMenuItems.ts b/src/frontend/views/data/Table/useTableMenuItems.ts index 1ae6ad0ed..5bb5e16ed 100644 --- a/src/frontend/views/data/Table/useTableMenuItems.ts +++ b/src/frontend/views/data/Table/useTableMenuItems.ts @@ -5,7 +5,7 @@ import { Plus } from "react-feather"; import { SLUG_LOADING_VALUE } from "frontend/lib/routing/constants"; import { IDropDownMenuItem } from "frontend/design-system/components/DropdownMenu"; import { usePluginTableMenuItems } from "./portal"; -import { useCanUserPerformCrudAction } from "../useCanUserPerformCrudAction"; +import { useCanUserPerformCrudAction } from "../hooks/useCanUserPerformCrudAction"; export const useTableMenuItems = ( entity: string, diff --git a/src/frontend/views/data/_BaseEntityForm.tsx b/src/frontend/views/data/_BaseEntityForm.tsx index 84c158269..9982d1669 100644 --- a/src/frontend/views/data/_BaseEntityForm.tsx +++ b/src/frontend/views/data/_BaseEntityForm.tsx @@ -3,10 +3,7 @@ import { FormSkeletonSchema, } from "frontend/design-system/components/Skeleton/Form"; import { SchemaForm } from "frontend/components/SchemaForm"; -import { - useAppConfiguration, - useEntityConfiguration, -} from "frontend/hooks/configuration/configuration.store"; +import { useEntityConfiguration } from "frontend/hooks/configuration/configuration.store"; import { useEntityFields, useEntityToOneReferenceFields, @@ -24,9 +21,10 @@ import { DataStateKeys, DataStates } from "frontend/lib/data/types"; import { SLUG_LOADING_VALUE } from "frontend/lib/routing/constants"; import { ButtonIconTypes } from "frontend/design-system/components/Button/constants"; import { buildAppliedSchemaFormConfig } from "./buildAppliedSchemaFormConfig"; -import { useEntityViewStateMachine } from "./useEntityViewStateMachine"; +import { useEntityViewStateMachine } from "./hooks/useEntityViewStateMachine"; import { filterOutHiddenScalarColumns } from "./utils"; import { usePortalExtendEntityFormConfig } from "./portal"; +import { useIsEntityFieldMutatable } from "./hooks/useIsEntityFieldMutatable"; type IProps = { entity: string; @@ -65,14 +63,14 @@ export function BaseEntityForm({ crudAction ); + const isEntityFieldMutatable = useIsEntityFieldMutatable(crudAction); + const entityFormExtension = useEntityConfiguration( "entity_form_extension", entity ); const entityToOneReferenceFields = useEntityToOneReferenceFields(entity); - const metaDataColumns = useAppConfiguration("metadata_columns"); - const error = entityFieldTypesMap.error || hiddenColumns.error || @@ -99,15 +97,7 @@ export function BaseEntityForm({ }); const fields = filterOutHiddenScalarColumns( - entityFields.data - .filter(({ isId }) => !isId) - .filter( - ({ name }) => - ![ - metaDataColumns.data.createdAt, - metaDataColumns.data.updatedAt, - ].includes(name) - ), + entityFields.data.filter(isEntityFieldMutatable), hiddenColumns.data ).map(({ name }) => name); diff --git a/src/frontend/views/data/useCanUserPerformCrudAction.ts b/src/frontend/views/data/hooks/useCanUserPerformCrudAction.ts similarity index 100% rename from src/frontend/views/data/useCanUserPerformCrudAction.ts rename to src/frontend/views/data/hooks/useCanUserPerformCrudAction.ts diff --git a/src/frontend/views/data/useEntityActionButtons.ts b/src/frontend/views/data/hooks/useEntityActionButtons.ts similarity index 100% rename from src/frontend/views/data/useEntityActionButtons.ts rename to src/frontend/views/data/hooks/useEntityActionButtons.ts diff --git a/src/frontend/views/data/useEntityViewStateMachine.ts b/src/frontend/views/data/hooks/useEntityViewStateMachine.ts similarity index 100% rename from src/frontend/views/data/useEntityViewStateMachine.ts rename to src/frontend/views/data/hooks/useEntityViewStateMachine.ts diff --git a/src/frontend/views/data/hooks/useIsEntityFieldMutatable.ts b/src/frontend/views/data/hooks/useIsEntityFieldMutatable.ts new file mode 100644 index 000000000..92f6ca67a --- /dev/null +++ b/src/frontend/views/data/hooks/useIsEntityFieldMutatable.ts @@ -0,0 +1,26 @@ +import { useAppConfiguration } from "frontend/hooks/configuration/configuration.store"; +import { IEntityCrudSettings } from "shared/configurations"; +import { IEntityField } from "shared/types/db"; + +export const useIsEntityFieldMutatable = ( + crudKey: keyof IEntityCrudSettings | "table" +) => { + const metaDataColumns = useAppConfiguration("metadata_columns"); + + return (entityField: IEntityField) => { + if (entityField.isId) { + return false; + } + + if ( + (crudKey === "create" || crudKey === "update") && + [metaDataColumns.data.createdAt, metaDataColumns.data.updatedAt].includes( + entityField.name + ) + ) { + return false; + } + + return true; + }; +}; diff --git a/src/frontend/views/data/portal/index.ts b/src/frontend/views/data/portal/index.ts index 9060c4dd8..15060a9ee 100644 --- a/src/frontend/views/data/portal/index.ts +++ b/src/frontend/views/data/portal/index.ts @@ -1,4 +1,5 @@ export { usePortalExtendEntityFormConfig, PortalEntityFormComponent, + useExtendRenderFormInputProps, } from "./main"; diff --git a/src/frontend/views/data/portal/main.ts b/src/frontend/views/data/portal/main.ts index b229db61a..8726adbbc 100644 --- a/src/frontend/views/data/portal/main.ts +++ b/src/frontend/views/data/portal/main.ts @@ -1,3 +1,4 @@ +import { IRenderFormInputProps } from "frontend/components/SchemaForm/types"; import { IAppliedSchemaFormConfig } from "shared/form-schemas/types"; import { noop } from "shared/lib/noop"; @@ -20,3 +21,7 @@ export const usePortalExtendEntityFormConfig = ( export function PortalEntityFormComponent() { return null; } + +export const useExtendRenderFormInputProps = (props: IRenderFormInputProps) => { + return props; +}; diff --git a/src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx b/src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx index 986a61e7a..4ec7a341a 100644 --- a/src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx +++ b/src/frontend/views/entity/Crud/EntityFieldsSelectionSettings.tsx @@ -13,7 +13,7 @@ import { useEntitySlug, } from "frontend/hooks/entity/entity.config"; import { IEntityCrudSettings } from "shared/configurations"; -import { useAppConfiguration } from "frontend/hooks/configuration/configuration.store"; +import { useIsEntityFieldMutatable } from "frontend/views/data/hooks/useIsEntityFieldMutatable"; import { ENTITY_CRUD_LABELS } from "../constants"; import { makeEntityFieldsSelectionKey } from "./constants"; @@ -44,7 +44,7 @@ export function EntityFieldsSelectionSettings({ const getEntityFieldLabels = useEntityFieldLabels(entity); - const metaDataColumns = useAppConfiguration("metadata_columns"); + const isEntityFieldMutatable = useIsEntityFieldMutatable(crudKey); const { toggleSelection, allSelections, selectMutiple, isSelected } = useStringSelections(makeEntityFieldsSelectionKey(entity, crudKey)); @@ -86,13 +86,7 @@ export function EntityFieldsSelectionSettings({ const isHidden = isSelected(menuItem.name); const disabled = - menuItem.isId || - !toggling.enabled || - ((crudKey === "create" || crudKey === "update") && - [ - metaDataColumns.data.createdAt, - metaDataColumns.data.updatedAt, - ].includes(menuItem.name)); + !isEntityFieldMutatable(menuItem) || !toggling.enabled; return ( ISharedFormInput["rightActions"]; + rightActions?: ISharedFormInput["rightActions"]; validations: IFieldValidationItem[]; }