diff --git a/package.json b/package.json index ba1c30f..fdc88f5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@fullcalendar/interaction": "5.10.0", "@fullcalendar/react": "5.10.0", "@fullcalendar/timegrid": "5.10.0", - "@kingsrook/qqq-frontend-core": "1.0.33", + "@kingsrook/qqq-frontend-core": "1.0.34", "@mui/icons-material": "5.4.1", "@mui/material": "5.4.1", "@mui/styled-engine": "5.4.1", diff --git a/src/qqq/components/QDynamicForm/index.tsx b/src/qqq/components/QDynamicForm/index.tsx index b89d3f5..82f2998 100644 --- a/src/qqq/components/QDynamicForm/index.tsx +++ b/src/qqq/components/QDynamicForm/index.tsx @@ -158,6 +158,7 @@ function QDynamicForm(props: Props): JSX.Element bulkEditMode={bulkEditMode} bulkEditSwitchChangeHandler={bulkEditSwitchChanged} success={`${values[fieldName]}` !== "" && !errors[fieldName] && touched[fieldName]} + formFieldObject={field} /> ); diff --git a/src/qqq/components/QDynamicForm/utils/DynamicFormUtils.ts b/src/qqq/components/QDynamicForm/utils/DynamicFormUtils.ts index cb46b5c..ec2374b 100644 --- a/src/qqq/components/QDynamicForm/utils/DynamicFormUtils.ts +++ b/src/qqq/components/QDynamicForm/utils/DynamicFormUtils.ts @@ -19,6 +19,7 @@ * along with this program. If not, see . */ +import {AdornmentType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/AdornmentType"; import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QFieldType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldType"; import * as Yup from "yup"; @@ -73,6 +74,17 @@ class DynamicFormUtils fieldType = "text"; } + let more: any = {}; + if (field.hasAdornment(AdornmentType.CODE_EDITOR)) + { + fieldType = "ace"; + const values = field.getAdornment(AdornmentType.CODE_EDITOR).values; + if (values.has("languageMode")) + { + more.languageMode = values.get("languageMode"); + } + } + let label = field.label ? field.label : field.name; label += field.isRequired ? " *" : ""; @@ -84,6 +96,7 @@ class DynamicFormUtils type: fieldType, displayFormat: field.displayFormat, // todo invalidMsg: "Zipcode is not valid (e.g. 70000).", + ...more }); } diff --git a/src/qqq/components/QDynamicFormField/index.tsx b/src/qqq/components/QDynamicFormField/index.tsx index 20ab1cc..cfd8bdc 100644 --- a/src/qqq/components/QDynamicFormField/index.tsx +++ b/src/qqq/components/QDynamicFormField/index.tsx @@ -22,8 +22,9 @@ import {InputAdornment, InputLabel} from "@mui/material"; import Box from "@mui/material/Box"; import Switch from "@mui/material/Switch"; -import {ErrorMessage, Field, useFormikContext} from "formik"; -import React, {SyntheticEvent, useState} from "react"; +import {ErrorMessage, Field, FieldProps, useFormikContext} from "formik"; +import React, {useState} from "react"; +import AceEditor from "react-ace"; import QBooleanFieldSwitch from "qqq/components/QDynamicFormField/QBooleanFieldSwitch"; import MDBox from "qqq/components/Temporary/MDBox"; import MDInput from "qqq/components/Temporary/MDInput"; @@ -43,14 +44,15 @@ interface Props bulkEditMode?: boolean; bulkEditSwitchChangeHandler?: any; + formFieldObject: any; // is the type returned by DynamicFormUtils.getDynamicField } function QDynamicFormField({ - label, name, displayFormat, value, bulkEditMode, bulkEditSwitchChangeHandler, type, isEditable, ...rest + label, name, displayFormat, value, bulkEditMode, bulkEditSwitchChangeHandler, type, isEditable, formFieldObject, ...rest }: Props): JSX.Element { - const [ switchChecked, setSwitchChecked ] = useState(false); - const [ isDisabled, setIsDisabled ] = useState(!isEditable || bulkEditMode); + const [switchChecked, setSwitchChecked] = useState(false); + const [isDisabled, setIsDisabled] = useState(!isEditable || bulkEditMode); const {setFieldValue} = useFormikContext(); @@ -82,15 +84,50 @@ function QDynamicFormField({ } }; + let field; + let getsBulkEditHtmlLabel = true; + if (type === "checkbox") + { + getsBulkEditHtmlLabel = false; + field = (); + } + else if (type === "ace") + { + let mode = "text"; + if(formFieldObject && formFieldObject.languageMode) + { + mode = formFieldObject.languageMode; + } - const field = () => - (type == "checkbox" ? - : + getsBulkEditHtmlLabel = false; + field = ( + <> + {label} + + { + setFieldValue(name, value, false); + }} + width="100%" + height="300px" + value={value} + style={{border: "1px solid gray"}} + /> + + ); + } + else + { + field = ( <> { - if(e.key === "Enter") + if (e.key === "Enter") { e.preventDefault(); } @@ -103,10 +140,16 @@ function QDynamicFormField({ ); + } const bulkEditSwitchChanged = () => { - const newSwitchValue = !switchChecked; + setBulkEditSwitch(!switchChecked); + }; + + const setBulkEditSwitch = (value: boolean) => + { + const newSwitchValue = value; setSwitchChecked(newSwitchValue); setIsDisabled(!newSwitchValue); bulkEditSwitchChangeHandler(name, newSwitchValue); @@ -124,13 +167,13 @@ function QDynamicFormField({ /> - {/* for checkboxes, if we put the whole thing in a label, we get bad overly aggressive toggling of the outer switch... */} - {(type == "checkbox" ? - field() : - - )} + { + getsBulkEditHtmlLabel + ? () + :
setBulkEditSwitch(true)}>{field}
+ }
); @@ -139,7 +182,7 @@ function QDynamicFormField({ { return ( - {field()} + {field} ); } diff --git a/src/qqq/pages/entity-list/EntityList.tsx b/src/qqq/pages/entity-list/EntityList.tsx index 7e11013..f0624fb 100644 --- a/src/qqq/pages/entity-list/EntityList.tsx +++ b/src/qqq/pages/entity-list/EntityList.tsx @@ -847,7 +847,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element // construct the url for the export // ////////////////////////////////////// const d = new Date(); - const dateString = `${d.getFullYear()}-${zp(d.getMonth())}-${zp(d.getDate())} ${zp(d.getHours())}${zp(d.getMinutes())}`; + const dateString = `${d.getFullYear()}-${zp(d.getMonth()+1)}-${zp(d.getDate())} ${zp(d.getHours())}${zp(d.getMinutes())}`; const filename = `${tableMetaData.label} Export ${dateString}.${format}`; const url = `/data/${tableMetaData.name}/export/${filename}?filter=${encodeURIComponent(JSON.stringify(buildQFilter(filterModel)))}&fields=${visibleFields.join(",")}`; @@ -870,7 +870,7 @@ function EntityList({table, launchProcess}: Props): JSX.Element }, 1); - Generating file ${filename}${totalRecords ? " with " + totalRecords.toLocaleString() + "records" : ""}... + Generating file ${filename}${totalRecords ? " with " + totalRecords.toLocaleString() + " record" + (totalRecords == 1 ? "" : "s") : ""}... `); /////////////////////////////////////////// diff --git a/src/qqq/pages/entity-view/AssociatedScriptEditor.tsx b/src/qqq/pages/entity-view/AssociatedScriptEditor.tsx index 21afce5..f2c4af4 100644 --- a/src/qqq/pages/entity-view/AssociatedScriptEditor.tsx +++ b/src/qqq/pages/entity-view/AssociatedScriptEditor.tsx @@ -20,7 +20,9 @@ */ -import {Typography} from "@mui/material"; +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {Label} from "@mui/icons-material"; +import {ToggleButton, ToggleButtonGroup, Typography} from "@mui/material"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import Card from "@mui/material/Card"; @@ -29,10 +31,20 @@ import TextField from "@mui/material/TextField"; import React, {useState} from "react"; import AceEditor from "react-ace"; import {QCancelButton, QSaveButton} from "qqq/components/QButtons"; +import ScriptDocsForm from "qqq/pages/entity-view/ScriptDocsForm"; +import ScriptTestForm from "qqq/pages/entity-view/ScriptTestForm"; import QClient from "qqq/utils/QClient"; +interface AssociatedScriptDefinition +{ + testInputFields: QFieldMetaData[]; + testOutputFields: QFieldMetaData[]; + scriptType: any; +} + interface Props { + scriptDefinition: AssociatedScriptDefinition; tableName: string; primaryKey: any; fieldName: string; @@ -46,11 +58,24 @@ interface Props const qController = QClient.getInstance(); -function AssociatedScriptEditor({tableName, primaryKey, fieldName, titlePrefix, recordLabel, scriptName, code, closeCallback}: Props): JSX.Element +function AssociatedScriptEditor({scriptDefinition, tableName, primaryKey, fieldName, titlePrefix, recordLabel, scriptName, code, closeCallback}: Props): JSX.Element { const [closing, setClosing] = useState(false); const [updatedCode, setUpdatedCode] = useState(code) const [commitMessage, setCommitMessage] = useState("") + const [openTool, setOpenTool] = useState(null); + + const changeOpenTool = (event: React.MouseEvent, newValue: string | null) => + { + setOpenTool(newValue); + + // need this to make Ace recognize new height. + setTimeout(() => + { + window.dispatchEvent(new Event("resize")) + }, 100); + }; + const saveClicked = () => { @@ -80,23 +105,56 @@ function AssociatedScriptEditor({tableName, primaryKey, fieldName, titlePrefix, } return ( - + - - {`${titlePrefix}: ${recordLabel} - ${scriptName}`} - - + + + {`${titlePrefix}: ${recordLabel} - ${scriptName}`} + + + + + Tools: + + + Test + Docs + + + + + + + + + { + openTool && + + { + openTool == "test" && + } + { + openTool == "docs" && + } + + } diff --git a/src/qqq/pages/entity-view/EntityDeveloperView.tsx b/src/qqq/pages/entity-view/EntityDeveloperView.tsx index 2da31f1..b7a2afb 100644 --- a/src/qqq/pages/entity-view/EntityDeveloperView.tsx +++ b/src/qqq/pages/entity-view/EntityDeveloperView.tsx @@ -20,6 +20,7 @@ */ import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException"; +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; import {Alert, Chip, Icon, ListItem, ListItemAvatar, Typography} from "@mui/material"; @@ -35,24 +36,20 @@ import ListItemText from "@mui/material/ListItemText"; import Modal from "@mui/material/Modal"; import Snackbar from "@mui/material/Snackbar"; import Tab from "@mui/material/Tab"; -import Table from "@mui/material/Table"; -import TableBody from "@mui/material/TableBody"; -import TableContainer from "@mui/material/TableContainer"; -import TableRow from "@mui/material/TableRow"; import Tabs from "@mui/material/Tabs"; -import TextField from "@mui/material/TextField"; import React, {useContext, useReducer, useState} from "react"; import AceEditor from "react-ace"; import {useParams} from "react-router-dom"; import QContext from "QContext"; import BaseLayout from "qqq/components/BaseLayout"; import CustomWidthTooltip from "qqq/components/CustomWidthTooltip/CustomWidthTooltip"; -import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell"; -import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell"; import MDBox from "qqq/components/Temporary/MDBox"; import AssociatedScriptEditor from "qqq/pages/entity-view/AssociatedScriptEditor"; +import ScriptLogsView from "qqq/pages/entity-view/ScriptLogsView"; +import ScriptTestForm from "qqq/pages/entity-view/ScriptTestForm"; import QClient from "qqq/utils/QClient"; import QValueUtils from "qqq/utils/QValueUtils"; +import ScriptDocsForm from "./ScriptDocsForm"; import "ace-builds/src-noconflict/mode-java"; import "ace-builds/src-noconflict/mode-javascript"; @@ -60,7 +57,6 @@ import "ace-builds/src-noconflict/mode-json"; import "ace-builds/src-noconflict/theme-github"; import "ace-builds/src-noconflict/ext-language_tools"; - const qController = QClient.getInstance(); interface TabPanelProps @@ -119,6 +115,8 @@ function EntityDeveloperView({table}: Props): JSX.Element const [selectedTabs, setSelectedTabs] = useState({} as any); const [viewingRevisions, setViewingRevisions] = useState({} as any); const [scriptLogs, setScriptLogs] = useState({} as any); + const [testInputValues, setTestInputValues] = useState({} as any); + const [testOutputValues, setTestOutputValues] = useState({} as any); const [editingScript, setEditingScript] = useState(null as any); const [alertText, setAlertText] = useState(null as string); @@ -157,6 +155,23 @@ function EntityDeveloperView({table}: Props): JSX.Element setAssociatedScripts(developerModeData.associatedScripts); + const testInputValues = {}; + const testOutputValues = {}; + console.log("@dk - here"); + console.log(developerModeData.associatedScripts); + developerModeData.associatedScripts.forEach((object: any) => + { + const fieldName = object.associatedScript.fieldName; + + // @ts-ignore + testInputValues[fieldName] = {}; + + // @ts-ignore + testOutputValues[fieldName] = {}; + }); + setTestInputValues(testInputValues); + setTestOutputValues(testOutputValues); + const recordJSONObject = {} as any; for (let key of record.values.keys()) { @@ -208,15 +223,44 @@ function EntityDeveloperView({table}: Props): JSX.Element return color; }; - const editScript = (fieldName: string, code: string) => + const editScript = (fieldName: string, code: string, object: any) => { const editingScript = {} as any; editingScript.fieldName = fieldName; editingScript.titlePrefix = code ? "Editing Script" : "Creating New Script"; editingScript.code = code; + editingScript.scriptDefinitionObject = object; setEditingScript(editingScript); }; + const testScript = (object: any, fieldName: string) => + { + const viewingRevisionArray = object.scriptRevisions?.filter((rev: any) => rev?.values?.id === viewingRevisions[fieldName]); + const code = viewingRevisionArray?.length > 0 ? viewingRevisionArray[0].values.contents : ""; + + const inputValues = new Map(); + if (object.testInputFields) + { + object.testInputFields.forEach((field: QFieldMetaData) => + { + console.log(`${field.name} = ${testInputValues[fieldName][field.name]}`) + inputValues.set(field.name, testInputValues[fieldName][field.name]); + }); + } + + const newTestOutputValues = JSON.parse(JSON.stringify(testOutputValues)); + newTestOutputValues[fieldName] = {}; + setTestOutputValues(newTestOutputValues); + + (async () => + { + const output = await qController.testScript(tableName, id, fieldName, code, inputValues); + const newTestOutputValues = JSON.parse(JSON.stringify(testOutputValues)); + newTestOutputValues[fieldName] = output.outputValues; + setTestOutputValues(newTestOutputValues); + })(); + }; + const closeEditingScript = (event: object, reason: string, alert: string = null) => { if (reason === "backdropClick") @@ -256,7 +300,7 @@ function EntityDeveloperView({table}: Props): JSX.Element scriptLogs[revisionId] = null; setScriptLogs(scriptLogs); - loadRevisionLogs(fieldName, revisionId) + loadRevisionLogs(fieldName, revisionId); forceUpdate(); }; @@ -276,7 +320,7 @@ function EntityDeveloperView({table}: Props): JSX.Element setScriptLogs(scriptLogs); forceUpdate(); })(); - } + }; function getRevisionsList(scriptRevisions: any, fieldName: any, currentScriptRevisionId: any) { @@ -333,54 +377,7 @@ function EntityDeveloperView({table}: Props): JSX.Element return No logs available for this version.; } - return ( - - - - - Timestamp - Run Time (ms) - Had Error? - Input - Output - Logs - - - - { - logs.map((logRecord) => - { - let logs = ""; - if (logRecord.values.scriptLogLine) - { - for (let i = 0; i < logRecord.values.scriptLogLine.length; i++) - { - console.log(" += " + i); - logs += (logRecord.values.scriptLogLine[i].values.text + "\n"); - } - } - - return ( - - {QValueUtils.formatDateTime(logRecord.values.startTimestamp)} - {logRecord.values.runTimeMillis?.toLocaleString()} - -
{QValueUtils.formatBoolean(logRecord.values.hadError)}
-
- {logRecord.values.input} - - {logRecord.values.output} - {logRecord.values.error} - - {logs} -
- ); - }) - } -
-
-
- ); + return (); } return ( @@ -440,7 +437,7 @@ function EntityDeveloperView({table}: Props): JSX.Element console.log(`Defaulting revision for ${fieldName} to ${currentScriptRevisionId}`); viewingRevisions[fieldName] = currentScriptRevisionId; - if(!scriptLogs[currentScriptRevisionId]) + if (!scriptLogs[currentScriptRevisionId]) { loadRevisionLogs(fieldName, currentScriptRevisionId); } @@ -508,7 +505,7 @@ function EntityDeveloperView({table}: Props): JSX.Element } - @@ -521,6 +518,7 @@ function EntityDeveloperView({table}: Props): JSX.Element theme="github" name={`view-${fieldName}`} readOnly + highlightActiveLine={false} editorProps={{$blockScrolling: true}} width="100%" height="400px" @@ -551,71 +549,15 @@ function EntityDeveloperView({table}: Props): JSX.Element
- - - - - - Test Input - - - - -
- -
-
-
-
-
- - - - Test Output - - - -
+ + +
+ - - - - - Documentation - - - -

