Skip to content

Commit

Permalink
✨ feat(form-settings): add default values for forms
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Dec 6, 2023
1 parent 9b461e3 commit 688c095
Show file tree
Hide file tree
Showing 25 changed files with 215 additions and 48 deletions.
2 changes: 0 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
name: CodeQL

on:
push:
pull_request:
schedule:
- cron: "30 1 * * 0"

Expand Down
37 changes: 21 additions & 16 deletions src/backend/entities/entities.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,31 +132,37 @@ export class EntitiesApiService implements IApplicationService {
}));
}

async getEntityValidRelations(
entity: string
): Promise<IDBSchema["relations"]> {
const [entityRelations, disabledEntities, hiddenEntity] = await Promise.all(
[
this.getEntityRelations(entity),
this._configurationApiService.show("disabled_entities"),
this._configurationApiService.show("hidden_entity_relations", entity),
]
);

return entityRelations.filter(
({ table }) =>
!disabledEntities.includes(table) && !hiddenEntity.includes(table)
);
}

async getEntityRelationsForUserRole(
entity: string,
userRole: string
): Promise<IEntityRelation[]> {
const [
entityRelations,
disabledEntities,
entityLabels,
entityOrders,
hiddenEntity,
] = await Promise.all([
this.getEntityRelations(entity),
this._configurationApiService.show("disabled_entities"),
const [validRelations, entityLabels, entityOrders] = await Promise.all([
this.getEntityValidRelations(entity),
this._configurationApiService.show("entity_relations_labels", entity),
this._configurationApiService.show("entity_relations_order", entity),
this._configurationApiService.show("hidden_entity_relations", entity),
]);

const allowedEntityRelation =
await this._rolesApiService.filterPermittedEntities(
userRole,
entityRelations.filter(
({ table }) =>
!disabledEntities.includes(table) && !hiddenEntity.includes(table)
),
validRelations,
"table"
);

Expand All @@ -173,8 +179,7 @@ export class EntitiesApiService implements IApplicationService {
table: relation.table,
label: entityLabels[relation.table],
type,
field:
type === "toOne" ? relation?.joinColumnOptions?.[0].name : undefined,
field: relation?.joinColumnOptions?.[0].name,
inverseToOneField:
relation?.joinColumnOptions?.[0].tag === "inverse"
? relation?.joinColumnOptions?.[0].referencedColumnName
Expand Down
13 changes: 11 additions & 2 deletions src/frontend/components/SchemaForm/_RenderFormInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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 { ISharedFormInput } from "frontend/design-system/components/Form/_types";
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";
Expand All @@ -14,15 +13,21 @@ 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";

interface IProps {
type: keyof typeof FIELD_TYPES_CONFIG_MAP;
renderProps: ISharedFormInput;
renderProps: {
input: FieldInputProps<any, HTMLElement>;
meta: FieldMetaState<any>;
};
apiSelections?: ISchemaFormConfig["apiSelections"];
entityFieldSelections?: IColorableSelection[];
required: boolean;
disabled: boolean;
label: string;
placeholder?: string;
description?: string;
}

export function RenderFormInput({
Expand All @@ -33,11 +38,15 @@ export function RenderFormInput({
apiSelections,
required,
disabled,
description,
placeholder,
}: IProps) {
const formProps = {
label,
required,
disabled,
placeholder,
description,
...renderProps,
};

Expand Down
2 changes: 2 additions & 0 deletions src/frontend/components/SchemaForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ export function SchemaForm<T extends Record<string, unknown>>({
required={bag.validations.some(
(validation) => validation.validationType === "required"
)}
placeholder={bag.placeholder}
description={bag.description}
apiSelections={bag.apiSelections}
label={bag.label || userFriendlyCase(field)}
entityFieldSelections={bag.selections}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const FormCodeEditor: React.FC<IFormCodeEditor> = (formInput) => {
highlight(code, languages[formInput.language || "javascript"])
}
disabled={formInput.disabled}
placeholder={formInput.placeholder}
textareaId={formInput.input.name}
padding={4}
style={{
Expand Down
1 change: 1 addition & 0 deletions src/frontend/design-system/components/Form/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface ISharedFormInput {
meta: FieldMetaState<any>;
label?: string;
description?: string;
placeholder?: string;
required?: boolean;
disabled?: boolean;
sm?: true;
Expand Down
24 changes: 21 additions & 3 deletions src/frontend/docs/scripts/form-scripts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export function FormScriptDocumentation(props: IDocumentationRootProps) {
can&apos;t make Promises or make network calls.{" "}
</p>
<p>
We have two tabs where which do different things so let&apos;s start
We have three tabs where which do different things so let&apos;s start
with the first
</p>
<h4>1. Field State</h4>
<h3>1. Field State</h3>
<p>
This allows you to hide or disable your form fields. Let&apos;s dive
straight into examples
Expand Down Expand Up @@ -132,7 +132,7 @@ return {
the database field name which is <code>accountBalance</code> any other
label will not work.{" "}
</p>
<h4>2. Before Submit</h4>
<h3>2. Before Submit</h3>
<p>This tab enables you to do two different things.</p>
<h5>1. Run custom validation</h5>
<p>
Expand Down Expand Up @@ -240,6 +240,24 @@ return {
createdById: JSON.parse($.auth.systemProfile).userId
}`}
/>

<h3>3. Initial Values</h3>
<p>
This tab simply allows you to set the initial values for the create form
</p>
<RenderCode
input={`/**
* Will the follow values as intial values on the create form
*/
return {
price: 1000,
status: "new",
country: "US",
isApproved: true
}`}
/>
<p>You will not have access to any variables here, so no `$.anything`</p>
</DocumentationRoot>
);
}
4 changes: 4 additions & 0 deletions src/frontend/lib/selection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export function useStringSelections(key: string) {
const update = Object.fromEntries(items.map((item) => [item, true]));
setSelections({ ...selections, ...update });
},
setMultiple: (items: string[]) => {
const update = Object.fromEntries(items.map((item) => [item, true]));
setSelections(update);
},
deSelectMutiple: (items: string[]) => {
const update = Object.fromEntries(items.map((item) => [item, false]));

Expand Down
16 changes: 16 additions & 0 deletions src/frontend/lib/selection/selection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,22 @@ describe("useStringSelections", () => {
]);
});

it("should select multiple", async () => {
const { result, rerender } = renderHook(() => useStringSelections(""));

result.current.setMultiple(["foo", "bar"]);

rerender();

expect(result.current.allSelections).toEqual(["foo", "bar"]);

result.current.setMultiple(["baz", "quz", "foo"]);

rerender();

expect(result.current.allSelections).toEqual(["baz", "quz", "foo"]);
});

it("should deSelectMutiple multiple", async () => {
const { result, rerender } = renderHook(() => useStringSelections(""));

Expand Down
30 changes: 28 additions & 2 deletions src/frontend/views/data/Create/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,29 @@ import {
} 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;
};

export function EntityCreate() {
const routeParams = useRouteParams();
const entity = useEntitySlug();
Expand All @@ -26,7 +43,7 @@ export function EntityCreate() {

const actionItems = useEntityActionMenuItems([
EntityActionTypes.Create,
EntityActionTypes.Types,
EntityActionTypes.Form,
]);

useSetPageDetails({
Expand All @@ -40,6 +57,15 @@ export function EntityCreate() {

const { backLink } = useNavigationStack();

const entityFormExtension = useEntityConfiguration(
"entity_form_extension",
entity
);

const scriptInitialValues = runInitialValuesScript(
entityFormExtension.data.initialValues
);

return (
<AppLayout actionItems={actionItems}>
<ContentLayout.Center>
Expand All @@ -53,7 +79,7 @@ export function EntityCreate() {
crudAction="create"
resetForm
buttonText={entityCrudConfig.FORM_LANG.CREATE}
initialValues={routeParams}
initialValues={{ ...scriptInitialValues, ...routeParams }}
onSubmit={entityDataCreationMutation.mutateAsync}
hiddenColumns={hiddenCreateColumns}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/views/data/Details/_Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export function DetailsLayout({
const actionItems = useEntityActionMenuItems(
[
EntityActionTypes.Details,
EntityActionTypes.Types,
EntityActionTypes.Form,
EntityActionTypes.Labels,
],
childEntity
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/views/data/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function EntityTable() {
EntityActionTypes.Table,
EntityActionTypes.Diction,
EntityActionTypes.Labels,
EntityActionTypes.Types,
EntityActionTypes.Form,
]);

useSetPageDetails({
Expand Down
4 changes: 2 additions & 2 deletions src/frontend/views/data/Update/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function EntityUpdate() {

const actionItems = useEntityActionMenuItems([
EntityActionTypes.Update,
EntityActionTypes.Types,
EntityActionTypes.Form,
]);

useSetPageDetails({
Expand All @@ -58,7 +58,7 @@ export function EntityUpdate() {
title={entityCrudConfig.TEXT_LANG.EDIT}
description={
userHasPermission(USER_PERMISSIONS.CAN_CONFIGURE_APP)
? "For security reasons, Any data that is hidden in details view will not show up here, So rememeber to toggle on all fields there if you want to update them here"
? `For security reasons, Any data that is hidden in details view will not show up here, So rememeber to toggle on all fields there if you want to update them here`
: undefined
}
backLink={backLink}
Expand Down
5 changes: 0 additions & 5 deletions src/frontend/views/entity/Actions/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,6 @@ export function ActionForm({
})),
},
...selectedImplementation,
// TODO: Actions script i.e return false or return a loop to do many users
// triggerLogic: {
// type: "json",
// validations: [],
// },
};
if (currentView.type === "entity") {
delete fields.entity;
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/views/entity/Fields/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export function EntityFieldsSettings() {
/>
</ViewStateMachine>
),
label: ENTITY_FIELD_SETTINGS_TAB_LABELS.TYPES,
label: ENTITY_FIELD_SETTINGS_TAB_LABELS.FORM,
},
{
content: (
Expand Down
3 changes: 3 additions & 0 deletions src/frontend/views/entity/Form/ScriptForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface IProps {
value: string;
onSubmit: (value: string) => Promise<void>;
isLoading: boolean;
placeholder: string;
field: string;
error?: unknown;
}
Expand All @@ -25,6 +26,7 @@ export function ScriptForm({
onSubmit,
field,
error,
placeholder,
isLoading,
}: IProps) {
const scriptContext = useSchemaFormScriptContext("test");
Expand All @@ -41,6 +43,7 @@ export function ScriptForm({
type: "json",
label: "Script",
validations: [],
placeholder,
},
}}
onSubmit={async (data) => {
Expand Down
Loading

0 comments on commit 688c095

Please sign in to comment.