diff --git a/.eslintrc.json b/.eslintrc.json index d35ae2b..83d4599 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -23,6 +23,8 @@ "no-console": "off", "react/prop-types": "off", "no-shadow": "off", + "no-unused-vars": "off", + "spaced-comment": "off", "react/jsx-props-no-spreading": "off", "react/react-in-jsx-scope": "off", "import/extensions": [ diff --git a/src/qqq/components/EntityForm/index.tsx b/src/qqq/components/EntityForm/index.tsx index 298ebe2..97a6a2f 100644 --- a/src/qqq/components/EntityForm/index.tsx +++ b/src/qqq/components/EntityForm/index.tsx @@ -60,17 +60,7 @@ function EntityForm({ id }: Props): JSX.Element { setTableMetaData(tableMetaData); if (id !== null) { - const records = await qController.query(tableName, 250); - let foundRecord: QRecord; - records.forEach((innerRecord) => { - const fieldKeys = [...innerRecord.values.keys()]; - fieldKeys.forEach((key) => { - const value = innerRecord.values.get(key); - if (key === tableMetaData.primaryKeyField && `${value}` === `${id}`) { - foundRecord = innerRecord; - } - }); - }); + const foundRecord = await qController.get(tableName, id); tableMetaData.fields.forEach((fieldMetaData, key) => { formValues[key] = foundRecord.values.get(key); diff --git a/src/qqq/components/QDynamicForm/index.tsx b/src/qqq/components/QDynamicForm/index.tsx new file mode 100644 index 0000000..0119315 --- /dev/null +++ b/src/qqq/components/QDynamicForm/index.tsx @@ -0,0 +1,162 @@ +/** + ========================================================= + * 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. + */ + +// @mui material components +import Grid from "@mui/material/Grid"; + +// Material Dashboard 2 PRO React TS components +import MDBox from "components/MDBox"; +import MDTypography from "components/MDTypography"; + +// NewUser page components +import FormField from "layouts/pages/users/new-user/components/FormField"; +import { QFrontendStepMetaData } from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFrontendStepMetaData"; + +interface Props { + formData: any; + step?: QFrontendStepMetaData | undefined; +} + +function QDynamicForm(props: Props): JSX.Element { + const { formData, step } = props; + const { formFields, values, errors, touched } = formData; + + /* + const { + firstName: firstNameV, + lastName: lastNameV, + company: companyV, + email: emailV, + password: passwordV, + repeatPassword: repeatPasswordV, + } = values; + */ + + return ( + + + {step?.label} + {/* TODO - help text + + Mandatory information + + */} + + + + {formFields && + Object.keys(formFields).length > 0 && + Object.keys(formFields).map((fieldName: any) => { + const field = formFields[fieldName]; + if (values[fieldName] === undefined) { + values[fieldName] = ""; + } + return ( + + + + ); + })} + + {/* + + + 0 && !errors.firstName} + /> + + + 0 && !errors.lastName} + /> + + + + + + + + 0 && !errors.email} + /> + + + + + 0 && !errors.password} + inputProps={{ autoComplete: "" }} + /> + + + 0 && !errors.repeatPassword} + inputProps={{ autoComplete: "" }} + /> + + + */} + + + ); +} + +QDynamicForm.defaultProps = { + step: undefined, +}; + +export default QDynamicForm; diff --git a/src/qqq/pages/entity-list/index.tsx b/src/qqq/pages/entity-list/index.tsx index 0a35bb6..5d18b0d 100644 --- a/src/qqq/pages/entity-list/index.tsx +++ b/src/qqq/pages/entity-list/index.tsx @@ -71,7 +71,7 @@ function EntityList({ table }: Props): JSX.Element { if (tableState === "") { (async () => { const tableMetaData = await qController.loadTableMetaData(tableName); - // const metaData = await qController.loadMetaData(); + const metaData = await qController.loadMetaData(); const results = await qController.query(tableName, 250); dataTableData = { columns: [], @@ -170,7 +170,7 @@ function EntityList({ table }: Props): JSX.Element { keyboard_arrow_down )} - {/* renderActionsMenu */} + {renderActionsMenu} { - const fieldKeys = [...innerRecord.values.keys()]; - fieldKeys.forEach((key) => { - const value = innerRecord.values.get(key); - if (key === tableMetaData.primaryKeyField && `${value}` === `${id}`) { - foundRecord = innerRecord; - } - }); - }); + const foundRecord = await qController.get(tableName, id); nameValues.push( diff --git a/src/qqq/pages/process-run/index.tsx b/src/qqq/pages/process-run/index.tsx new file mode 100644 index 0000000..1415c9d --- /dev/null +++ b/src/qqq/pages/process-run/index.tsx @@ -0,0 +1,239 @@ +/** + ========================================================= + * 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. + */ + +import { useReducer, useState } from "react"; + +// formik components +import { Formik, Form } from "formik"; + +// @mui material components +import Grid from "@mui/material/Grid"; +import Card from "@mui/material/Card"; +import Stepper from "@mui/material/Stepper"; +import Step from "@mui/material/Step"; +import StepLabel from "@mui/material/StepLabel"; + +// Material Dashboard 2 PRO React TS components +import MDBox from "components/MDBox"; +import MDButton from "components/MDButton"; + +// Material Dashboard 2 PRO React TS examples components +import DashboardLayout from "examples/LayoutContainers/DashboardLayout"; +import DashboardNavbar from "examples/Navbars/DashboardNavbar"; +import Footer from "examples/Footer"; + +// ProcessRun page components +import UserInfo from "layouts/pages/users/new-user/components/UserInfo"; +import Address from "layouts/pages/users/new-user/components/Address"; +import Socials from "layouts/pages/users/new-user/components/Socials"; +import Profile from "layouts/pages/users/new-user/components/Profile"; + +// ProcessRun layout schemas for form and form fields +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 QDynamicForm from "../../components/QDynamicForm"; + +function getDynamicStepContent(stepIndex: number, stepParam: any, formData: any): JSX.Element { + const { formFields, values, errors, touched } = formData; + const { step } = stepParam; + // console.log(`in getDynamicStepContent: step label ${step?.label}`); + + if (!Object.keys(formFields).length) { + console.log("in getDynamicStepContent. No fields yet, so returning 'loading'"); + return
Loading...
; + } + + return ; +} + +const qController = new QController(""); +console.log(qController); + +function ProcessRun(): JSX.Element { + const { processName } = useParams(); + const [activeStepIndex, setActiveStepIndex] = useState(0); + const [activeStep, setActiveStep] = useState(null as QFrontendStepMetaData); + const [steps, setSteps] = useState([] as QFrontendStepMetaData[]); + const [loadCounter, setLoadCounter] = useState(0); + const [processMetaData, setProcessMetaData] = useState(null); + const [formId, setFormId] = useState(""); + const [formFields, setFormFields] = useState({}); + const [initialValues, setInitialValues] = useState({}); + const [validations, setValidations] = useState({}); + // const currentValidation = validations[activeStepIndex]; + const [_, forceUpdate] = useReducer((x) => x + 1, 0); + const isLastStep = activeStepIndex === steps.length - 1; + + console.log("In the function"); + + const updateActiveStep = (newIndex: number, steps: QFrontendStepMetaData[]) => { + console.log(`Steps are: ${steps}`); + console.log(`Setting step to ${newIndex}`); + setActiveStepIndex(newIndex); + + if (steps) { + const activeStep = steps[newIndex]; + setActiveStep(activeStep); + setFormId(activeStep.name); + + 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); + setInitialValues(initialValues); + setValidations(Yup.object().shape(validations)); + console.log(`in updateActiveStep: formFields ${JSON.stringify(formFields)}`); + console.log(`in updateActiveStep: initialValues ${JSON.stringify(initialValues)}`); + } + }; + + const doInitialLoad = async () => { + console.log("Starting doInitialLoad"); + const processMetaData = await qController.loadProcessMetaData(processName); + console.log(processMetaData); + setProcessMetaData(processMetaData); + setSteps(processMetaData.frontendSteps); + updateActiveStep(0, processMetaData.frontendSteps); + console.log("Done with doInitialLoad"); + }; + + if (loadCounter === 0) { + setLoadCounter(1); + doInitialLoad(); + } + + const sleep = (ms: any) => + new Promise((resolve) => { + setTimeout(resolve, ms); + }); + const handleBack = () => updateActiveStep(activeStepIndex - 1, processMetaData.frontendSteps); + + const submitForm = async (values: any, actions: any) => { + await sleep(1000); + + // eslint-disable-next-line no-alert + alert(JSON.stringify(values, null, 2)); + + actions.setSubmitting(false); + actions.resetForm(); + + updateActiveStep(0, processMetaData.frontendSteps); + }; + + const handleSubmit = (values: any, actions: any) => { + submitForm(values, actions); + + // if (isLastStep) { + // submitForm(values, actions); + // } else { + // updateActiveStep(activeStepIndex + 1, processMetaData.frontendSteps); + // actions.setTouched({}); + // actions.setSubmitting(false); + // } + }; + + return ( + + + + + + + {({ values, errors, touched, isSubmitting }) => ( +
+ + + + {steps.map((step) => ( + + {step.label} + + ))} + + + + + {/*************************************************************************** + ** step content - e.g., the appropriate form or other screen for the step ** + ***************************************************************************/} + {getDynamicStepContent( + activeStepIndex, + { step: activeStep }, + { + values, + touched, + formFields, + errors, + } + )} + {/******************************** + ** back &| next/submit buttons ** + ********************************/} + + {activeStepIndex === 0 ? ( + + ) : ( + + back + + )} + + {isLastStep ? "submit" : "next"} + + + + + +
+ )} +
+
+
+
+