A Deposco Order Optimization Batch Name Script is called when an order is being - optimized for shipping within Deposco. It is responsible for determining the order's  - Batch Name - in other words, an indication of what day the order should be shipped, - and whether or not the order is a line haul.

- -

Input

-

The input to this type of script is an object named input, with the following fields:

-
    -
  • warehouseId The id of the warehouse that the order is shipping from. See the Warehouse table for mappings.
  • -
  • shipToZipCode The zip code that the order is shipping to.
  • -
  • estimatedNoOfCartons The estimated number of cartons that the order will ship in.
  • -
- -

Output

-

The script is responsible only for outputting a single value - a string which will be set as the order's  - Batch Name in Deposco.

- -

Example

- - if(today.weekday == 1) - ( - return "TUE-Line-Haul" - ) - - -
-
-
-
-
+ + +
); @@ -629,6 +571,7 @@ function EntityDeveloperView({table}: Props): JSX.Element editingScript && closeEditingScript(event, reason)}> . + */ + + +import {Typography} from "@mui/material"; +import Box from "@mui/material/Box"; +import Card from "@mui/material/Card"; +import Grid from "@mui/material/Grid"; +import React from "react"; +import AceEditor from "react-ace"; + +interface Props +{ + helpText: string; + exampleCode: string; + aceEditorHeight: string +} + +ScriptDocsForm.defaultProps = { + aceEditorHeight: "100%", +}; + +function ScriptDocsForm({helpText, exampleCode, aceEditorHeight}: Props): JSX.Element +{ + + const oneBlock = (name: string, mode: string, heading: string, code: string): JSX.Element => + { + return ( + + + + {heading} + + + + + + + + + ) + } + + return ( + + {oneBlock("helpText", "text", "Documentation", helpText)} + {oneBlock("exampleCode", "javascript", "ExampleCode", exampleCode)} + + ); +} + +export default ScriptDocsForm; + diff --git a/src/qqq/pages/entity-view/ScriptLogsView.tsx b/src/qqq/pages/entity-view/ScriptLogsView.tsx new file mode 100644 index 0000000..b70a964 --- /dev/null +++ b/src/qqq/pages/entity-view/ScriptLogsView.tsx @@ -0,0 +1,96 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord"; +import Box from "@mui/material/Box"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableContainer from "@mui/material/TableContainer"; +import TableRow from "@mui/material/TableRow"; +import React from "react"; +import DataTableBodyCell from "qqq/components/Temporary/DataTable/DataTableBodyCell"; +import DataTableHeadCell from "qqq/components/Temporary/DataTable/DataTableHeadCell"; +import QValueUtils from "qqq/utils/QValueUtils"; + +interface Props +{ + logs: any; +} + +ScriptLogsView.defaultProps = { + logs: null, +}; + +function ScriptLogsView({logs}: Props): JSX.Element +{ + return ( + + + + + Timestamp + Run Time (ms) + Had Error? + Input + Output + Logs + + + + { + logs.map((logRecord: any) => + { + let logs = ""; + if (logRecord.values.scriptLogLine) + { + for (let i = 0; i < logRecord.values.scriptLogLine.length; i++) + { + console.log(" += " + i); + logs += (logRecord.values.scriptLogLine[i].values.text + "\n"); + } + } + + return ( + + {QValueUtils.formatDateTime(logRecord.values.startTimestamp)} + {logRecord.values.runTimeMillis?.toLocaleString()} + +
{QValueUtils.formatBoolean(logRecord.values.hadError)}
+
+ {logRecord.values.input} + + {logRecord.values.output} + {logRecord.values.error} + + {logs} +
+ ); + }) + } +
+
+
+ ); +} + +export default ScriptLogsView; + + diff --git a/src/qqq/pages/entity-view/ScriptTestForm.tsx b/src/qqq/pages/entity-view/ScriptTestForm.tsx new file mode 100644 index 0000000..6c33944 --- /dev/null +++ b/src/qqq/pages/entity-view/ScriptTestForm.tsx @@ -0,0 +1,189 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {Typography} from "@mui/material"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Card from "@mui/material/Card"; +import Grid from "@mui/material/Grid"; +import TextField from "@mui/material/TextField"; +import React, {useEffect, useState} from "react"; +import MDTypography from "components/MDTypography"; +import MDBox from "qqq/components/Temporary/MDBox"; +import QClient from "qqq/utils/QClient"; +import QValueUtils from "qqq/utils/QValueUtils"; + +interface AssociatedScriptDefinition +{ + testInputFields: QFieldMetaData[]; + testOutputFields: QFieldMetaData[]; +} + +interface Props +{ + scriptDefinition: AssociatedScriptDefinition; + tableName: string; + fieldName: string; + recordId: any; + code: string; +} + +ScriptTestForm.defaultProps = { + // foo: null, +}; + +const qController = QClient.getInstance(); + +function ScriptTestForm({scriptDefinition, tableName, fieldName, recordId, code}: Props): JSX.Element +{ + const [testInputValues, setTestInputValues] = useState({} as any); + const [testOutputValues, setTestOutputValues] = useState({} as any); + const [testException, setTestException] = useState(null as string) + const [firstRender, setFirstRender] = useState(true); + + if(firstRender) + { + setFirstRender(false) + } + + if(firstRender) + { + scriptDefinition.testInputFields.forEach((field: QFieldMetaData) => + { + testInputValues[field.name] = ""; + }); + } + + const testScript = () => + { + const inputValues = new Map(); + if (scriptDefinition.testInputFields) + { + scriptDefinition.testInputFields.forEach((field: QFieldMetaData) => + { + inputValues.set(field.name, testInputValues[field.name]); + }); + } + + setTestOutputValues({}); + setTestException(null); + + (async () => + { + const output = await qController.testScript(tableName, recordId, fieldName, code, inputValues); + console.log("got output:") + console.log(output); + console.log(Object.keys(output)); + setTestOutputValues(output.outputObject); + if(output.exception) + { + setTestException(output.exception.message) + console.log(`set test exception to ${output.exception.message}`); + } + })(); + }; + + // console.log("Rendering vvv"); + // console.log(`${testOutputValues}`); + // console.log("Rendering ^^^"); + + const handleInputChange = (fieldName: string, newValue: string) => + { + testInputValues[fieldName] = newValue; + console.log(`Setting ${fieldName} = ${newValue}`); + setTestInputValues(JSON.parse(JSON.stringify(testInputValues))); + } + + // console.log(testInputValues); + + return ( + + + + + + Test Input + + { + scriptDefinition.testInputFields && testInputValues && scriptDefinition.testInputFields.map((field: QFieldMetaData) => + { + return ( + { + handleInputChange(field.name, event.target.value); + }} + fullWidth + sx={{mb: 2}} + />); + }) + } + +
+ +
+
+
+
+
+ + + + Test Output + + { + testException && + + {testException} + + } + { + scriptDefinition.testOutputFields && testOutputValues && scriptDefinition.testOutputFields.map((f: any) => + { + const field = new QFieldMetaData(f); + console.log(field.name); + console.log(testOutputValues[field.name]); + return ( + + + {field.label}: + + + {QValueUtils.getValueForDisplay(field, testOutputValues[field.name], testOutputValues[field.name], "view")} + + + ); + }) + } + + + + +
+ ); +} + +export default ScriptTestForm; diff --git a/src/qqq/utils/QValueUtils.tsx b/src/qqq/utils/QValueUtils.tsx index 038d5f7..c51af2b 100644 --- a/src/qqq/utils/QValueUtils.tsx +++ b/src/qqq/utils/QValueUtils.tsx @@ -144,15 +144,23 @@ class QValueUtils if (field.hasAdornment(AdornmentType.CODE_EDITOR)) { + let mode = "text"; + const adornmentValues = field.getAdornment(AdornmentType.CODE_EDITOR).values; + if (adornmentValues.has("languageMode")) + { + mode = adornmentValues.get("languageMode"); + } + if(usage === "view") { return (