mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-21 22:58:43 +00:00
571 lines
27 KiB
TypeScript
571 lines
27 KiB
TypeScript
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException";
|
|
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
|
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
|
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
|
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
|
import {QFilterCriteria} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterCriteria";
|
|
import {QFilterOrderBy} from "@kingsrook/qqq-frontend-core/lib/model/query/QFilterOrderBy";
|
|
import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter";
|
|
import {Chip, SelectChangeEvent} from "@mui/material";
|
|
import Alert from "@mui/material/Alert";
|
|
import Avatar from "@mui/material/Avatar";
|
|
import Box from "@mui/material/Box";
|
|
import Button from "@mui/material/Button";
|
|
import Divider from "@mui/material/Divider";
|
|
import FormControl from "@mui/material/FormControl/FormControl";
|
|
import Grid from "@mui/material/Grid";
|
|
import List from "@mui/material/List";
|
|
import ListItem from "@mui/material/ListItem";
|
|
import ListItemAvatar from "@mui/material/ListItemAvatar";
|
|
import ListItemText from "@mui/material/ListItemText";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import Modal from "@mui/material/Modal";
|
|
import Select from "@mui/material/Select/Select";
|
|
import Snackbar from "@mui/material/Snackbar";
|
|
import Tab from "@mui/material/Tab";
|
|
import Tabs from "@mui/material/Tabs";
|
|
import Typography from "@mui/material/Typography";
|
|
import TabPanel from "qqq/components/misc/TabPanel";
|
|
import ScriptDocsForm from "qqq/components/scripts/ScriptDocsForm";
|
|
import ScriptEditor, {ScriptEditorProps} from "qqq/components/scripts/ScriptEditor";
|
|
import ScriptLogsView from "qqq/components/scripts/ScriptLogsView";
|
|
import ScriptTestForm from "qqq/components/scripts/ScriptTestForm";
|
|
import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip";
|
|
import {LoadingState} from "qqq/models/LoadingState";
|
|
import DeveloperModeUtils from "qqq/utils/DeveloperModeUtils";
|
|
import Client from "qqq/utils/qqq/Client";
|
|
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
|
|
|
import "ace-builds/src-noconflict/mode-java";
|
|
import "ace-builds/src-noconflict/mode-javascript";
|
|
import "ace-builds/src-noconflict/mode-velocity";
|
|
import "ace-builds/src-noconflict/mode-json";
|
|
import "ace-builds/src-noconflict/theme-github";
|
|
import React, {useReducer, useState} from "react";
|
|
import AceEditor from "react-ace";
|
|
import {Link} from "react-router-dom";
|
|
import "ace-builds/src-noconflict/ext-language_tools";
|
|
|
|
const qController = Client.getInstance();
|
|
|
|
// Declaring props types for ViewForm
|
|
interface Props
|
|
{
|
|
scriptId: number,
|
|
associatedScriptTableName?: string,
|
|
associatedScriptFieldName?: string,
|
|
associatedScriptRecordId?: any,
|
|
testInputFields?: QFieldMetaData[],
|
|
testOutputFields?: QFieldMetaData[],
|
|
}
|
|
|
|
ScriptViewer.defaultProps =
|
|
{
|
|
associatedScriptTableName: null,
|
|
associatedScriptFieldName: null,
|
|
associatedScriptRecordId: null,
|
|
testInputFields: null,
|
|
testOutputFields: null,
|
|
};
|
|
|
|
export default function ScriptViewer({scriptId, associatedScriptTableName, associatedScriptFieldName, associatedScriptRecordId, testInputFields, testOutputFields}: Props): JSX.Element
|
|
{
|
|
const [metaData, setMetaData] = useState(null as QInstance);
|
|
const [scriptRecord, setScriptRecord] = useState(null as QRecord);
|
|
const [asyncLoadInited, setAsyncLoadInited] = useState(false);
|
|
const [versionRecordList, setVersionRecordList] = useState(null as QRecord[]);
|
|
const [selectedVersionRecord, setSelectedVersionRecord] = useState(null as QRecord);
|
|
const [scriptLogs, setScriptLogs] = useState({} as any);
|
|
const [scriptTypeRecord, setScriptTypeRecord] = useState(null as QRecord);
|
|
const [scriptTypeFileSchemaList, setScriptTypeFileSchemaList] = useState(null as QRecord[]);
|
|
const [availableFileNames, setAvailableFileNames] = useState([] as string[]);
|
|
const [selectedFileName, setSelectedFileName] = useState("");
|
|
const [currentVersionId, setCurrentVersionId] = useState(null as number);
|
|
const [notFoundMessage, setNotFoundMessage] = useState(null);
|
|
const [selectedTab, setSelectedTab] = useState(0);
|
|
const [editorProps, setEditorProps] = useState(null as ScriptEditorProps);
|
|
const [successText, setSuccessText] = useState(null as string);
|
|
const [failText, setFailText] = useState(null as string);
|
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
|
|
const [loadingSelectedVersion, _] = useState(new LoadingState(forceUpdate, "loading"));
|
|
|
|
if (!asyncLoadInited)
|
|
{
|
|
setAsyncLoadInited(true);
|
|
|
|
(async () =>
|
|
{
|
|
try
|
|
{
|
|
setMetaData(await qController.loadMetaData());
|
|
|
|
const scriptRecord = await qController.get("script", scriptId);
|
|
setScriptRecord(scriptRecord);
|
|
|
|
const scriptTypeRecord = await qController.get("scriptType", scriptRecord.values.get("scriptTypeId"));
|
|
setScriptTypeRecord(scriptTypeRecord);
|
|
|
|
let fileMode = scriptTypeRecord.values.get("fileMode");
|
|
let scriptTypeFileSchemaList: QRecord[] = null;
|
|
if (fileMode == 1) // SINGLE
|
|
{
|
|
scriptTypeFileSchemaList = [new QRecord({values: {name: "Script.js", fileType: "javascript"}})];
|
|
}
|
|
else if (fileMode == 2) // MULTI_PRE_DEFINED
|
|
{
|
|
const filter = new QQueryFilter([new QFilterCriteria("scriptTypeId", QCriteriaOperator.EQUALS, [scriptRecord.values.get("scriptTypeId")])], [new QFilterOrderBy("id")]);
|
|
scriptTypeFileSchemaList = await qController.query("scriptTypeFileSchema", filter);
|
|
}
|
|
else // MULTI AD_HOC
|
|
{
|
|
// todo - not yet supported
|
|
console.log(`Script Type File Mode of ${fileMode} is not yet supported.`);
|
|
}
|
|
|
|
setScriptTypeFileSchemaList(scriptTypeFileSchemaList);
|
|
if (scriptTypeFileSchemaList)
|
|
{
|
|
const availableFileNames = scriptTypeFileSchemaList.map((fileSchemaRecord) => fileSchemaRecord.values.get("name"));
|
|
setAvailableFileNames(availableFileNames);
|
|
setSelectedFileName(availableFileNames[0]);
|
|
}
|
|
|
|
const criteria = [new QFilterCriteria("scriptId", QCriteriaOperator.EQUALS, [scriptId])];
|
|
const orderBys = [new QFilterOrderBy("sequenceNo", false)];
|
|
const filter = new QQueryFilter(criteria, orderBys, null, "AND", 0, 25);
|
|
const versions = await qController.query("scriptRevision", filter);
|
|
console.log("Fetched versions:");
|
|
console.log(versions);
|
|
setVersionRecordList(versions);
|
|
|
|
if (versions && versions.length > 0)
|
|
{
|
|
selectVersion(versions[0]);
|
|
}
|
|
}
|
|
catch (e)
|
|
{
|
|
if (e instanceof QException)
|
|
{
|
|
if ((e as QException).status === 404)
|
|
{
|
|
setNotFoundMessage("Script code could not be found.");
|
|
return;
|
|
}
|
|
}
|
|
setNotFoundMessage("Error loading Script code: " + e);
|
|
}
|
|
})();
|
|
}
|
|
|
|
const editData = (selectedVersionRecord: QRecord) =>
|
|
{
|
|
const editorProps = {} as ScriptEditorProps;
|
|
editorProps.title = (selectedVersionRecord ? "Editing Code for Script: " : "Initializing Code for Script: ") + scriptRecord?.values?.get("name");
|
|
editorProps.scriptRevisionRecord = selectedVersionRecord;
|
|
editorProps.scriptId = scriptId;
|
|
editorProps.tableName = associatedScriptTableName;
|
|
editorProps.fieldName = associatedScriptFieldName;
|
|
editorProps.recordId = associatedScriptRecordId;
|
|
editorProps.scriptTypeRecord = scriptTypeRecord;
|
|
editorProps.scriptTypeFileSchemaList = scriptTypeFileSchemaList;
|
|
setEditorProps(editorProps);
|
|
};
|
|
|
|
const closeEditingScript = (event: object, reason: string, alert: string = null) =>
|
|
{
|
|
if (reason === "backdropClick" || reason === "escapeKeyDown")
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (reason === "saved")
|
|
{
|
|
setAsyncLoadInited(false);
|
|
forceUpdate();
|
|
|
|
if (alert)
|
|
{
|
|
setSuccessText(alert);
|
|
}
|
|
}
|
|
else if (reason === "failed")
|
|
{
|
|
setAsyncLoadInited(false);
|
|
forceUpdate();
|
|
|
|
if (alert)
|
|
{
|
|
setFailText(alert);
|
|
}
|
|
}
|
|
|
|
setEditorProps(null);
|
|
};
|
|
|
|
const changeTab = (newValue: number) =>
|
|
{
|
|
setSelectedTab(newValue);
|
|
forceUpdate();
|
|
};
|
|
|
|
const selectVersion = (version: QRecord) =>
|
|
{
|
|
(async () =>
|
|
{
|
|
setCurrentVersionId(version.values.get("id"));
|
|
loadingSelectedVersion.setLoading();
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
// fetch the full version - including its associated scriptRevisionFile sub-records //
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
const selectedVersion = await qController.get("scriptRevision", version.values.get("id"), null, true);
|
|
console.log("Fetched selectedVersion:");
|
|
console.log(selectedVersion);
|
|
setSelectedVersionRecord(selectedVersion);
|
|
loadingSelectedVersion.setNotLoading();
|
|
forceUpdate();
|
|
})();
|
|
};
|
|
|
|
const handleSelectFile = (event: SelectChangeEvent) =>
|
|
{
|
|
setSelectedFileName(event.target.value);
|
|
};
|
|
|
|
const getSelectedFileCode = (): string =>
|
|
{
|
|
return (getSelectedVersionCode()[selectedFileName] ?? "");
|
|
};
|
|
|
|
const getSelectedFileType = (): string =>
|
|
{
|
|
for (let i = 0; i < scriptTypeFileSchemaList.length; i++)
|
|
{
|
|
let name = scriptTypeFileSchemaList[i].values.get("name");
|
|
if (name == selectedFileName)
|
|
{
|
|
return (scriptTypeFileSchemaList[i].values.get("fileType"));
|
|
}
|
|
}
|
|
|
|
return ("javascript"); // have some default...
|
|
};
|
|
|
|
const getSelectedVersionCode = (): { [name: string]: string } =>
|
|
{
|
|
let rs: { [name: string]: string } = {};
|
|
let files = selectedVersionRecord?.associatedRecords?.get("files");
|
|
|
|
for (let j = 0; j < files?.length; j++)
|
|
{
|
|
let file = files[j];
|
|
rs[file.values.get("fileName")] = file.values.get("contents");
|
|
}
|
|
|
|
return (rs);
|
|
};
|
|
|
|
function getVersionsList(versionRecordList: QRecord[], selectedVersionRecord: QRecord)
|
|
{
|
|
return <List sx={{pl: 3, height: "400px", overflow: "auto"}}>
|
|
{
|
|
(versionRecordList == null || versionRecordList.length == 0) ?
|
|
<Typography variant="body2">
|
|
There are not any versions of this script.
|
|
</Typography>
|
|
: <></>
|
|
}
|
|
{
|
|
versionRecordList?.map((version: any) =>
|
|
{
|
|
const timeAuthorLine = `${ValueUtils.formatDateTime(version.values.get("createDate"))} by ${version.values.get("author")}`;
|
|
const apiLine = `API: ${version.displayValues.get("apiName") ?? "None"} version ${version.displayValues.get("apiVersion") ?? "None"}`;
|
|
|
|
return (
|
|
<React.Fragment key={version.values.get("id")}>
|
|
<ListItem sx={{p: 1}} alignItems="flex-start" selected={selectedVersionRecord?.values?.get("id") == version.values.get("id")} onClick={(event) => selectVersion(version)}>
|
|
<ListItemAvatar>
|
|
<Avatar sx={{bgcolor: DeveloperModeUtils.revToColor("", scriptId, version.values.get("sequenceNo"))}}>{`${version.values.get("sequenceNo")}`}</Avatar>
|
|
</ListItemAvatar>
|
|
<ListItemText
|
|
primaryTypographyProps={{fontSize: "1rem"}}
|
|
secondaryTypographyProps={{fontSize: ".85rem"}}
|
|
primary={
|
|
<div style={{overflow: "hidden", textOverflow: "ellipsis", maxHeight: "5rem"}} title={version.values.get("commitMessage")}>
|
|
{scriptRecord.values.get("currentScriptRevisionId") == version?.values?.get("id") && <Chip label="CURRENT" color="success" variant="outlined" size="small" sx={{mr: 1, fontSize: "0.75rem"}} />}
|
|
{version.values.get("commitMessage")}
|
|
</div>
|
|
}
|
|
secondary={
|
|
<>
|
|
<div style={{whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}} title={timeAuthorLine}>
|
|
{timeAuthorLine}
|
|
</div>
|
|
{
|
|
(version.displayValues.get("apiName") || version.displayValues.get("apiVersion")) &&
|
|
<div style={{whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}} title={apiLine}>
|
|
{apiLine}
|
|
</div>
|
|
}
|
|
</>
|
|
}
|
|
/>
|
|
</ListItem>
|
|
<Divider sx={{my: 0.5}} variant="inset" component="li" />
|
|
</React.Fragment>
|
|
);
|
|
})
|
|
}
|
|
</List>;
|
|
}
|
|
|
|
const getScriptLogs = (scriptRevisionId: number) =>
|
|
{
|
|
if (!scriptLogs[scriptRevisionId])
|
|
{
|
|
(async () =>
|
|
{
|
|
let filter = new QQueryFilter([new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, [scriptRevisionId])], [new QFilterOrderBy("id", false)], null, "AND", 0, 100);
|
|
scriptLogs[scriptRevisionId] = await qController.query("scriptLog", filter);
|
|
setScriptLogs(scriptLogs);
|
|
forceUpdate();
|
|
})();
|
|
return <Typography variant="body2" p={3}>Loading...</Typography>;
|
|
}
|
|
|
|
const logs = scriptLogs[scriptRevisionId] as any[];
|
|
if (logs === null || logs === undefined)
|
|
{
|
|
return <Typography variant="body2" p={3}>Loading...</Typography>;
|
|
}
|
|
|
|
if (logs.length === 0)
|
|
{
|
|
return <Typography variant="body2" p={3}>No logs available for this version.</Typography>;
|
|
}
|
|
|
|
return (<ScriptLogsView logs={logs} />);
|
|
};
|
|
|
|
let editButtonTooltip = "";
|
|
let editButtonText = "Create New Version";
|
|
if (currentVersionId)
|
|
{
|
|
if (currentVersionId === scriptRecord?.values?.get("currentScriptRevisionId"))
|
|
{
|
|
editButtonTooltip = "If you make any changes to this script, a new version will be created when you hit Save.";
|
|
editButtonText = "Edit";
|
|
}
|
|
else
|
|
{
|
|
editButtonTooltip = "If you want to make this previous version active, bring up the Edit window, make any changes " +
|
|
"to the old version if they are needed, then click Save. A new version will be created, and set as current.";
|
|
editButtonText = "Edit and Activate";
|
|
}
|
|
}
|
|
|
|
function buildScriptLogFilter(scriptRevisionId: any)
|
|
{
|
|
return JSON.stringify(new QQueryFilter([new QFilterCriteria("scriptRevisionId", QCriteriaOperator.EQUALS, [scriptRevisionId])]));
|
|
}
|
|
|
|
return (
|
|
<Grid container className="scriptViewer" m={-2} pt={4} width={"calc(100% + 2rem)"}>
|
|
<Grid item xs={12}>
|
|
<Box>
|
|
{
|
|
notFoundMessage
|
|
?
|
|
<Box>{notFoundMessage}</Box>
|
|
:
|
|
<Box>
|
|
{
|
|
successText ? (
|
|
<Snackbar open={successText !== null && successText !== ""} autoHideDuration={6000} onClose={() => setSuccessText(null)} anchorOrigin={{vertical: "top", horizontal: "center"}}>
|
|
<Alert color="success" onClose={() => setSuccessText(null)}>
|
|
{successText}
|
|
</Alert>
|
|
</Snackbar>
|
|
) : ("")
|
|
}
|
|
{
|
|
failText ? (
|
|
<Snackbar open={failText !== null && failText !== ""} autoHideDuration={6000} onClose={() => setFailText(null)} anchorOrigin={{vertical: "top", horizontal: "center"}}>
|
|
<Alert color="error" onClose={() => setFailText(null)}>
|
|
{failText}
|
|
</Alert>
|
|
</Snackbar>
|
|
) : ("")
|
|
}
|
|
|
|
<Grid container spacing={3}>
|
|
<Grid item xs={12}>
|
|
<>
|
|
<Tabs
|
|
sx={{m: 0, mb: 1, mt: -3}}
|
|
value={selectedTab}
|
|
onChange={(event, newValue) => changeTab(newValue)}
|
|
variant="standard"
|
|
>
|
|
<Tab label="Code" id="simple-tab-0" aria-controls="simple-tabpanel-0" />
|
|
<Tab label="Logs" id="simple-tab-1" aria-controls="simple-tabpanel-1" />
|
|
<Tab label="Test" id="simple-tab-1" aria-controls="simple-tabpanel-2" />
|
|
<Tab label="Docs" id="simple-tab-1" aria-controls="simple-tabpanel-3" />
|
|
</Tabs>
|
|
|
|
<TabPanel index={0} value={selectedTab}>
|
|
<Grid container>
|
|
<Grid item xs={4}>
|
|
<Box display="flex" alignItems="center" gap={2} pb={1} height="40px">
|
|
<Typography variant="h6" pl={3}>Versions</Typography>
|
|
</Box>
|
|
{getVersionsList(versionRecordList, selectedVersionRecord)}
|
|
</Grid>
|
|
|
|
<Grid item xs={8}>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between" gap={2} pb={1} height="40px">
|
|
{
|
|
selectedVersionRecord ?
|
|
<Typography variant="h6">
|
|
Version {selectedVersionRecord.values.get("sequenceNo")}
|
|
{
|
|
currentVersionId === scriptRecord.values.get("currentScriptRevisionId")
|
|
? (<> (Current)</>)
|
|
: <></>
|
|
}
|
|
</Typography>
|
|
: <></>
|
|
}
|
|
<CustomWidthTooltip title={editButtonTooltip}>
|
|
<Button sx={{py: 0}} onClick={() => editData(selectedVersionRecord)}>
|
|
{editButtonText}
|
|
</Button>
|
|
</CustomWidthTooltip>
|
|
</Box>
|
|
{
|
|
loadingSelectedVersion.isNotLoading() && selectedVersionRecord ? (
|
|
<>
|
|
{
|
|
availableFileNames && availableFileNames.length > 1 &&
|
|
<FormControl className="selectedFileTab" variant="standard" sx={{verticalAlign: "bottom", pl: "4px"}}>
|
|
<Select value={selectedFileName} onChange={(event) => handleSelectFile(event)}>
|
|
{
|
|
availableFileNames.map((name) => (
|
|
<MenuItem key={name} value={name}>{name}</MenuItem>
|
|
))
|
|
}
|
|
</Select>
|
|
</FormControl>
|
|
}
|
|
<AceEditor
|
|
mode={getSelectedFileType()}
|
|
theme="github"
|
|
name={"viewData"}
|
|
readOnly
|
|
highlightActiveLine={false}
|
|
editorProps={{$blockScrolling: true}}
|
|
setOptions={{useWorker: false}}
|
|
width="100%"
|
|
height="400px"
|
|
value={getSelectedFileCode()}
|
|
style={{borderTop: "1px solid lightgray", borderBottomRightRadius: "1rem"}}
|
|
/>
|
|
</>
|
|
) : null
|
|
}
|
|
{
|
|
loadingSelectedVersion.isLoadingSlow() && <Box fontSize="14px" pl={3}>Loading...</Box>
|
|
}
|
|
</Grid>
|
|
</Grid>
|
|
</TabPanel>
|
|
|
|
<TabPanel index={1} value={selectedTab}>
|
|
<Grid container height="440px">
|
|
<Grid item xs={4}>
|
|
<Box display="flex" alignItems="center" gap={2} pb={1} height="40px">
|
|
<Typography variant="h6" pl={3}>Versions</Typography>
|
|
</Box>
|
|
{getVersionsList(versionRecordList, selectedVersionRecord)}
|
|
</Grid>
|
|
<Grid item xs={8}>
|
|
{
|
|
selectedVersionRecord ? (
|
|
<>
|
|
<Box display="flex" alignItems="center" gap={2} pb={1} height="40px">
|
|
<Typography variant="h6">Script Logs (Version {selectedVersionRecord?.values.get("sequenceNo")})</Typography>
|
|
<Link style={{fontSize: "1rem"}} to={`${metaData.getTablePathByName("scriptLog")}?filter=${buildScriptLogFilter(selectedVersionRecord?.values.get("id"))}`}>View All</Link>
|
|
</Box>
|
|
<Box height="400px" overflow="auto">
|
|
{getScriptLogs(selectedVersionRecord.values.get("id"))}
|
|
</Box>
|
|
</>
|
|
) : <Box>Select a version to view logs</Box>
|
|
}
|
|
</Grid>
|
|
</Grid>
|
|
</TabPanel>
|
|
|
|
<TabPanel index={2} value={selectedTab}>
|
|
<Box sx={{height: "455px"}} px={2} pb={1}>
|
|
<ScriptTestForm scriptId={scriptId}
|
|
scriptType={scriptTypeRecord}
|
|
tableName={associatedScriptTableName}
|
|
fieldName={associatedScriptFieldName}
|
|
recordId={associatedScriptRecordId}
|
|
fileContents={getSelectedVersionCode()}
|
|
apiName={selectedVersionRecord?.values.get("apiName")}
|
|
apiVersion={selectedVersionRecord?.values.get("apiVersion")} />
|
|
</Box>
|
|
</TabPanel>
|
|
|
|
<TabPanel index={3} value={selectedTab}>
|
|
<Box sx={{height: "455px"}} px={2} pb={1}>
|
|
<ScriptDocsForm helpText={scriptTypeRecord?.values.get("helpText")} exampleCode={scriptTypeRecord?.values.get("sampleCode")} />
|
|
</Box>
|
|
</TabPanel>
|
|
</>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
{
|
|
editorProps &&
|
|
<Modal open={editorProps !== null} onClose={(event, reason) => closeEditingScript(event, reason)}>
|
|
<ScriptEditor
|
|
closeCallback={closeEditingScript}
|
|
{...editorProps}
|
|
/>
|
|
</Modal>
|
|
}
|
|
|
|
</Box>
|
|
}
|
|
</Box>
|
|
</Grid>
|
|
</Grid>
|
|
);
|
|
}
|