diff --git a/src/qqq/components/EntityForm/index.tsx b/src/qqq/components/EntityForm/index.tsx
index 97a6a2f..77f4d5e 100644
--- a/src/qqq/components/EntityForm/index.tsx
+++ b/src/qqq/components/EntityForm/index.tsx
@@ -1,26 +1,16 @@
-/**
- =========================================================
- * Material Dashboard 2 PRO React TS - v1.0.0
- =========================================================
-
- * Product Page: https://www.creative-tim.com/product/material-dashboard-2-pro-react-ts
- * Copyright 2022 Creative Tim (https://www.creative-tim.com)
-
- Coded by www.creative-tim.com
-
- =========================================================
-
- * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
- */
-
-// react imports
+// react components
import { useParams } from "react-router-dom";
import React, { useReducer, useState } from "react";
-// qqq imports
+// misc components
+import * as Yup from "yup";
+import { Form, Formik } from "formik";
+
+// qqq components
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
-import { QRecord } from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import { QFieldType } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
+import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
+import QDynamicForm from "qqq/components/QDynamicForm";
+import { QFieldMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
// @material-ui core components
import Card from "@mui/material/Card";
@@ -29,7 +19,6 @@ import Grid from "@mui/material/Grid";
// Material Dashboard 2 PRO React TS components
import MDBox from "components/MDBox";
import MDTypography from "components/MDTypography";
-import FormField from "layouts/pages/account/components/FormField";
import MDButton from "../../../components/MDButton";
// Declaring props types for EntityForm
@@ -41,17 +30,24 @@ function EntityForm({ id }: Props): JSX.Element {
const qController = new QController("");
const { tableName } = useParams();
+ const [validations, setValidations] = useState({});
+ const [initialValues, setInitialValues] = useState({} as { [key: string]: string });
+ const [formFields, setFormFields] = useState({});
+
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
const [formValues, setFormValues] = useState({} as { [key: string]: string });
- const [formFields, setFormFields] = useState([] as JSX.Element[]);
const [tableMetaData, setTableMetaData] = useState(null);
const [, forceUpdate] = useReducer((x) => x + 1, 0);
- const handleInputChange = (e: { target: { name: any; value: any } }) => {
- const { name, value } = e.target;
- formValues[name] = value;
- setFormValues(formValues);
- };
+ function getDynamicStepContent(formData: any): JSX.Element {
+ const { formFields, values, errors, touched } = formData;
+
+ if (!Object.keys(formFields).length) {
+ return
Loading...
;
+ }
+
+ return ;
+ }
if (!asyncLoadInited) {
setAsyncLoadInited(true);
@@ -59,73 +55,46 @@ function EntityForm({ id }: Props): JSX.Element {
const tableMetaData = await qController.loadTableMetaData(tableName);
setTableMetaData(tableMetaData);
+ const fieldArray = [] as QFieldMetaData[];
+ const sortedKeys = [...tableMetaData.fields.keys()].sort();
+ sortedKeys.forEach((key) => {
+ const fieldMetaData = tableMetaData.fields.get(key);
+ fieldArray.push(fieldMetaData);
+ });
+
if (id !== null) {
- const foundRecord = await qController.get(tableName, id);
+ const record = await qController.get(tableName, id);
tableMetaData.fields.forEach((fieldMetaData, key) => {
- formValues[key] = foundRecord.values.get(key);
+ initialValues[key] = record.values.get(key);
});
setFormValues(formValues);
}
- const sortedKeys = [...tableMetaData.fields.keys()].sort();
- sortedKeys.forEach((key) => {
- const fieldMetaData = tableMetaData.fields.get(key);
- if (fieldMetaData.name !== tableMetaData.primaryKeyField) {
- let fieldType: string;
- switch (fieldMetaData.type.toString()) {
- case QFieldType.DECIMAL:
- case QFieldType.INTEGER:
- fieldType = "number";
- break;
- case QFieldType.DATE_TIME:
- fieldType = "datetime-local";
- break;
- case QFieldType.PASSWORD:
- case QFieldType.TIME:
- case QFieldType.DATE:
- fieldType = fieldMetaData.type.toString();
- break;
- case QFieldType.TEXT:
- case QFieldType.HTML:
- case QFieldType.STRING:
- default:
- fieldType = "text";
- }
+ const { dynamicFormFields, formValidations } = DynamicFormUtils.getFormData(fieldArray);
+ setInitialValues(initialValues);
+ setFormFields(dynamicFormFields);
+ setValidations(Yup.object().shape(formValidations));
- formFields.push(
-
-
-
- );
- }
- });
-
- setFormFields(formFields);
forceUpdate();
})();
}
- const handleSubmit = (event: { preventDefault: () => void }) => {
- event.preventDefault();
-
- (async () => {
+ const handleSubmit = async (values: any, actions: any) => {
+ actions.setSubmitting(true);
+ await (async () => {
if (id !== null) {
- await qController.update(tableName, id, formValues).then((record) => {
- window.location.href = `/${tableName}/view/${record.values.get("id")}`; // todo - primaryKeyField
+ await qController.update(tableName, id, values).then((record) => {
+ window.location.href = `/${tableName}/${record.values.get(
+ tableMetaData.primaryKeyField
+ )}`;
});
} else {
- await qController.create(tableName, formValues).then((record) => {
- window.location.href = `/${tableName}/view/${record.values.get("id")}`; // todo - primaryKeyField
+ await qController.create(tableName, values).then((record) => {
+ window.location.href = `/${tableName}/${record.values.get(
+ tableMetaData.primaryKeyField
+ )}`;
});
}
})();
@@ -133,24 +102,46 @@ function EntityForm({ id }: Props): JSX.Element {
const pageTitle =
id != null ? `Edit ${tableMetaData?.label} (${id})` : `Create New ${tableMetaData?.label}`;
+ const formId =
+ id != null ? `edit-${tableMetaData?.label}-form` : `create-${tableMetaData?.label}-form`;
return (
-
+
{pageTitle}
-
-
- {formFields}
-
-
-
-
-
-
- save {tableMetaData?.label}
-
-
+
+
+
+ {({ values, errors, touched, isSubmitting }) => (
+
+ )}
+
diff --git a/src/qqq/components/QDynamicForm/index.tsx b/src/qqq/components/QDynamicForm/index.tsx
index 0119315..ce5c695 100644
--- a/src/qqq/components/QDynamicForm/index.tsx
+++ b/src/qqq/components/QDynamicForm/index.tsx
@@ -25,29 +25,30 @@ import FormField from "layouts/pages/users/new-user/components/FormField";
import { QFrontendStepMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData";
interface Props {
+ formLabel?: string;
formData: any;
- step?: QFrontendStepMetaData | undefined;
+ primaryKeyId?: string;
}
function QDynamicForm(props: Props): JSX.Element {
- const { formData, step } = props;
+ const { formData, formLabel, primaryKeyId } = props;
const { formFields, values, errors, touched } = formData;
/*
- const {
- firstName: firstNameV,
- lastName: lastNameV,
- company: companyV,
- email: emailV,
- password: passwordV,
- repeatPassword: repeatPasswordV,
- } = values;
- */
+ const {
+ firstName: firstNameV,
+ lastName: lastNameV,
+ company: companyV,
+ email: emailV,
+ password: passwordV,
+ repeatPassword: repeatPasswordV,
+ } = values;
+ */
return (
- {step?.label}
+ {formLabel}
{/* TODO - help text
Mandatory information
@@ -60,12 +61,16 @@ function QDynamicForm(props: Props): JSX.Element {
Object.keys(formFields).length > 0 &&
Object.keys(formFields).map((fieldName: any) => {
const field = formFields[fieldName];
+ if (primaryKeyId && fieldName === primaryKeyId) {
+ return null;
+ }
if (values[fieldName] === undefined) {
values[fieldName] = "";
}
return (
.
+ */
+
+import * as Yup from "yup";
+
+import { QFieldMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
+import { QFieldType } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType";
+
+/*******************************************************************************
+ ** Meta-data to represent a single field in a table.
+ **
+ *******************************************************************************/
+class DynamicFormUtils {
+ public static getFormData(qqqFormFields: QFieldMetaData[]) {
+ const dynamicFormFields: any = {};
+ const formValidations: any = {};
+
+ qqqFormFields.forEach((field) => {
+ let fieldType: string;
+ switch (field.type.toString()) {
+ case QFieldType.DECIMAL:
+ case QFieldType.INTEGER:
+ fieldType = "number";
+ break;
+ case QFieldType.DATE_TIME:
+ fieldType = "datetime-local";
+ break;
+ case QFieldType.PASSWORD:
+ case QFieldType.TIME:
+ case QFieldType.DATE:
+ fieldType = field.type.toString();
+ break;
+ case QFieldType.TEXT:
+ case QFieldType.HTML:
+ case QFieldType.STRING:
+ default:
+ fieldType = "text";
+ }
+
+ dynamicFormFields[field.name] = {
+ name: field.name,
+ label: field.label ? field.label : field.name,
+ isRequired: field.isRequired,
+ type: fieldType,
+ // todo invalidMsg: "Zipcode is not valid (e.g. 70000).",
+ };
+
+ if (field.isRequired) {
+ formValidations[field.name] = Yup.string().required(`${field.label} is required.`);
+ }
+ });
+
+ return { dynamicFormFields, formValidations };
+ }
+}
+
+export default DynamicFormUtils;
diff --git a/src/qqq/pages/entity-list/index.tsx b/src/qqq/pages/entity-list/index.tsx
index 5d18b0d..cd53a75 100644
--- a/src/qqq/pages/entity-list/index.tsx
+++ b/src/qqq/pages/entity-list/index.tsx
@@ -22,23 +22,24 @@ import Icon from "@mui/material/Icon";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import Divider from "@mui/material/Divider";
-
-// Material Dashboard 2 PRO React TS components
-import MDBox from "components/MDBox";
-import MDTypography from "components/MDTypography";
-import MDButton from "components/MDButton";
+import Link from "@mui/material/Link";
// Material Dashboard 2 PRO React TS examples components
import DashboardLayout from "examples/LayoutContainers/DashboardLayout";
import DashboardNavbar from "examples/Navbars/DashboardNavbar";
import DataTable from "examples/Tables/DataTable";
-// Data
+// QQQ
import { QProcessMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QProcessMetaData";
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
-import Link from "@mui/material/Link";
import { QTableMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
import { useParams } from "react-router-dom";
+import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
+
+// Material Dashboard 2 PRO React TS components
+import MDBox from "components/MDBox";
+import MDTypography from "components/MDTypography";
+import MDButton from "components/MDButton";
import Footer from "../../components/Footer";
import IdCell from "../../components/EntityForm/components/IdCell";
diff --git a/src/qqq/pages/entity-view/components/ViewContents/index.tsx b/src/qqq/pages/entity-view/components/ViewContents/index.tsx
index a48880c..97b03f7 100644
--- a/src/qqq/pages/entity-view/components/ViewContents/index.tsx
+++ b/src/qqq/pages/entity-view/components/ViewContents/index.tsx
@@ -13,7 +13,9 @@
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
+// react components
import { useParams } from "react-router-dom";
+import React, { useReducer, useState } from "react";
// @material-ui core components
import Card from "@mui/material/Card";
@@ -26,17 +28,12 @@ import DialogContentText from "@mui/material/DialogContentText";
import DialogActions from "@mui/material/DialogActions";
import Button from "@mui/material/Button";
+// qqq imports
+import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
+
// Material Dashboard 2 PRO React TS components
import MDBox from "components/MDBox";
import MDTypography from "components/MDTypography";
-
-// Settings page components
-
-// qqq imports
-import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
-import { QRecord } from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
-import React, { useReducer, useState } from "react";
-
import MDButton from "../../../../../components/MDButton";
const qController = new QController("");
@@ -104,20 +101,16 @@ function ViewContents({ id }: Props): JSX.Element {
setOpen(false);
};
- /*
const handleDelete = (event: { preventDefault: () => void }) => {
event.preventDefault();
- /*
(async () => {
await qController.delete(tableName, id).then(() => {
- window.location.href = `/${tableName}/list/`;
+ window.location.href = `/${tableName}`;
});
})();
-
};
- */
- const editPath = `/${tableName}/edit/${id}`;
+ const editPath = `/${tableName}/${id}/edit`;
return (
@@ -131,7 +124,6 @@ function ViewContents({ id }: Props): JSX.Element {
-
-
+
edit {tableMetaData?.label}
diff --git a/src/qqq/pages/process-run/index.tsx b/src/qqq/pages/process-run/index.tsx
index 1415c9d..a926fd6 100644
--- a/src/qqq/pages/process-run/index.tsx
+++ b/src/qqq/pages/process-run/index.tsx
@@ -45,6 +45,7 @@ import * as Yup from "yup";
import { QController } from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
import { QFrontendStepMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData";
import { useParams } from "react-router-dom";
+import DynamicFormUtils from "qqq/components/QDynamicForm/utils/DynamicFormUtils";
import QDynamicForm from "../../components/QDynamicForm";
function getDynamicStepContent(stepIndex: number, stepParam: any, formData: any): JSX.Element {
@@ -57,7 +58,7 @@ function getDynamicStepContent(stepIndex: number, stepParam: any, formData: any)
return Loading...
;
}
- return ;
+ return ;
}
const qController = new QController("");
@@ -90,31 +91,23 @@ function ProcessRun(): JSX.Element {
setActiveStep(activeStep);
setFormId(activeStep.name);
+ const { dynamicFormFields, formValidations } = DynamicFormUtils.getFormData(
+ activeStep.formFields
+ );
+
const formFields: any = {};
const initialValues: any = {};
const validations: any = {};
activeStep.formFields.forEach((field) => {
- formFields[field.name] = {
- name: field.name,
- label: field.label,
- type: "text", // todo better
- // todo invalidMsg: "Zipcode is not valid (e.g. 70000).",
- };
-
// todo - not working - also, needs real value.
initialValues[field.name] = "Hi";
-
- // todo - all this based on type and other metadata.
- // see src/layouts/pages/users/new-user/schemas/validations.ts
- validations[field.name] = Yup.string().required(`${field.label} is required.`);
- // validations[field.name] = Yup.string().optional();
});
- setFormFields(formFields);
+ setFormFields(dynamicFormFields);
setInitialValues(initialValues);
- setValidations(Yup.object().shape(validations));
- console.log(`in updateActiveStep: formFields ${JSON.stringify(formFields)}`);
+ setValidations(Yup.object().shape(formValidations));
+ console.log(`in updateActiveStep: formFields ${JSON.stringify(dynamicFormFields)}`);
console.log(`in updateActiveStep: initialValues ${JSON.stringify(initialValues)}`);
}
};