Skip to content

Commit

Permalink
✨ feat(entity-forms): implement right actions on schema form
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 25, 2023
1 parent db6746c commit b4bc92d
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 46 deletions.
4 changes: 4 additions & 0 deletions src/frontend/components/SchemaForm/_RenderFormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { FormFileInput } from "frontend/design-system/components/Form/FormFileIn
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";

interface IProps {
type: keyof typeof FIELD_TYPES_CONFIG_MAP;
Expand All @@ -28,6 +29,7 @@ interface IProps {
label: string;
placeholder?: string;
description?: string;
rightActions?: ISharedFormInput["rightActions"];
}

export function RenderFormInput({
Expand All @@ -40,13 +42,15 @@ export function RenderFormInput({
disabled,
description,
placeholder,
rightActions,
}: IProps) {
const formProps = {
label,
required,
disabled,
placeholder: placeholder || label,
description,
rightActions,
...renderProps,
};

Expand Down
1 change: 1 addition & 0 deletions src/frontend/components/SchemaForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function SchemaForm<T extends Record<string, unknown>>({
(validation) =>
validation.validationType === "required"
)}
rightActions={bag?.rightActions?.(form)}
placeholder={bag.placeholder}
description={bag.description}
apiSelections={bag.apiSelections}
Expand Down
28 changes: 20 additions & 8 deletions src/frontend/hooks/data/data.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,20 +137,32 @@ export const useEntityDataReference = (entity: string, entityId: string) => {
defaultData: "",
});
};
export function useEntityDataCreationMutation(entity: string) {
export function useEntityDataCreationMutation(
entity: string,
option?: {
hideSuccessMessage?: boolean;
onSuccessActionWithFormData?: (id: string) => void;
}
) {
const entityCrudConfig = useEntityCrudConfig(entity);
const router = useRouter();
const apiMutateOptions = useWaitForResponseMutationOptions<
Record<string, string>
>({
endpoints: DATA_MUTATION_ENDPOINTS_TO_CLEAR(entity),
smartSuccessMessage: ({ id }) => ({
message: entityCrudConfig.MUTATION_LANG.CREATE,
action: {
label: entityCrudConfig.MUTATION_LANG.VIEW_DETAILS,
action: () => router.push(NAVIGATION_LINKS.ENTITY.DETAILS(entity, id)),
},
}),
onSuccessActionWithFormData: ({ id }) => {
option?.onSuccessActionWithFormData(id);
},
smartSuccessMessage: option?.hideSuccessMessage
? undefined
: ({ id }) => ({
message: entityCrudConfig.MUTATION_LANG.CREATE,
action: {
label: entityCrudConfig.MUTATION_LANG.VIEW_DETAILS,
action: () =>
router.push(NAVIGATION_LINKS.ENTITY.DETAILS(entity, id)),
},
}),
});

return useMutation(
Expand Down
30 changes: 9 additions & 21 deletions src/frontend/views/data/Create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,17 @@ import { AppLayout } from "frontend/_layouts/app";
import {
useEntityCrudConfig,
useEntitySlug,
useHiddenEntityColumns,
} from "frontend/hooks/entity/entity.config";
import { useEntityDataCreationMutation } from "frontend/hooks/data/data.store";
import { useRouteParams } from "frontend/lib/routing/useRouteParam";
import { useEntityConfiguration } from "frontend/hooks/configuration/configuration.store";
import { evalJavascriptStringSafely } from "frontend/lib/script-runner";
import {
EntityActionTypes,
useEntityActionMenuItems,
} from "../../entity/constants";
import { BaseEntityForm } from "../_BaseEntityForm";

const runInitialValuesScript = (
initialValuesScript: string
): Record<string, unknown> => {
if (!initialValuesScript) {
return {};
}

const response = evalJavascriptStringSafely<{}>(initialValuesScript, {});

if (typeof response !== "object") {
return {};
}
return response;
};
import { runInitialValuesScript } from "./run-initial-scripts";
import { PortalEntityFormComponent } from "../portal";

export function EntityCreate() {
const routeParams = useRouteParams();
Expand All @@ -53,8 +38,6 @@ export function EntityCreate() {
permission: META_USER_PERMISSIONS.NO_PERMISSION_REQUIRED,
});

const hiddenCreateColumns = useHiddenEntityColumns("create", entity);

const { backLink } = useNavigationStack();

const entityFormExtension = useEntityConfiguration(
Expand All @@ -79,12 +62,17 @@ export function EntityCreate() {
crudAction="create"
resetForm
buttonText={entityCrudConfig.FORM_LANG.CREATE}
initialValues={{ ...scriptInitialValues, ...routeParams }}
initialValuesData={{
data: { ...scriptInitialValues, ...routeParams },
error: entityFormExtension.error,
isLoading: entityFormExtension.isLoading,
isRefetching: false,
}}
onSubmit={entityDataCreationMutation.mutateAsync}
hiddenColumns={hiddenCreateColumns}
/>
</SectionBox>
</ContentLayout.Center>
<PortalEntityFormComponent />
</AppLayout>
);
}
16 changes: 16 additions & 0 deletions src/frontend/views/data/Create/run-initial-scripts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { evalJavascriptStringSafely } from "frontend/lib/script-runner";

export const runInitialValuesScript = (
initialValuesScript: string
): Record<string, unknown> => {
if (!initialValuesScript) {
return {};
}

const response = evalJavascriptStringSafely<{}>(initialValuesScript, {});

if (typeof response !== "object") {
return {};
}
return response;
};
9 changes: 3 additions & 6 deletions src/frontend/views/data/Update/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
useEntityCrudConfig,
useEntityId,
useEntitySlug,
useHiddenEntityColumns,
} from "frontend/hooks/entity/entity.config";
import {
useEntityDataDetails,
Expand All @@ -21,6 +20,7 @@ import {
} from "../../entity/constants";
import { BaseEntityForm } from "../_BaseEntityForm";
import { useDataUpdateActions } from "./portal";
import { PortalEntityFormComponent } from "../portal";

export function EntityUpdate() {
const entityId = useEntityId();
Expand Down Expand Up @@ -49,8 +49,6 @@ export function EntityUpdate() {

const userHasPermission = useUserHasPermission();

const hiddenUpdateColumns = useHiddenEntityColumns("update", entity);

const dataDetails = useEntityDataDetails({ entity, entityId });

const { backLink } = useNavigationStack();
Expand All @@ -73,12 +71,11 @@ export function EntityUpdate() {
icon="save"
buttonText={entityCrudConfig.FORM_LANG.UPDATE}
onSubmit={entityDataUpdationMutation.mutateAsync}
hiddenColumns={hiddenUpdateColumns}
initialValues={dataDetails.data}
additionalDataState={dataDetails}
initialValuesData={dataDetails}
/>
</SectionBox>
</ContentLayout.Center>
<PortalEntityFormComponent />
</AppLayout>
);
}
19 changes: 9 additions & 10 deletions src/frontend/views/data/_BaseEntityForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
useEntityFieldSelections,
useProcessedEntityFieldTypes,
useEntityFieldValidations,
useHiddenEntityColumns,
} from "frontend/hooks/entity/entity.config";
import { useMemo } from "react";
import { ViewStateMachine } from "frontend/components/ViewStateMachine";
Expand All @@ -29,10 +30,8 @@ import { usePortalExtendEntityFormConfig } from "./portal";

type IProps = {
entity: string;
initialValues?: Record<string, unknown>;
initialValuesData?: DataStateKeys<Record<string, unknown>>;
crudAction: "create" | "update";
hiddenColumns: DataStateKeys<string[]>;
additionalDataState?: DataStateKeys<unknown>;
allOptional?: boolean;
onSubmit: (data: Record<string, string>) => Promise<void>;
resetForm?: true;
Expand All @@ -42,14 +41,12 @@ type IProps = {

export function BaseEntityForm({
entity,
initialValues,
initialValuesData,
crudAction,
allOptional,
icon,
resetForm,
buttonText,
additionalDataState,
hiddenColumns,
onSubmit,
}: IProps) {
const entityValidationsMap = useEntityFieldValidations(entity);
Expand All @@ -61,6 +58,7 @@ export function BaseEntityForm({
"entity_columns_types",
entity
);
const hiddenColumns = useHiddenEntityColumns(crudAction, entity);

const extendEntityFormConfig = usePortalExtendEntityFormConfig(
entity,
Expand All @@ -78,7 +76,7 @@ export function BaseEntityForm({
const error =
entityFieldTypesMap.error ||
hiddenColumns.error ||
additionalDataState?.error ||
initialValuesData?.error ||
entityFormExtension.error ||
entityToOneReferenceFields.error ||
entityFields.error;
Expand All @@ -91,7 +89,7 @@ export function BaseEntityForm({
entity === SLUG_LOADING_VALUE ||
entityFieldTypesMap.isLoading ||
extendEntityFormConfig === "loading" ||
additionalDataState?.isLoading;
initialValuesData?.isLoading;

const viewState = useEntityViewStateMachine({
isLoading,
Expand All @@ -114,6 +112,7 @@ export function BaseEntityForm({
).map(({ name }) => name);

const fieldsInitialValues = useMemo(() => {
const initialValues = initialValuesData?.data;
if (!initialValues) {
return initialValues;
}
Expand All @@ -128,7 +127,7 @@ export function BaseEntityForm({
return [field, value];
})
);
}, [initialValues, hiddenColumns]);
}, [initialValuesData, hiddenColumns]);

const formSchemaConfig = {
entityToOneReferenceFields: entityToOneReferenceFields.data,
Expand Down Expand Up @@ -169,7 +168,7 @@ export function BaseEntityForm({
icon={icon}
initialValues={fieldsInitialValues}
fields={
typeof extendEntityFormConfig === "string"
extendEntityFormConfig === "loading"
? formConfig
: extendEntityFormConfig(formConfig)
}
Expand Down
1 change: 1 addition & 0 deletions src/frontend/views/data/buildAppliedSchemaFormConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const buildAppliedSchemaFormConfig = (
entityFieldTypes[field] === "reference"
? {
listUrl: ENTITY_LIST_PATH(entityToOneReferenceFields[field]),
entity: entityToOneReferenceFields[field],
referenceUrl: (value: string) =>
ENTITY_REFERENCE_PATH({
entity: entityToOneReferenceFields[field],
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/views/data/portal/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
export { usePortalExtendEntityFormConfig } from "./main";
export {
usePortalExtendEntityFormConfig,
PortalEntityFormComponent,
} from "./main";
4 changes: 4 additions & 0 deletions src/frontend/views/data/portal/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,7 @@ export const usePortalExtendEntityFormConfig = (
return formConfig;
};
};

export function PortalEntityFormComponent() {
return null;
}
4 changes: 4 additions & 0 deletions src/shared/form-schemas/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FormApi } from "final-form";
import { ISharedFormInput } from "frontend/design-system/components/Form/_types";
import { GridSpanSizes, IColorableSelection } from "shared/types/ui";
import { FIELD_TYPES_CONFIG_MAP } from "shared/validations";
import { IFieldValidationItem } from "shared/validations/types";
Expand All @@ -6,13 +8,15 @@ export interface ISchemaFormConfig {
selections?: IColorableSelection[];
apiSelections?: {
listUrl: string;
entity?: string;
referenceUrl?: (value: string) => string;
};
type: keyof typeof FIELD_TYPES_CONFIG_MAP;
label?: string;
placeholder?: string;
description?: string;
span?: GridSpanSizes;
rightActions?: (form: FormApi) => ISharedFormInput["rightActions"];
validations: IFieldValidationItem[];
}

Expand Down

0 comments on commit b4bc92d

Please sign in to comment.