mirror of
https://github.com/Kingsrook/qqq-frontend-material-dashboard.git
synced 2025-07-17 21:00:45 +00:00
Merged feature/workflows-support into integration
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
"@auth0/auth0-react": "1.10.2",
|
||||
"@emotion/react": "11.7.1",
|
||||
"@emotion/styled": "11.6.0",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.122",
|
||||
"@kingsrook/qqq-frontend-core": "1.0.123",
|
||||
"@mui/icons-material": "5.4.1",
|
||||
"@mui/material": "5.11.1",
|
||||
"@mui/styles": "5.11.1",
|
||||
|
8
pom.xml
8
pom.xml
@ -66,7 +66,13 @@
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-backend-core</artifactId>
|
||||
<version>0.25.0-integration-sprint-62-20250307-205536</version>
|
||||
<version>0.26.0-integration-20250529-234230</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.kingsrook.qqq</groupId>
|
||||
<artifactId>qqq-middleware-javalin</artifactId>
|
||||
<optional>true</optional>
|
||||
<version>0.26.0-integration-20250529-234230</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
34
src/App.tsx
34
src/App.tsx
@ -377,6 +377,39 @@ export default function App({authenticationMetaData}: Props)
|
||||
});
|
||||
});
|
||||
|
||||
const materialDashboardInstanceMetaData = metaData.supplementalInstanceMetaData?.get("materialDashboard");
|
||||
if (materialDashboardInstanceMetaData)
|
||||
{
|
||||
const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
|
||||
if (processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
for (let processName of processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
const process = metaData.processes.get(processName);
|
||||
if (process)
|
||||
{
|
||||
routeList.push({
|
||||
name: process.label,
|
||||
key: process.name,
|
||||
route: `${path}/${process.name}`,
|
||||
component: <RecordQuery table={table} key={`${table.name}-${process.name}`} launchProcess={process} />,
|
||||
});
|
||||
|
||||
routeList.push({
|
||||
name: process.label,
|
||||
key: `${app.name}/${process.name}`,
|
||||
route: `${path}/:id/${process.name}`,
|
||||
component: <RecordView table={table} launchProcess={process} />,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////
|
||||
// deprecated //
|
||||
////////////////
|
||||
const runRecordScriptProcess = metaData.processes.get("runRecordScript");
|
||||
if (runRecordScriptProcess)
|
||||
{
|
||||
@ -395,6 +428,7 @@ export default function App({authenticationMetaData}: Props)
|
||||
component: <RecordView table={table} launchProcess={process} />,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const reportsForTable = ProcessUtils.getReportsForTable(metaData, table.name, true);
|
||||
reportsForTable.forEach((report) =>
|
||||
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class FormAdjusterInput
|
||||
{
|
||||
private String event;
|
||||
private String fieldName;
|
||||
|
||||
private Serializable newValue;
|
||||
private Map<String, Serializable> allValues;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for event
|
||||
*******************************************************************************/
|
||||
public String getEvent()
|
||||
{
|
||||
return (this.event);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for event
|
||||
*******************************************************************************/
|
||||
public void setEvent(String event)
|
||||
{
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for event
|
||||
*******************************************************************************/
|
||||
public FormAdjusterInput withEvent(String event)
|
||||
{
|
||||
this.event = event;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldName
|
||||
*******************************************************************************/
|
||||
public String getFieldName()
|
||||
{
|
||||
return (this.fieldName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldName
|
||||
*******************************************************************************/
|
||||
public void setFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldName
|
||||
*******************************************************************************/
|
||||
public FormAdjusterInput withFieldName(String fieldName)
|
||||
{
|
||||
this.fieldName = fieldName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for newValue
|
||||
*******************************************************************************/
|
||||
public Serializable getNewValue()
|
||||
{
|
||||
return (this.newValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for newValue
|
||||
*******************************************************************************/
|
||||
public void setNewValue(Serializable newValue)
|
||||
{
|
||||
this.newValue = newValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for newValue
|
||||
*******************************************************************************/
|
||||
public FormAdjusterInput withNewValue(Serializable newValue)
|
||||
{
|
||||
this.newValue = newValue;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for allValues
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getAllValues()
|
||||
{
|
||||
return (this.allValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for allValues
|
||||
*******************************************************************************/
|
||||
public void setAllValues(Map<String, Serializable> allValues)
|
||||
{
|
||||
this.allValues = allValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for allValues
|
||||
*******************************************************************************/
|
||||
public FormAdjusterInput withAllValues(Map<String, Serializable> allValues)
|
||||
{
|
||||
this.allValues = allValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface to be implemented by application-specific form-adjusters
|
||||
*******************************************************************************/
|
||||
public interface FormAdjusterInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
***************************************************************************/
|
||||
FormAdjusterOutput execute(FormAdjusterInput input) throws QException;
|
||||
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendFieldMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class FormAdjusterOutput
|
||||
{
|
||||
private Map<String, QFrontendFieldMetaData> updatedFieldMetaData = null;
|
||||
private Map<String, Serializable> updatedFieldValues = null;
|
||||
private Map<String, String> updatedFieldDisplayValues = null;
|
||||
private Set<String> fieldsToClear = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for updatedFieldValues
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getUpdatedFieldValues()
|
||||
{
|
||||
return (this.updatedFieldValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for updatedFieldValues
|
||||
*******************************************************************************/
|
||||
public void setUpdatedFieldValues(Map<String, Serializable> updatedFieldValues)
|
||||
{
|
||||
this.updatedFieldValues = updatedFieldValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for updatedFieldValues
|
||||
*******************************************************************************/
|
||||
public FormAdjusterOutput withUpdatedFieldValues(Map<String, Serializable> updatedFieldValues)
|
||||
{
|
||||
this.updatedFieldValues = updatedFieldValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldsToClear
|
||||
*******************************************************************************/
|
||||
public Set<String> getFieldsToClear()
|
||||
{
|
||||
return (this.fieldsToClear);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldsToClear
|
||||
*******************************************************************************/
|
||||
public void setFieldsToClear(Set<String> fieldsToClear)
|
||||
{
|
||||
this.fieldsToClear = fieldsToClear;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldsToClear
|
||||
*******************************************************************************/
|
||||
public FormAdjusterOutput withFieldsToClear(Set<String> fieldsToClear)
|
||||
{
|
||||
this.fieldsToClear = fieldsToClear;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for updatedFieldMetaData
|
||||
*******************************************************************************/
|
||||
public Map<String, QFrontendFieldMetaData> getUpdatedFieldMetaData()
|
||||
{
|
||||
return (this.updatedFieldMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for updatedFieldMetaData
|
||||
*******************************************************************************/
|
||||
public void setUpdatedFieldMetaData(Map<String, QFrontendFieldMetaData> updatedFieldMetaData)
|
||||
{
|
||||
this.updatedFieldMetaData = updatedFieldMetaData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for updatedFieldMetaData
|
||||
*******************************************************************************/
|
||||
public FormAdjusterOutput withUpdatedFieldMetaData(Map<String, QFrontendFieldMetaData> updatedFieldMetaData)
|
||||
{
|
||||
this.updatedFieldMetaData = updatedFieldMetaData;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for updatedFieldDisplayValues
|
||||
*******************************************************************************/
|
||||
public Map<String, String> getUpdatedFieldDisplayValues()
|
||||
{
|
||||
return (this.updatedFieldDisplayValues);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for updatedFieldDisplayValues
|
||||
*******************************************************************************/
|
||||
public void setUpdatedFieldDisplayValues(Map<String, String> updatedFieldDisplayValues)
|
||||
{
|
||||
this.updatedFieldDisplayValues = updatedFieldDisplayValues;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for updatedFieldDisplayValues
|
||||
*******************************************************************************/
|
||||
public FormAdjusterOutput withUpdatedFieldDisplayValues(Map<String, String> updatedFieldDisplayValues)
|
||||
{
|
||||
this.updatedFieldDisplayValues = updatedFieldDisplayValues;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
|
||||
import com.kingsrook.qqq.backend.javalin.QJavalinMetaData;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.model.metadata.MaterialDashboardFieldMetaData;
|
||||
import com.kingsrook.qqq.middleware.javalin.metadata.JavalinRouteProviderMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Class that stores code-references for the application's defined fromAdjusters
|
||||
** This class also, when registering its first formAdjuster, adds the route to
|
||||
** the javalin instance to service form-adjuster calls from the frontend.
|
||||
*******************************************************************************/
|
||||
public class FormAdjusterRegistry
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(FormAdjusterRegistry.class);
|
||||
|
||||
private static boolean didRegisterRouteProvider = false;
|
||||
private static QInstance lastRegisteredQInstance = null;
|
||||
|
||||
private static Map<String, QCodeReference> onChangeAdjusters = new HashMap<>();
|
||||
private static Map<String, QCodeReference> onLoadAdjusters = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static void registerFormAdjusters(QInstance qInstance, MaterialDashboardFieldMetaData materialDashboardFieldMetaData) throws QException
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// support hot-swaps, by checking if the input qInstance is different from one we previously registered for //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(didRegisterRouteProvider && lastRegisteredQInstance != qInstance)
|
||||
{
|
||||
didRegisterRouteProvider = false;
|
||||
onChangeAdjusters.clear();
|
||||
onLoadAdjusters.clear();
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// if we need to register the javalin router, do so (only once per qInstance) //
|
||||
// note, javalin is optional dep, so make sure it's available before try to use it //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
if(!didRegisterRouteProvider)
|
||||
{
|
||||
if(ClassPathUtils.isClassAvailable(QJavalinMetaData.class.getName()))
|
||||
{
|
||||
QJavalinMetaData javalinMetaData = QJavalinMetaData.ofOrWithNew(qInstance);
|
||||
javalinMetaData.withRouteProvider(new JavalinRouteProviderMetaData()
|
||||
.withHostedPath("/material-dashboard-backend/form-adjuster/{identifier}/{event}")
|
||||
.withMethods(List.of("POST"))
|
||||
.withProcessName(RunFormAdjusterProcess.NAME)
|
||||
);
|
||||
|
||||
qInstance.add(new RunFormAdjusterProcess().produce(qInstance));
|
||||
}
|
||||
|
||||
didRegisterRouteProvider = true;
|
||||
lastRegisteredQInstance = qInstance;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// add the code-references to the map of registered adjusters //
|
||||
////////////////////////////////////////////////////////////////
|
||||
String identifier = materialDashboardFieldMetaData.getFormAdjusterIdentifier();
|
||||
|
||||
QCodeReference onChangeCode = materialDashboardFieldMetaData.getOnChangeFormAdjuster();
|
||||
if(onChangeCode != null)
|
||||
{
|
||||
if(onChangeAdjusters.containsKey(identifier))
|
||||
{
|
||||
LOG.warn("Attempt to register more than one onChangeFormAdjuster with identifier: " + identifier);
|
||||
}
|
||||
onChangeAdjusters.put(identifier, onChangeCode);
|
||||
}
|
||||
|
||||
QCodeReference onLoadCode = materialDashboardFieldMetaData.getOnLoadFormAdjuster();
|
||||
if(onLoadCode != null)
|
||||
{
|
||||
if(onLoadAdjusters.containsKey(identifier))
|
||||
{
|
||||
LOG.warn("Attempt to register more than one onLoadFormAdjuster with identifier: " + identifier);
|
||||
}
|
||||
onLoadAdjusters.put(identifier, onLoadCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static FormAdjusterInterface getOnChangeAdjuster(String identifier)
|
||||
{
|
||||
QCodeReference codeReference = onChangeAdjusters.get(identifier);
|
||||
if(codeReference != null)
|
||||
{
|
||||
return QCodeLoader.getAdHoc(FormAdjusterInterface.class, codeReference);
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static FormAdjusterInterface getOnLoadAdjuster(String identifier)
|
||||
{
|
||||
QCodeReference codeReference = onLoadAdjusters.get(identifier);
|
||||
if(codeReference != null)
|
||||
{
|
||||
return QCodeLoader.getAdHoc(FormAdjusterInterface.class, codeReference);
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.middleware.javalin.routeproviders.ProcessBasedRouterPayload;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** process that looks up a form adjuster from the registry, and then runs it
|
||||
*******************************************************************************/
|
||||
public class RunFormAdjusterProcess implements BackendStep, MetaDataProducerInterface<QProcessMetaData>
|
||||
{
|
||||
public static final String NAME = "MaterialDashboardRunFormAdjusterProcess";
|
||||
|
||||
private static final QLogger LOG = QLogger.getLogger(RunFormAdjusterProcess.class);
|
||||
|
||||
public static final String EVENT_ON_LOAD = "onLoad";
|
||||
public static final String EVENT_ON_CHANGE = "onChange";
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QProcessMetaData produce(QInstance qInstance) throws QException
|
||||
{
|
||||
return new QProcessMetaData()
|
||||
.withName(NAME)
|
||||
.withStep(new QBackendStepMetaData()
|
||||
.withName("execute")
|
||||
.withCode(new QCodeReference(getClass())));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
ProcessBasedRouterPayload payload = runBackendStepInput.getProcessPayload(ProcessBasedRouterPayload.class);
|
||||
|
||||
String identifier = payload.getPathParams().get("identifier");
|
||||
String event = payload.getPathParams().get("event");
|
||||
|
||||
try
|
||||
{
|
||||
FormAdjusterInterface formAdjuster = switch(event)
|
||||
{
|
||||
case EVENT_ON_CHANGE -> FormAdjusterRegistry.getOnChangeAdjuster(identifier);
|
||||
case EVENT_ON_LOAD -> FormAdjusterRegistry.getOnLoadAdjuster(identifier);
|
||||
default -> throw new QException("Unknown event type: " + event);
|
||||
};
|
||||
|
||||
if(formAdjuster == null)
|
||||
{
|
||||
throw new QException("No form adjuster found for identifier: " + identifier + " and event: " + event);
|
||||
}
|
||||
|
||||
FormAdjusterInput input = new FormAdjusterInput();
|
||||
input.setEvent(event);
|
||||
input.setFieldName(payload.getFormParam("fieldName"));
|
||||
input.setNewValue(payload.getFormParam("newValue"));
|
||||
|
||||
String allValuesJson = payload.getFormParam("allValues");
|
||||
Map<String, Serializable> allValues = StringUtils.hasContent(allValuesJson) ? JsonUtils.toObject(allValuesJson, new TypeReference<>() {}) : Collections.emptyMap();
|
||||
input.setAllValues(allValues);
|
||||
|
||||
FormAdjusterOutput output = formAdjuster.execute(input);
|
||||
|
||||
payload.setResponseString(JsonUtils.toJson(output));
|
||||
runBackendStepOutput.setProcessPayload(payload);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error running form adjuster process", e, logPair("identifier", identifier), logPair("event", event));
|
||||
throw new QException("Error running form adjuster process: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.model.metadata;
|
||||
|
||||
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QSupplementalFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster.FormAdjusterInterface;
|
||||
import com.kingsrook.qqq.frontend.materialdashboard.actions.formadjuster.FormAdjusterRegistry;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MaterialDashboardFieldMetaData extends QSupplementalFieldMetaData
|
||||
{
|
||||
public static final String TYPE = "materialDashboard";
|
||||
|
||||
private static final QLogger LOG = QLogger.getLogger(MaterialDashboardFieldMetaData.class);
|
||||
|
||||
private String formAdjusterIdentifier = null;
|
||||
private QCodeReference onChangeFormAdjuster = null;
|
||||
private QCodeReference onLoadFormAdjuster = null;
|
||||
private Set<String> fieldsToDisableWhileRunningAdjusters = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean includeInFrontendMetaData()
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for onChangeFormAdjuster
|
||||
*******************************************************************************/
|
||||
public QCodeReference getOnChangeFormAdjuster()
|
||||
{
|
||||
return (this.onChangeFormAdjuster);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for onChangeFormAdjuster
|
||||
*******************************************************************************/
|
||||
public void setOnChangeFormAdjuster(QCodeReference onChangeFormAdjuster)
|
||||
{
|
||||
this.onChangeFormAdjuster = onChangeFormAdjuster;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for onChangeFormAdjuster
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardFieldMetaData withOnChangeFormAdjuster(QCodeReference onChangeFormAdjuster)
|
||||
{
|
||||
this.onChangeFormAdjuster = onChangeFormAdjuster;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for onLoadFormAdjuster
|
||||
*******************************************************************************/
|
||||
public QCodeReference getOnLoadFormAdjuster()
|
||||
{
|
||||
return (this.onLoadFormAdjuster);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for onLoadFormAdjuster
|
||||
*******************************************************************************/
|
||||
public void setOnLoadFormAdjuster(QCodeReference onLoadFormAdjuster)
|
||||
{
|
||||
this.onLoadFormAdjuster = onLoadFormAdjuster;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for onLoadFormAdjuster
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardFieldMetaData withOnLoadFormAdjuster(QCodeReference onLoadFormAdjuster)
|
||||
{
|
||||
this.onLoadFormAdjuster = onLoadFormAdjuster;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void enrich(QInstance qInstance, QFieldMetaData fieldMetaData)
|
||||
{
|
||||
try
|
||||
{
|
||||
FormAdjusterRegistry.registerFormAdjusters(qInstance, this);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error enriching MaterialDashboardFieldMetaData", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void validate(QInstance qInstance, QFieldMetaData fieldMetaData, QInstanceValidator qInstanceValidator)
|
||||
{
|
||||
String prefix = "MaterialDashboardFieldMetaData for field [" + fieldMetaData.getName() + "]";
|
||||
|
||||
boolean needsFormAdjusterIdentifer = false;
|
||||
if(onChangeFormAdjuster != null)
|
||||
{
|
||||
needsFormAdjusterIdentifer = true;
|
||||
qInstanceValidator.validateSimpleCodeReference(prefix + ", onChangeFormAdjuster", onChangeFormAdjuster, FormAdjusterInterface.class);
|
||||
}
|
||||
|
||||
if(onLoadFormAdjuster != null)
|
||||
{
|
||||
needsFormAdjusterIdentifer = true;
|
||||
qInstanceValidator.validateSimpleCodeReference(prefix + ", onLoadFormAdjuster", onLoadFormAdjuster, FormAdjusterInterface.class);
|
||||
}
|
||||
|
||||
if(needsFormAdjusterIdentifer)
|
||||
{
|
||||
qInstanceValidator.assertCondition(StringUtils.hasContent(formAdjusterIdentifier), prefix + ", formAdjusterIdentifier is required if using any FormAdjusters");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for formAdjusterIdentifier
|
||||
*******************************************************************************/
|
||||
public String getFormAdjusterIdentifier()
|
||||
{
|
||||
return (this.formAdjusterIdentifier);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for formAdjusterIdentifier
|
||||
*******************************************************************************/
|
||||
public void setFormAdjusterIdentifier(String formAdjusterIdentifier)
|
||||
{
|
||||
this.formAdjusterIdentifier = formAdjusterIdentifier;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for formAdjusterIdentifier
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardFieldMetaData withFormAdjusterIdentifier(String formAdjusterIdentifier)
|
||||
{
|
||||
this.formAdjusterIdentifier = formAdjusterIdentifier;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fieldsToDisableWhileRunningAdjusters
|
||||
*******************************************************************************/
|
||||
public Set<String> getFieldsToDisableWhileRunningAdjusters()
|
||||
{
|
||||
return (this.fieldsToDisableWhileRunningAdjusters);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fieldsToDisableWhileRunningAdjusters
|
||||
*******************************************************************************/
|
||||
public void setFieldsToDisableWhileRunningAdjusters(Set<String> fieldsToDisableWhileRunningAdjusters)
|
||||
{
|
||||
this.fieldsToDisableWhileRunningAdjusters = fieldsToDisableWhileRunningAdjusters;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fieldsToDisableWhileRunningAdjusters
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardFieldMetaData withFieldsToDisableWhileRunningAdjusters(Set<String> fieldsToDisableWhileRunningAdjusters)
|
||||
{
|
||||
this.fieldsToDisableWhileRunningAdjusters = fieldsToDisableWhileRunningAdjusters;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.materialdashboard.model.metadata;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** table-level meta-data for this module (handled as QSupplementalTableMetaData)
|
||||
*******************************************************************************/
|
||||
public class MaterialDashboardInstanceMetaData implements QSupplementalInstanceMetaData
|
||||
{
|
||||
public static final String TYPE = "materialDashboard";
|
||||
|
||||
private List<String> processNamesToAddToAllQueryAndViewScreens = new ArrayList<>();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return (TYPE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static MaterialDashboardInstanceMetaData ofOrWithNew(QInstance qInstance)
|
||||
{
|
||||
MaterialDashboardInstanceMetaData supplementalMetaData = (MaterialDashboardInstanceMetaData) qInstance.getSupplementalMetaData(TYPE);
|
||||
if(supplementalMetaData == null)
|
||||
{
|
||||
supplementalMetaData = new MaterialDashboardInstanceMetaData();
|
||||
qInstance.withSupplementalMetaData(supplementalMetaData);
|
||||
}
|
||||
|
||||
return (supplementalMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processNamesToAddToAllQueryAndViewScreens
|
||||
*******************************************************************************/
|
||||
public List<String> getProcessNamesToAddToAllQueryAndViewScreens()
|
||||
{
|
||||
return (this.processNamesToAddToAllQueryAndViewScreens);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addProcessNameToAddToAllQueryAndViewScreens(String processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
if(this.processNamesToAddToAllQueryAndViewScreens == null)
|
||||
{
|
||||
this.processNamesToAddToAllQueryAndViewScreens = new ArrayList<>();
|
||||
}
|
||||
this.processNamesToAddToAllQueryAndViewScreens.add(processNamesToAddToAllQueryAndViewScreens);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for processNamesToAddToAllQueryAndViewScreens
|
||||
*******************************************************************************/
|
||||
public void setProcessNamesToAddToAllQueryAndViewScreens(List<String> processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
this.processNamesToAddToAllQueryAndViewScreens = processNamesToAddToAllQueryAndViewScreens;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for processNamesToAddToAllQueryAndViewScreens
|
||||
*******************************************************************************/
|
||||
public MaterialDashboardInstanceMetaData withProcessNamesToAddToAllQueryAndViewScreens(List<String> processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
this.processNamesToAddToAllQueryAndViewScreens = processNamesToAddToAllQueryAndViewScreens;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -48,7 +48,7 @@ export default function useAnonymousAuthenticationModule({setIsFullyAuthenticate
|
||||
{
|
||||
console.log("Generating random token...");
|
||||
setIsFullyAuthenticated(true);
|
||||
qController.setGotAuthentication();
|
||||
Client.setGotAuthenticationInAllControllers();
|
||||
setCookie(SESSION_UUID_COOKIE_NAME, Md5.hashStr(`${new Date()}`), {path: "/"});
|
||||
console.log("Token generation complete.");
|
||||
};
|
||||
|
@ -30,6 +30,7 @@ import {useCookies} from "react-cookie";
|
||||
import {useNavigate, useSearchParams} from "react-router-dom";
|
||||
|
||||
const qController = Client.getInstance();
|
||||
const qControllerV1 = Client.getInstanceV1();
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -131,7 +132,7 @@ export default function useAuth0AuthenticationModule({setIsFullyAuthenticated, s
|
||||
}
|
||||
|
||||
setIsFullyAuthenticated(true);
|
||||
qController.setGotAuthentication();
|
||||
Client.setGotAuthenticationInAllControllers();
|
||||
|
||||
setLoggedInUser(auth0User);
|
||||
console.log("Token load complete.");
|
||||
|
@ -80,7 +80,7 @@ export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated,
|
||||
console.log(`we have new session UUID: ${newSessionUuid}`);
|
||||
|
||||
setIsFullyAuthenticated(true);
|
||||
qController.setGotAuthentication();
|
||||
Client.setGotAuthenticationInAllControllers();
|
||||
|
||||
setLoggedInUser(values?.user);
|
||||
console.log("Token load complete.");
|
||||
@ -109,7 +109,7 @@ export default function useOAuth2AuthenticationModule({setIsFullyAuthenticated,
|
||||
const {values} = await qController.manageSession(null, sessionUuid, null);
|
||||
|
||||
setIsFullyAuthenticated(true);
|
||||
qController.setGotAuthentication();
|
||||
Client.setGotAuthenticationInAllControllers();
|
||||
|
||||
setLoggedInUser(values?.user);
|
||||
console.log("Token load complete.");
|
||||
|
@ -20,15 +20,21 @@
|
||||
*/
|
||||
|
||||
import {AdornmentType} from "@kingsrook/qqq-frontend-core/lib/model/metaData/AdornmentType";
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QRecord} from "@kingsrook/qqq-frontend-core/lib/model/QRecord";
|
||||
import Box from "@mui/material/Box";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import {useFormikContext} from "formik";
|
||||
import QDynamicFormField from "qqq/components/forms/DynamicFormField";
|
||||
import DynamicFormUtils from "qqq/components/forms/DynamicFormUtils";
|
||||
import DynamicSelect from "qqq/components/forms/DynamicSelect";
|
||||
import FileInputField from "qqq/components/forms/FileInputField";
|
||||
import MDTypography from "qqq/components/legacy/MDTypography";
|
||||
import HelpContent from "qqq/components/misc/HelpContent";
|
||||
import React from "react";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import React, {useEffect, useState} from "react";
|
||||
|
||||
const qController = Client.getInstance();
|
||||
|
||||
interface Props
|
||||
{
|
||||
@ -43,7 +49,12 @@ interface Props
|
||||
|
||||
function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHandler, record, helpRoles, helpContentKeyPrefix}: Props): JSX.Element
|
||||
{
|
||||
const {formFields, values, errors, touched} = formData;
|
||||
const {formFields: origFormFields, errors, touched} = formData;
|
||||
const {setFieldValue, values} = useFormikContext<Record<string, any>>();
|
||||
|
||||
const [formAdjustmentCounter, setFormAdjustmentCounter] = useState(0)
|
||||
|
||||
const [formFields, setFormFields] = useState(origFormFields as {[key: string]: any});
|
||||
|
||||
const bulkEditSwitchChanged = (name: string, value: boolean) =>
|
||||
{
|
||||
@ -51,6 +62,204 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
};
|
||||
|
||||
|
||||
/////////////////////////////////////////
|
||||
// run on-load handlers if we have any //
|
||||
/////////////////////////////////////////
|
||||
useEffect(() =>
|
||||
{
|
||||
for (let fieldName in formFields)
|
||||
{
|
||||
const field = formFields[fieldName];
|
||||
|
||||
const materialDashboardFieldMetaData = field.fieldMetaData?.supplementalFieldMetaData?.get("materialDashboard");
|
||||
if(materialDashboardFieldMetaData?.onLoadFormAdjuster)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo consider cases with multiple - do they need to list a sequenceNo? do they need to run serially? //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
considerRunningFormAdjuster("onLoad", fieldName, values[fieldName]);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
const handleFieldChange = async (fieldName: string, newValue: any) =>
|
||||
{
|
||||
const field = formFields[fieldName];
|
||||
if (!field)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// map possible-value objects to ids - also capture their labels... //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
let actualNewValue = newValue;
|
||||
let possibleValueLabel: string = null;
|
||||
if (field.possibleValueProps)
|
||||
{
|
||||
actualNewValue = newValue ? newValue.id : null;
|
||||
possibleValueLabel = newValue ? newValue.label : null;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure formik has the value - and that we capture the possible-value label if needed //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
setFieldValue(fieldName, actualNewValue);
|
||||
if (field.possibleValueProps)
|
||||
{
|
||||
field.possibleValueProps.initialDisplayValue = possibleValueLabel;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////
|
||||
// run onChange adjuster if there is one //
|
||||
///////////////////////////////////////////
|
||||
considerRunningFormAdjuster("onChange", fieldName, actualNewValue);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
const considerRunningFormAdjuster = async (event: "onChange" | "onLoad", fieldName: string, newValue: any) =>
|
||||
{
|
||||
const field = formFields[fieldName];
|
||||
if (!field)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const materialDashboardFieldMetaData = field.fieldMetaData?.supplementalFieldMetaData?.get("materialDashboard");
|
||||
const adjuster = event == "onChange" ? materialDashboardFieldMetaData?.onChangeFormAdjuster : materialDashboardFieldMetaData?.onLoadFormAdjuster;
|
||||
if (!adjuster)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Running form adjuster for field ${fieldName} ${event} (value is: ${newValue})`);
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// disable fields temporarily while waiting on backend response //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
const fieldNamesToTempDisable: string[] = materialDashboardFieldMetaData?.fieldsToDisableWhileRunningAdjusters ?? []
|
||||
const previousIsEditableValues: {[key: string]: boolean} = {};
|
||||
if(fieldNamesToTempDisable.length > 0)
|
||||
{
|
||||
for (let oldFieldName in formFields)
|
||||
{
|
||||
if (fieldNamesToTempDisable.indexOf(oldFieldName) > -1)
|
||||
{
|
||||
previousIsEditableValues[oldFieldName] = formFields[oldFieldName].isEditable;
|
||||
formFields[oldFieldName].isEditable = false;
|
||||
}
|
||||
}
|
||||
|
||||
setFormAdjustmentCounter(formAdjustmentCounter + 1);
|
||||
setFormFields({...formFields});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// build request to backend for field adjustments //
|
||||
////////////////////////////////////////////////////
|
||||
const postBody = new FormData();
|
||||
postBody.append("event", event);
|
||||
postBody.append("fieldName", fieldName);
|
||||
postBody.append("newValue", newValue);
|
||||
postBody.append("allValues", JSON.stringify(values));
|
||||
const response = await qController.axiosRequest(
|
||||
{
|
||||
method: "post",
|
||||
url: `/material-dashboard-backend/form-adjuster/${encodeURIComponent(materialDashboardFieldMetaData.formAdjusterIdentifier)}/${event}`,
|
||||
data: postBody,
|
||||
headers: qController.defaultMultipartFormDataHeaders()
|
||||
});
|
||||
console.log("Form adjuster response: " + JSON.stringify(response));
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// un-disable any temp disabled fields from above //
|
||||
////////////////////////////////////////////////////
|
||||
if(fieldNamesToTempDisable.length > 0)
|
||||
{
|
||||
for (let oldFieldName in formFields)
|
||||
{
|
||||
if (fieldNamesToTempDisable.indexOf(oldFieldName) > -1)
|
||||
{
|
||||
formFields[oldFieldName].isEditable = previousIsEditableValues[oldFieldName];
|
||||
}
|
||||
}
|
||||
setFormFields({...formFields});
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// replace field definitions, if we have updates //
|
||||
///////////////////////////////////////////////////
|
||||
const updatedFields: { [fieldName: string]: QFieldMetaData } = response.updatedFieldMetaData;
|
||||
if(updatedFields)
|
||||
{
|
||||
for (let updatedFieldName in updatedFields)
|
||||
{
|
||||
const updatedField = new QFieldMetaData(updatedFields[updatedFieldName]);
|
||||
const dynamicField = DynamicFormUtils.getDynamicField(updatedField); // todo dynamicallyDisabledFields? second param...
|
||||
|
||||
const dynamicFieldInObject: any = {};
|
||||
dynamicFieldInObject[updatedFieldName] = dynamicField;
|
||||
let tableName = null;
|
||||
let processName = null;
|
||||
let displayValues = new Map();
|
||||
|
||||
DynamicFormUtils.addPossibleValueProps(dynamicFieldInObject, [updatedFields[updatedFieldName]], tableName, processName, displayValues);
|
||||
for (let oldFieldName in formFields)
|
||||
{
|
||||
if (oldFieldName == updatedFieldName)
|
||||
{
|
||||
formFields[updatedFieldName] = dynamicField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setFormAdjustmentCounter(formAdjustmentCounter + 2);
|
||||
setFormFields({...formFields});
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// update field values //
|
||||
/////////////////////////
|
||||
const updatedFieldValues: {[fieldName: string]: any} = response?.updatedFieldValues ?? {};
|
||||
for (let fieldNameToUpdate in updatedFieldValues)
|
||||
{
|
||||
setFieldValue(fieldNameToUpdate, updatedFieldValues[fieldNameToUpdate]);
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - track if a pvs field gets a value, but not a display value, and fetch it?? //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// set display values in PVS's if we have them //
|
||||
/////////////////////////////////////////////////
|
||||
const updatedFieldDisplayValues: {[fieldName: string]: any} = response?.updatedFieldDisplayValues ?? {};
|
||||
for (let fieldNameToUpdate in updatedFieldDisplayValues)
|
||||
{
|
||||
const fieldToUpdate = formFields[fieldNameToUpdate];
|
||||
if(fieldToUpdate?.possibleValueProps)
|
||||
{
|
||||
fieldToUpdate.possibleValueProps.initialDisplayValue = updatedFieldDisplayValues[fieldNameToUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// clear field values if we have them //
|
||||
////////////////////////////////////////
|
||||
const fieldsToClear: string[] = response?.fieldsToClear ?? [];
|
||||
for (let fieldToClear of fieldsToClear)
|
||||
{
|
||||
setFieldValue(fieldToClear, "");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box lineHeight={0}>
|
||||
@ -68,6 +277,8 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
return null;
|
||||
}
|
||||
|
||||
const display = field.fieldMetaData?.isHidden ? "none" : "initial";
|
||||
|
||||
if (values[fieldName] === undefined)
|
||||
{
|
||||
values[fieldName] = "";
|
||||
@ -100,7 +311,7 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid item lg={itemLG} xs={itemXS} sm={itemSM} flexDirection="column" key={fieldName}>
|
||||
<Grid item lg={itemLG} xs={itemXS} sm={itemSM} flexDirection="column" key={fieldName + "-" + formAdjustmentCounter}>
|
||||
{labelElement}
|
||||
<FileInputField field={field} record={record} errorMessage={errors[fieldName]} />
|
||||
</Grid>
|
||||
@ -119,7 +330,7 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
});
|
||||
|
||||
return (
|
||||
<Grid item lg={itemLG} xs={itemXS} sm={itemSM} key={fieldName}>
|
||||
<Grid item display={display} lg={itemLG} xs={itemXS} sm={itemSM} key={fieldName + "-" + formAdjustmentCounter}>
|
||||
{labelElement}
|
||||
<DynamicSelect
|
||||
fieldPossibleValueProps={field.possibleValueProps}
|
||||
@ -130,6 +341,7 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
bulkEditSwitchChangeHandler={bulkEditSwitchChanged}
|
||||
otherValues={otherValuesMap}
|
||||
useCase="form"
|
||||
onChange={(newValue: any) => handleFieldChange(fieldName, newValue)}
|
||||
/>
|
||||
{formattedHelpContent}
|
||||
</Grid>
|
||||
@ -140,7 +352,7 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
// everything else!! //
|
||||
///////////////////////
|
||||
return (
|
||||
<Grid item lg={itemLG} xs={itemXS} sm={itemSM} key={fieldName}>
|
||||
<Grid item display={display} lg={itemLG} xs={itemXS} sm={itemSM} key={fieldName + "-" + formAdjustmentCounter}>
|
||||
{labelElement}
|
||||
<QDynamicFormField
|
||||
id={field.name}
|
||||
@ -155,6 +367,7 @@ function QDynamicForm({formData, formLabel, bulkEditMode, bulkEditSwitchChangeHa
|
||||
bulkEditSwitchChangeHandler={bulkEditSwitchChanged}
|
||||
success={`${values[fieldName]}` !== "" && !errors[fieldName] && touched[fieldName]}
|
||||
formFieldObject={field}
|
||||
onChangeCallback={(newValue) => handleFieldChange(fieldName, newValue)}
|
||||
/>
|
||||
{formattedHelpContent}
|
||||
</Grid>
|
||||
|
@ -26,9 +26,9 @@ import Box from "@mui/material/Box";
|
||||
import Icon from "@mui/material/Icon";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import {GridRowsProp} from "@mui/x-data-grid-pro";
|
||||
import React from "react";
|
||||
import CustomWidthTooltip from "qqq/components/tooltips/CustomWidthTooltip";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
import React from "react";
|
||||
|
||||
interface CustomPaginationProps
|
||||
{
|
||||
@ -56,7 +56,7 @@ export default function CustomPaginationComponent({tableMetaData, rows, totalRec
|
||||
The number of rows shown on this screen may be greater than the number of {tableMetaData?.label} records
|
||||
that match your query, because you have included fields from other tables which may have
|
||||
more than one record associated with each {tableMetaData?.label}.
|
||||
</>
|
||||
</>;
|
||||
let distinctPart = isJoinMany ? (<Box display="inline" component="span" textAlign="right">
|
||||
({ValueUtils.safeToLocaleString(distinctRecords)} distinct<CustomWidthTooltip title={tooltipHTML}>
|
||||
<IconButton sx={{p: 0, pl: 0.25, mb: 0.25}}><Icon fontSize="small" sx={{fontSize: "1.125rem !important", color: "#9f9f9f"}}>info_outlined</Icon></IconButton>
|
||||
@ -66,13 +66,23 @@ export default function CustomPaginationComponent({tableMetaData, rows, totalRec
|
||||
|
||||
if (tableMetaData && !tableMetaData.capabilities.has(Capability.TABLE_COUNT))
|
||||
{
|
||||
if (loading)
|
||||
{
|
||||
return "Counting...";
|
||||
}
|
||||
|
||||
if (!rows || rows.length == 0)
|
||||
{
|
||||
return "No rows";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// to avoid a non-countable table showing (this is what data-grid did) 91-100 even if there were only 95 records, //
|
||||
// we'll do this... not quite good enough, but better than the original //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if (rows.length > 0 && rows.length < to - from)
|
||||
{
|
||||
to = from + rows.length;
|
||||
to = from + (rows.length - 1);
|
||||
}
|
||||
return (`Showing ${from.toLocaleString()} to ${to.toLocaleString()}`);
|
||||
}
|
||||
@ -102,14 +112,55 @@ export default function CustomPaginationComponent({tableMetaData, rows, totalRec
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// the `count` param that we pass to <TablePagination> below is very //
|
||||
// important - it drives which of the < and > (prev & next) buttons are //
|
||||
// enabled - and, it's a little tricky for tables where we don't do a count. //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
let countForTablePagination: number;
|
||||
if (tableMetaData && !tableMetaData.capabilities.has(Capability.TABLE_COUNT))
|
||||
{
|
||||
////////////////////////////////////////////
|
||||
// handle tables where count is disabled. //
|
||||
////////////////////////////////////////////
|
||||
if(!rows || rows.length == 0)
|
||||
{
|
||||
/////////////////////////////////////////////
|
||||
// if we have no rows, assume a count of 0 //
|
||||
/////////////////////////////////////////////
|
||||
countForTablePagination = 0;
|
||||
}
|
||||
if(rows.length < rowsPerPage)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the # of rows we have is less than the rowsPerPage, assume we're at the end of the query //
|
||||
// so, setting count to pageNo*rowsPer + rows.length - leaves prev. enabled, but disables next. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
countForTablePagination = (pageNumber * rowsPerPage) + rows.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// else, we don't know how many more pages there could be - so, just assume it's at least 1 more //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
countForTablePagination = ((pageNumber + 1) * rowsPerPage) + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cases where count is enabled - they work much more like we'd expect: //
|
||||
// if we don't know totalRecords (probably same as loading?) - use a -1, //
|
||||
// which lets us see < and > both active; else, use totalRecords when known. //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
countForTablePagination = totalRecords === null || totalRecords === undefined ? -1 : totalRecords;
|
||||
}
|
||||
|
||||
return (
|
||||
<TablePagination
|
||||
component="div"
|
||||
sx={{minWidth: "450px"}}
|
||||
// note - passing null here makes the 'to' param in the defaultLabelDisplayedRows also be null,
|
||||
// so pass a sentinel value of -1...
|
||||
count={totalRecords === null || totalRecords === undefined ? -1 : totalRecords}
|
||||
sx={{minWidth: "450px", "& .MuiTablePagination-displayedRows": {minWidth: "110px"}}}
|
||||
count={countForTablePagination}
|
||||
page={pageNumber}
|
||||
rowsPerPageOptions={[10, 25, 50, 100, 250]}
|
||||
rowsPerPage={rowsPerPage}
|
||||
|
@ -29,9 +29,9 @@ import Icon from "@mui/material/Icon";
|
||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import {QActionsMenuButton} from "qqq/components/buttons/DefaultButtons";
|
||||
import React, {useState} from "react";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {QActionsMenuButton} from "qqq/components/buttons/DefaultButtons";
|
||||
|
||||
interface QueryScreenActionMenuProps
|
||||
{
|
||||
@ -44,40 +44,35 @@ interface QueryScreenActionMenuProps
|
||||
processClicked: (process: QProcessMetaData) => void;
|
||||
}
|
||||
|
||||
QueryScreenActionMenu.defaultProps = {
|
||||
};
|
||||
QueryScreenActionMenu.defaultProps = {};
|
||||
|
||||
export default function QueryScreenActionMenu({metaData, tableMetaData, tableProcesses, bulkLoadClicked, bulkEditClicked, bulkDeleteClicked, processClicked}: QueryScreenActionMenuProps): JSX.Element
|
||||
{
|
||||
const [anchorElement, setAnchorElement] = useState(null)
|
||||
const [anchorElement, setAnchorElement] = useState(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const openActionsMenu = (event: any) =>
|
||||
{
|
||||
setAnchorElement(event.currentTarget);
|
||||
}
|
||||
};
|
||||
|
||||
const closeActionsMenu = () =>
|
||||
{
|
||||
setAnchorElement(null);
|
||||
}
|
||||
|
||||
const pushDividerIfNeeded = (menuItems: JSX.Element[]) =>
|
||||
{
|
||||
if (menuItems.length > 0)
|
||||
{
|
||||
menuItems.push(<Divider key="divider" />);
|
||||
}
|
||||
};
|
||||
|
||||
const runSomething = (handler: () => void) =>
|
||||
{
|
||||
closeActionsMenu();
|
||||
handler();
|
||||
}
|
||||
};
|
||||
|
||||
const menuItems: JSX.Element[] = [];
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// start with bulk actions, if user has permissions //
|
||||
//////////////////////////////////////////////////////
|
||||
if (tableMetaData.capabilities.has(Capability.TABLE_INSERT) && tableMetaData.insertPermission)
|
||||
{
|
||||
menuItems.push(<MenuItem key="bulkLoad" onClick={() => runSomething(bulkLoadClicked)}><ListItemIcon><Icon>library_add</Icon></ListItemIcon>Bulk Load</MenuItem>);
|
||||
@ -91,19 +86,7 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
|
||||
menuItems.push(<MenuItem key="bulkDelete" onClick={() => runSomething(bulkDeleteClicked)}><ListItemIcon><Icon>delete</Icon></ListItemIcon>Bulk Delete</MenuItem>);
|
||||
}
|
||||
|
||||
const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
|
||||
if (runRecordScriptProcess)
|
||||
{
|
||||
const process = runRecordScriptProcess;
|
||||
menuItems.push(<MenuItem key={process.name} onClick={() => runSomething(() => processClicked(process))}><ListItemIcon><Icon>{process.iconName ?? "arrow_forward"}</Icon></ListItemIcon>{process.label}</MenuItem>);
|
||||
}
|
||||
|
||||
menuItems.push(<MenuItem key="developerMode" onClick={() => navigate(`${metaData.getTablePathByName(tableMetaData.name)}/dev`)}><ListItemIcon><Icon>code</Icon></ListItemIcon>Developer Mode</MenuItem>);
|
||||
|
||||
if (tableProcesses && tableProcesses.length)
|
||||
{
|
||||
pushDividerIfNeeded(menuItems);
|
||||
}
|
||||
menuItems.push(<Divider key="divider1" />);
|
||||
|
||||
tableProcesses.sort((a, b) => a.label.localeCompare(b.label));
|
||||
tableProcesses.map((process) =>
|
||||
@ -111,11 +94,62 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
|
||||
menuItems.push(<MenuItem key={process.name} onClick={() => runSomething(() => processClicked(process))}><ListItemIcon><Icon>{process.iconName ?? "arrow_forward"}</Icon></ListItemIcon>{process.label}</MenuItem>);
|
||||
});
|
||||
|
||||
menuItems.push(<Divider key="divider2" />);
|
||||
|
||||
////////////////////////////////////////////
|
||||
// add processes that apply to all tables //
|
||||
////////////////////////////////////////////
|
||||
const materialDashboardInstanceMetaData = metaData.supplementalInstanceMetaData?.get("materialDashboard");
|
||||
if (materialDashboardInstanceMetaData)
|
||||
{
|
||||
const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
|
||||
if (processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
for (let processName of processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
const process = metaData?.processes.get(processName);
|
||||
if (process)
|
||||
{
|
||||
menuItems.push(<MenuItem key={process.name} onClick={() => runSomething(() => processClicked(process))}><ListItemIcon><Icon>{process.iconName ?? "arrow_forward"}</Icon></ListItemIcon>{process.label}</MenuItem>);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////
|
||||
// deprecated in favor of the above //
|
||||
//////////////////////////////////////
|
||||
const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
|
||||
if (runRecordScriptProcess)
|
||||
{
|
||||
const process = runRecordScriptProcess;
|
||||
menuItems.push(<MenuItem key={process.name} onClick={() => runSomething(() => processClicked(process))}><ListItemIcon><Icon>{process.iconName ?? "arrow_forward"}</Icon></ListItemIcon>{process.label}</MenuItem>);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// todo - any conditions around this? //
|
||||
////////////////////////////////////////
|
||||
menuItems.push(<MenuItem key="developerMode" onClick={() => navigate(`${metaData.getTablePathByName(tableMetaData.name)}/dev`)}><ListItemIcon><Icon>code</Icon></ListItemIcon>Developer Mode</MenuItem>);
|
||||
|
||||
if (menuItems.length === 0)
|
||||
{
|
||||
menuItems.push(<MenuItem key="notAvaialableNow" disabled><ListItemIcon><Icon>block</Icon></ListItemIcon><i>No actions available</i></MenuItem>);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// remove any duplicated dividers, and any dividers in the first or last slot //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
for (let i = 0; i < menuItems.length; i++)
|
||||
{
|
||||
if (menuItems[i].type == Divider && (i == 0 || (i > 0 && menuItems[i - 1].type == Divider) || i == menuItems.length - 1))
|
||||
{
|
||||
menuItems.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<QActionsMenuButton isOpen={anchorElement} onClickHandler={openActionsMenu} />
|
||||
@ -130,5 +164,5 @@ export default function QueryScreenActionMenu({metaData, tableMetaData, tablePro
|
||||
{menuItems}
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
|
||||
import {ApiVersion} from "@kingsrook/qqq-frontend-core/lib/controllers/QControllerV1";
|
||||
import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData";
|
||||
import {QWidgetMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QWidgetMetaData";
|
||||
import {QCriteriaOperator} from "@kingsrook/qqq-frontend-core/lib/model/query/QCriteriaOperator";
|
||||
@ -42,6 +43,7 @@ import QQueryColumns, {Column} from "qqq/models/query/QQueryColumns";
|
||||
import RecordQuery from "qqq/pages/records/query/RecordQuery";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import FilterUtils from "qqq/utils/qqq/FilterUtils";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import React, {useContext, useEffect, useRef, useState} from "react";
|
||||
|
||||
interface FilterAndColumnsSetupWidgetProps
|
||||
@ -80,6 +82,7 @@ unborderedButtonSX.opacity = "0.7";
|
||||
|
||||
|
||||
const qController = Client.getInstance();
|
||||
const qControllerV1 = Client.getInstanceV1();
|
||||
|
||||
/*******************************************************************************
|
||||
** Component for editing the main setup of a report - that is: filter & columns
|
||||
@ -90,13 +93,18 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
const [hideColumns] = useState(widgetData?.hideColumns);
|
||||
const [hidePreview] = useState(widgetData?.hidePreview);
|
||||
const [hideSortBy] = useState(widgetData?.hideSortBy);
|
||||
const [isEditable] = useState(widgetData?.overrideIsEditable ?? isEditableProp)
|
||||
const [isEditable] = useState(widgetData?.overrideIsEditable ?? isEditableProp);
|
||||
const [tableMetaData, setTableMetaData] = useState(null as QTableMetaData);
|
||||
|
||||
const [isApiVersioned] = useState(widgetData?.isApiVersioned);
|
||||
const [apiVersion, setApiVersion] = useState(null as ApiVersion | null);
|
||||
|
||||
const [filterFieldName] = useState(widgetData?.filterFieldName ?? "queryFilterJson");
|
||||
const [columnsFieldName] = useState(widgetData?.columnsFieldName ?? "columnsJson");
|
||||
|
||||
const [alertContent, setAlertContent] = useState(null as string);
|
||||
const [warning, setWarning] = useState(null as string);
|
||||
const [widgetFailureAlertContent, setWidgetFailureAlertContent] = useState(null as string);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we'll actually keep 2 copies of the query filter around here - //
|
||||
@ -114,7 +122,9 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
/////////////////////////////
|
||||
let columns: QQueryColumns = null;
|
||||
let usingDefaultEmptyFilter = false;
|
||||
let queryFilter = recordValues[filterFieldName] && JSON.parse(recordValues[filterFieldName]) as QQueryFilter;
|
||||
const rawFilterValueFromRecord = recordValues[filterFieldName];
|
||||
let queryFilter = rawFilterValueFromRecord &&
|
||||
((typeof rawFilterValueFromRecord == "string" ? JSON.parse(rawFilterValueFromRecord) : rawFilterValueFromRecord) as QQueryFilter);
|
||||
const defaultFilterFields = widgetData?.filterDefaultFieldNames;
|
||||
if (!queryFilter)
|
||||
{
|
||||
@ -167,16 +177,73 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
tableName = recordValues["tableName"];
|
||||
}
|
||||
|
||||
let version: ApiVersion | null = null;
|
||||
if (isApiVersioned)
|
||||
{
|
||||
let apiName = widgetData?.apiName;
|
||||
let apiPath = widgetData?.apiPath;
|
||||
let apiVersion = widgetData?.apiVersion;
|
||||
|
||||
if (!apiName && recordValues["apiName"])
|
||||
{
|
||||
apiName = recordValues["apiName"];
|
||||
}
|
||||
|
||||
if (!apiPath && recordValues["apiPath"])
|
||||
{
|
||||
apiPath = recordValues["apiPath"];
|
||||
}
|
||||
|
||||
if (!apiVersion && recordValues["apiVersion"])
|
||||
{
|
||||
apiVersion = recordValues["apiVersion"];
|
||||
}
|
||||
|
||||
if (!apiName || !apiPath || !apiVersion)
|
||||
{
|
||||
console.log("API Name/Path/Version not set, but widget isApiVersioned, so cannot load table meta data...");
|
||||
return;
|
||||
}
|
||||
|
||||
version = {name: apiName, path: apiPath, version: apiVersion};
|
||||
setApiVersion(version);
|
||||
}
|
||||
|
||||
if (tableName)
|
||||
{
|
||||
(async () =>
|
||||
{
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
try
|
||||
{
|
||||
const tableMetaData = await qControllerV1.loadTableMetaData(tableName, version);
|
||||
setTableMetaData(tableMetaData);
|
||||
|
||||
const queryFilterForFrontend = Object.assign({}, queryFilter);
|
||||
|
||||
let warnings: string[] = [];
|
||||
for (let i = 0; i < queryFilterForFrontend?.criteria?.length; i++)
|
||||
{
|
||||
const criteria = queryFilter.criteria[i];
|
||||
let [field, fieldTable] = TableUtils.getFieldAndTable(tableMetaData, criteria.fieldName);
|
||||
if(!field)
|
||||
{
|
||||
warnings.push("Removing non-existing filter field: " + criteria.fieldName);
|
||||
queryFilterForFrontend.criteria.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
await FilterUtils.cleanupValuesInFilerFromQueryString(qController, tableMetaData, queryFilterForFrontend);
|
||||
setFrontendQueryFilter(queryFilterForFrontend);
|
||||
|
||||
setWarning(warnings.join("; "));
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log(e);
|
||||
//@ts-ignore e.message
|
||||
setWidgetFailureAlertContent("Error preparing filter widget: " + (e.message ?? "Details not available."));
|
||||
}
|
||||
})();
|
||||
}
|
||||
}, [JSON.stringify(recordValues)]);
|
||||
@ -337,7 +404,7 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
/////////////////////////////////////////////////
|
||||
// add link to widget header for opening modal //
|
||||
/////////////////////////////////////////////////
|
||||
const selectTableFirstTooltipTitle = tableMetaData ? null : "You must select a table before you can set up your report filters and columns";
|
||||
const selectTableFirstTooltipTitle = tableMetaData ? null : `You must select a table${isApiVersioned ? " and API details" : ""} before you can set up your filters${hideColumns ? "" : " and columns"}`;
|
||||
const labelAdditionalElementsRight: JSX.Element[] = [];
|
||||
if (isEditable)
|
||||
{
|
||||
@ -351,6 +418,12 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
}
|
||||
}
|
||||
|
||||
if (widgetFailureAlertContent)
|
||||
{
|
||||
return (<Widget widgetMetaData={widgetMetaData}>
|
||||
<Alert severity="error" sx={{mt: 1.5, mb: 0.5}}>{widgetFailureAlertContent}</Alert>
|
||||
</Widget>);
|
||||
}
|
||||
|
||||
return (<Widget widgetMetaData={widgetMetaData} labelAdditionalElementsRight={labelAdditionalElementsRight}>
|
||||
<React.Fragment>
|
||||
@ -363,6 +436,9 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
<Collapse in={Boolean(alertContent)}>
|
||||
<Alert severity="error" sx={{mt: 1.5, mb: 0.5}} onClose={() => setAlertContent(null)}>{alertContent}</Alert>
|
||||
</Collapse>
|
||||
<Collapse in={Boolean(warning)}>
|
||||
<Alert severity="warning" sx={{mt: 1.5, mb: 0.5}} onClose={() => setWarning(null)}>{warning}</Alert>
|
||||
</Collapse>
|
||||
<Box pt="0.5rem">
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<h5>{label ?? "Query Filter"}</h5>
|
||||
@ -424,6 +500,7 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
isModal={true}
|
||||
initialQueryFilter={frontendQueryFilter}
|
||||
initialColumns={columns}
|
||||
apiVersion={apiVersion}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
@ -449,6 +526,7 @@ export default function FilterAndColumnsSetupWidget({isEditable: isEditableProp,
|
||||
isModal={true}
|
||||
initialQueryFilter={usingDefaultEmptyFilter ? null : frontendQueryFilter}
|
||||
initialColumns={columns}
|
||||
apiVersion={apiVersion}
|
||||
/>
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ export interface ChildRecordListData extends WidgetData
|
||||
defaultValuesForNewChildRecords?: { [fieldName: string]: any };
|
||||
disabledFieldsForNewChildRecords?: { [fieldName: string]: any };
|
||||
defaultValuesForNewChildRecordsFromParentFields?: { [fieldName: string]: string };
|
||||
omitFieldNames?: string[];
|
||||
}
|
||||
|
||||
interface Props
|
||||
@ -119,6 +120,19 @@ function RecordGridWidget({widgetMetaData, data, addNewRecordCallback, disableRo
|
||||
const childTablePath = data.tablePath ? data.tablePath + (data.tablePath.endsWith("/") ? "" : "/") : data.tablePath;
|
||||
const columns = DataGridUtils.setupGridColumns(tableMetaData, childTablePath, null, "bySection");
|
||||
|
||||
if (data.omitFieldNames)
|
||||
{
|
||||
for (let i = 0; i < columns.length; i++)
|
||||
{
|
||||
const column = columns[i];
|
||||
if (data.omitFieldNames.indexOf(column.field) > -1)
|
||||
{
|
||||
columns.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// capture all-columns to use for the export (before we might splice some away from the on-screen display) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -117,6 +117,11 @@ export default class QQueryColumns
|
||||
{
|
||||
const [field, tableForField] = TableUtils.getFieldAndTable(table, fieldName)
|
||||
|
||||
if(!field)
|
||||
{
|
||||
console.warn(`Couldn't find field ${fieldName} in tableMetaData - so not adding a column for it`);
|
||||
}
|
||||
|
||||
let column: Column;
|
||||
if(tableForField.name == table.name)
|
||||
{
|
||||
|
@ -72,6 +72,7 @@ import {ChildRecordListData} from "qqq/components/widgets/misc/RecordGridWidget"
|
||||
import BaseLayout from "qqq/layouts/BaseLayout";
|
||||
import ProcessWidgetBlockUtils from "qqq/pages/processes/ProcessWidgetBlockUtils";
|
||||
import {TABLE_VARIANT_LOCAL_STORAGE_KEY_ROOT} from "qqq/pages/records/query/RecordQuery";
|
||||
import {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
import ValueUtils from "qqq/utils/qqq/ValueUtils";
|
||||
@ -114,9 +115,14 @@ let formikSetTouched = ({}: any, touched: boolean): void =>
|
||||
|
||||
const cachedPossibleValueLabels: { [fieldName: string]: { [id: string | number]: string } } = {};
|
||||
|
||||
export interface SubFormPreSubmitCallbackResultType {maySubmit: boolean; values: {[name: string]: any}}
|
||||
export interface SubFormPreSubmitCallbackResultType
|
||||
{
|
||||
maySubmit: boolean;
|
||||
values: { [name: string]: any };
|
||||
}
|
||||
|
||||
type SubFormPreSubmitCallback = () => SubFormPreSubmitCallbackResultType;
|
||||
type SubFormPreSubmitCallbackWithName = {name: string, callback: SubFormPreSubmitCallback}
|
||||
type SubFormPreSubmitCallbackWithName = { name: string, callback: SubFormPreSubmitCallback }
|
||||
|
||||
function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, isReport, recordIds, closeModalHandler, forceReInit, overrideLabel}: Props): JSX.Element
|
||||
{
|
||||
@ -161,7 +167,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
const [previouslySeenUpdatedFieldMetaDataMap, setPreviouslySeenUpdatedFieldMetaDataMap] = useState(new Map<string, QFieldMetaData>);
|
||||
|
||||
const [renderedWidgets, setRenderedWidgets] = useState({} as { [step: string]: { [widgetName: string]: any } });
|
||||
const [controlCallbacks, setControlCallbacks] = useState({} as {[name: string]: () => void});
|
||||
const [controlCallbacks, setControlCallbacks] = useState({} as { [name: string]: () => void });
|
||||
const [subFormPreSubmitCallbacks, setSubFormPreSubmitCallbacks] = useState([] as SubFormPreSubmitCallbackWithName[]);
|
||||
|
||||
const {pageHeader, recordAnalytics, setPageHeader, helpHelpActive} = useContext(QContext);
|
||||
@ -237,7 +243,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
const bulkLoadFileMappingFormRef = useRef();
|
||||
const bulkLoadValueMappingFormRef = useRef();
|
||||
const bulkLoadProfileFormRef = useRef();
|
||||
const [bulkLoadValueMappingFormFields, setBulkLoadValueMappingFormFields] = useState([] as any[])
|
||||
const [bulkLoadValueMappingFormFields, setBulkLoadValueMappingFormFields] = useState([] as any[]);
|
||||
|
||||
const doesStepHaveComponent = (step: QFrontendStepMetaData, type: QComponentType): boolean =>
|
||||
{
|
||||
@ -699,10 +705,10 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
if (doesStepHaveComponent(activeStep, QComponentType.BULK_LOAD_FILE_MAPPING_FORM))
|
||||
{
|
||||
if(bulkLoadFileMappingFormRef?.current)
|
||||
if (bulkLoadFileMappingFormRef?.current)
|
||||
{
|
||||
// @ts-ignore ...
|
||||
addSubFormPreSubmitCallbacks("bulkLoadFileMappingForm", bulkLoadFileMappingFormRef?.current?.preSubmit)
|
||||
addSubFormPreSubmitCallbacks("bulkLoadFileMappingForm", bulkLoadFileMappingFormRef?.current?.preSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -711,10 +717,10 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
if (doesStepHaveComponent(activeStep, QComponentType.BULK_LOAD_VALUE_MAPPING_FORM))
|
||||
{
|
||||
if(bulkLoadValueMappingFormRef?.current)
|
||||
if (bulkLoadValueMappingFormRef?.current)
|
||||
{
|
||||
// @ts-ignore ...
|
||||
addSubFormPreSubmitCallbacks("bulkLoadValueMappingForm", bulkLoadValueMappingFormRef?.current?.preSubmit)
|
||||
addSubFormPreSubmitCallbacks("bulkLoadValueMappingForm", bulkLoadValueMappingFormRef?.current?.preSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -723,10 +729,10 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
if (doesStepHaveComponent(activeStep, QComponentType.BULK_LOAD_PROFILE_FORM))
|
||||
{
|
||||
if(bulkLoadProfileFormRef?.current)
|
||||
if (bulkLoadProfileFormRef?.current)
|
||||
{
|
||||
// @ts-ignore ...
|
||||
addSubFormPreSubmitCallbacks("bulkLoadProfileFormRef", bulkLoadProfileFormRef?.current?.preSubmit)
|
||||
addSubFormPreSubmitCallbacks("bulkLoadProfileFormRef", bulkLoadProfileFormRef?.current?.preSubmit);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1298,7 +1304,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// Help make this component's fields work with our formik form //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
if(activeStep && doesStepHaveComponent(activeStep, QComponentType.BULK_LOAD_VALUE_MAPPING_FORM))
|
||||
if (activeStep && doesStepHaveComponent(activeStep, QComponentType.BULK_LOAD_VALUE_MAPPING_FORM))
|
||||
{
|
||||
const fileValues = processValues.fileValues ?? [];
|
||||
const valueMapping = processValues.valueMapping ?? {};
|
||||
@ -1320,16 +1326,16 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
|
||||
const initialValue = valueMapping[fileValues[i]];
|
||||
|
||||
if(dynamicField.possibleValueProps)
|
||||
if (dynamicField.possibleValueProps)
|
||||
{
|
||||
dynamicField.possibleValueProps.initialDisplayValue = mappedValueLabels[initialValue]
|
||||
dynamicField.possibleValueProps.initialDisplayValue = mappedValueLabels[initialValue];
|
||||
}
|
||||
|
||||
addField(`${fieldFullName}.value.${i}`, dynamicField, initialValue, null)
|
||||
addField(`${fieldFullName}.value.${i}`, dynamicField, initialValue, null);
|
||||
fieldsForComponent.push(dynamicField);
|
||||
}
|
||||
|
||||
setBulkLoadValueMappingFormFields(fieldsForComponent)
|
||||
setBulkLoadValueMappingFormFields(fieldsForComponent);
|
||||
}
|
||||
|
||||
if (Object.keys(dynamicFormFields).length > 0)
|
||||
@ -1522,15 +1528,15 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
***************************************************************************/
|
||||
function addSubFormPreSubmitCallbacks(name: string, callback: SubFormPreSubmitCallback)
|
||||
{
|
||||
if(subFormPreSubmitCallbacks.findIndex(c => c.name == name) == -1)
|
||||
if (subFormPreSubmitCallbacks.findIndex(c => c.name == name) == -1)
|
||||
{
|
||||
const newCallbacks: SubFormPreSubmitCallbackWithName[] = []
|
||||
for(let i = 0; i < subFormPreSubmitCallbacks.length; i++)
|
||||
const newCallbacks: SubFormPreSubmitCallbackWithName[] = [];
|
||||
for (let i = 0; i < subFormPreSubmitCallbacks.length; i++)
|
||||
{
|
||||
newCallbacks[i] = subFormPreSubmitCallbacks[i];
|
||||
}
|
||||
newCallbacks.push({name, callback})
|
||||
setSubFormPreSubmitCallbacks(newCallbacks)
|
||||
newCallbacks.push({name, callback});
|
||||
setSubFormPreSubmitCallbacks(newCallbacks);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1620,7 +1626,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
setRenderedWidgets({});
|
||||
setSubFormPreSubmitCallbacks([]);
|
||||
setQJobRunning(null);
|
||||
setBackStepName(qJobComplete.backStep)
|
||||
setBackStepName(qJobComplete.backStep);
|
||||
|
||||
if (formikSetFieldValueFunction)
|
||||
{
|
||||
@ -1815,8 +1821,8 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
setProcessMetaData(processMetaData);
|
||||
setSteps(processMetaData.frontendSteps);
|
||||
|
||||
recordAnalytics({location: window.location, title: "Process: " + processMetaData?.label});
|
||||
recordAnalytics({category: "processEvents", action: "startProcess", label: processMetaData?.label});
|
||||
doRecordAnalytics({location: window.location, title: "Process: " + processMetaData?.label});
|
||||
doRecordAnalytics({category: "processEvents", action: "startProcess", label: processMetaData?.label});
|
||||
|
||||
if (processMetaData.tableName && !tableMetaData)
|
||||
{
|
||||
@ -1838,17 +1844,17 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
return;
|
||||
}
|
||||
|
||||
if(urlSearchParams.get("defaultProcessValues"))
|
||||
if (urlSearchParams.get("defaultProcessValues"))
|
||||
{
|
||||
if(!defaultProcessValues)
|
||||
if (!defaultProcessValues)
|
||||
{
|
||||
defaultProcessValues = {}
|
||||
defaultProcessValues = {};
|
||||
}
|
||||
|
||||
const values = JSON.parse(urlSearchParams.get("defaultProcessValues"));
|
||||
for (let key in values)
|
||||
{
|
||||
defaultProcessValues[key] = values[key]
|
||||
defaultProcessValues[key] = values[key];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1894,7 +1900,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
|
||||
setTimeout(async () =>
|
||||
{
|
||||
recordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
|
||||
doRecordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
|
||||
|
||||
const processResponse = await qController.processStep(
|
||||
processName,
|
||||
@ -1914,7 +1920,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
{
|
||||
setTimeout(async () =>
|
||||
{
|
||||
recordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
|
||||
doRecordAnalytics({category: "processEvents", action: "processStep", label: activeStep.label});
|
||||
|
||||
const processResponse = await Client.getInstance().processStep(
|
||||
processName,
|
||||
@ -1938,20 +1944,20 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
///////////////////////////////////////////////////////////////
|
||||
// run any sub-form pre-submit callbacks that are registered //
|
||||
///////////////////////////////////////////////////////////////
|
||||
for(let i = 0; i < subFormPreSubmitCallbacks.length; i++)
|
||||
for (let i = 0; i < subFormPreSubmitCallbacks.length; i++)
|
||||
{
|
||||
const {maySubmit, values: moreValues} = subFormPreSubmitCallbacks[i].callback();
|
||||
if(!maySubmit)
|
||||
if (!maySubmit)
|
||||
{
|
||||
console.log(`May not submit form, per callback: ${subFormPreSubmitCallbacks[i].name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if(moreValues)
|
||||
if (moreValues)
|
||||
{
|
||||
for (let key in moreValues)
|
||||
{
|
||||
values[key] = moreValues[key]
|
||||
values[key] = moreValues[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2026,7 +2032,7 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
setLoadingRecords(true);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -2055,6 +2061,21 @@ function ProcessRun({process, table, defaultProcessValues, isModal, isWidget, is
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function doRecordAnalytics(model: AnalyticsModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
recordAnalytics(model);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
console.log(`Error recording analytics: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
const formStyles: any = {};
|
||||
if (isWidget)
|
||||
{
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
import {ApiVersion} from "@kingsrook/qqq-frontend-core/lib/controllers/QControllerV1";
|
||||
import {Capability} from "@kingsrook/qqq-frontend-core/lib/model/metaData/Capability";
|
||||
import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData";
|
||||
import {QInstance} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QInstance";
|
||||
@ -69,9 +70,9 @@ import RecordQueryView from "qqq/models/query/RecordQueryView";
|
||||
import ProcessRun from "qqq/pages/processes/ProcessRun";
|
||||
import ColumnStats from "qqq/pages/records/query/ColumnStats";
|
||||
import DataGridUtils from "qqq/utils/DataGridUtils";
|
||||
import {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUtils";
|
||||
import Client from "qqq/utils/qqq/Client";
|
||||
import FilterUtils from "qqq/utils/qqq/FilterUtils";
|
||||
import {AnalyticsModel} from "qqq/utils/GoogleAnalyticsUtils";
|
||||
import ProcessUtils from "qqq/utils/qqq/ProcessUtils";
|
||||
import {SavedViewUtils} from "qqq/utils/qqq/SavedViewUtils";
|
||||
import TableUtils from "qqq/utils/qqq/TableUtils";
|
||||
@ -89,6 +90,7 @@ export type QueryScreenUsage = "queryScreen" | "reportSetup"
|
||||
interface Props
|
||||
{
|
||||
table?: QTableMetaData;
|
||||
apiVersion?: ApiVersion;
|
||||
launchProcess?: QProcessMetaData;
|
||||
usage?: QueryScreenUsage;
|
||||
isModal?: boolean;
|
||||
@ -101,9 +103,10 @@ interface Props
|
||||
///////////////////////////////////////////////////////
|
||||
// define possible values for our pageState variable //
|
||||
///////////////////////////////////////////////////////
|
||||
type PageState = "initial" | "loadingMetaData" | "loadedMetaData" | "loadingView" | "loadedView" | "preparingGrid" | "ready";
|
||||
type PageState = "initial" | "loadingMetaData" | "loadedMetaData" | "loadingView" | "loadedView" | "preparingGrid" | "ready" | "error";
|
||||
|
||||
const qController = Client.getInstance();
|
||||
const qControllerV1 = Client.getInstanceV1();
|
||||
|
||||
/*******************************************************************************
|
||||
** function to produce standard version of the screen while we're "loading"
|
||||
@ -127,7 +130,7 @@ const getLoadingScreen = (isModal: boolean) =>
|
||||
**
|
||||
** Yuge component. The best. Lots of very smart people are saying so.
|
||||
*******************************************************************************/
|
||||
const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariables, initialQueryFilter, initialColumns}: Props, ref) =>
|
||||
const RecordQuery = forwardRef(({table, apiVersion, usage, isModal, isPreview, allowVariables, initialQueryFilter, initialColumns}: Props, ref) =>
|
||||
{
|
||||
const tableName = table.name;
|
||||
const [searchParams] = useSearchParams();
|
||||
@ -979,7 +982,8 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
}
|
||||
|
||||
let includeDistinct = isJoinMany(tableMetaData, getVisibleJoinTables());
|
||||
qController.count(tableName, filterForBackend, queryJoins, includeDistinct, tableVariant).then(([count, distinctCount]) =>
|
||||
// qController.count(tableName, filterForBackend, queryJoins, includeDistinct, tableVariant).then(([count, distinctCount]) =>
|
||||
qControllerV1.count(tableName, apiVersion, filterForBackend, queryJoins, includeDistinct, tableVariant).then(([count, distinctCount]) =>
|
||||
{
|
||||
console.log(`Received count results for query ${thisQueryId}: ${count} ${distinctCount}`);
|
||||
countResults[thisQueryId] = [];
|
||||
@ -998,7 +1002,8 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
|
||||
setLastFetchedQFilterJSON(JSON.stringify(queryFilter));
|
||||
setLastFetchedVariant(tableVariant);
|
||||
qController.query(tableName, filterForBackend, queryJoins, tableVariant).then((results) =>
|
||||
// qController.query(tableName, filterForBackend, queryJoins, tableVariant).then((results) =>
|
||||
qControllerV1.query(tableName, apiVersion, filterForBackend, queryJoins, tableVariant).then((results) =>
|
||||
{
|
||||
console.log(`Received results for query ${thisQueryId}`);
|
||||
queryResults[thisQueryId] = results;
|
||||
@ -1141,6 +1146,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
const handlePageNumberChange = (page: number) =>
|
||||
{
|
||||
setPageNumber(page);
|
||||
setLoading(true);
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
@ -1149,6 +1155,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
const handleRowsPerPageChange = (size: number) =>
|
||||
{
|
||||
setRowsPerPage(size);
|
||||
setLoading(true);
|
||||
|
||||
view.rowsPerPage = size;
|
||||
doSetView(view);
|
||||
@ -1672,8 +1679,9 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
{
|
||||
if (savedViewRecord == null)
|
||||
{
|
||||
console.log("doSetCurrentView called with a null view record - calling doClearCurrentSavedView instead.");
|
||||
console.log("doSetCurrentView called with a null view record - calling doClearCurrentSavedView, and activating tableDefaultView instead.");
|
||||
doClearCurrentSavedView();
|
||||
activateView(buildTableDefaultView(tableMetaData));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2435,7 +2443,10 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
const metaData = await qController.loadMetaData();
|
||||
setMetaData(metaData);
|
||||
|
||||
const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
try
|
||||
{
|
||||
// const tableMetaData = await qController.loadTableMetaData(tableName);
|
||||
const tableMetaData = await qControllerV1.loadTableMetaData(tableName, apiVersion);
|
||||
setTableMetaData(tableMetaData);
|
||||
setTableLabel(tableMetaData.label);
|
||||
|
||||
@ -2452,6 +2463,13 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
setTableDefaultView(newDefaultView);
|
||||
|
||||
setPageState("loadedMetaData");
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
setPageState("error");
|
||||
//@ts-ignore e.message
|
||||
setAlertContent("Error loading table: " + e?.message ?? "Details not available.");
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
@ -2719,6 +2737,16 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// render an error screen (alert) if needed //
|
||||
//////////////////////////////////////////////
|
||||
if (pageState == "error")
|
||||
{
|
||||
console.log(`page state is ${pageState}... rendering an alert...`);
|
||||
const errorBody = <Box py={3}><Alert severity="error">{alertContent}</Alert></Box>;
|
||||
return isModal ? errorBody : <BaseLayout>{errorBody}</BaseLayout>;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// render a loading screen if the page state isn't ready //
|
||||
///////////////////////////////////////////////////////////
|
||||
@ -3069,6 +3097,7 @@ const RecordQuery = forwardRef(({table, usage, isModal, isPreview, allowVariable
|
||||
|
||||
RecordQuery.defaultProps = {
|
||||
table: null,
|
||||
apiVersion: null,
|
||||
usage: "queryScreen",
|
||||
launchProcess: null,
|
||||
isModal: false,
|
||||
|
@ -440,6 +440,34 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
};
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
function getGenericProcesses(metaData: QInstance)
|
||||
{
|
||||
const genericProcesses: QProcessMetaData[] = [];
|
||||
const materialDashboardInstanceMetaData = metaData?.supplementalInstanceMetaData?.get("materialDashboard");
|
||||
if (materialDashboardInstanceMetaData)
|
||||
{
|
||||
const processNamesToAddToAllQueryAndViewScreens = materialDashboardInstanceMetaData.processNamesToAddToAllQueryAndViewScreens;
|
||||
if (processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
for (let processName of processNamesToAddToAllQueryAndViewScreens)
|
||||
{
|
||||
genericProcesses.push(metaData?.processes?.get(processName));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////
|
||||
// deprecated //
|
||||
////////////////
|
||||
genericProcesses.push(metaData?.processes.get("runRecordScript"));
|
||||
}
|
||||
return genericProcesses;
|
||||
}
|
||||
|
||||
if (!asyncLoadInited)
|
||||
{
|
||||
setAsyncLoadInited(true);
|
||||
@ -472,11 +500,16 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
// load processes that the routing needs to respect //
|
||||
//////////////////////////////////////////////////////
|
||||
const allTableProcesses = ProcessUtils.getProcessesForTable(metaData, tableName, true); // these include hidden ones (e.g., to find the bulks)
|
||||
const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
|
||||
if (runRecordScriptProcess)
|
||||
const genericProcesses = getGenericProcesses(metaData);
|
||||
|
||||
for (let genericProcess of genericProcesses)
|
||||
{
|
||||
allTableProcesses.unshift(runRecordScriptProcess);
|
||||
if (genericProcess)
|
||||
{
|
||||
allTableProcesses.unshift(genericProcess);
|
||||
}
|
||||
}
|
||||
|
||||
setAllTableProcesses(allTableProcesses);
|
||||
|
||||
if (launchingProcess)
|
||||
@ -726,7 +759,6 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
|
||||
let hasEditOrDelete = (table.capabilities.has(Capability.TABLE_UPDATE) && table.editPermission) || (table.capabilities.has(Capability.TABLE_DELETE) && table.deletePermission);
|
||||
|
||||
const runRecordScriptProcess = metaData?.processes.get("runRecordScript");
|
||||
|
||||
const renderActionsMenu = (
|
||||
<Menu
|
||||
@ -785,11 +817,14 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
))}
|
||||
{(tableProcesses?.length > 0 || hasEditOrDelete) && <Divider />}
|
||||
{
|
||||
runRecordScriptProcess &&
|
||||
<MenuItem key={runRecordScriptProcess.name} onClick={() => processClicked(runRecordScriptProcess)}>
|
||||
<ListItemIcon><Icon>{runRecordScriptProcess.iconName ?? "arrow_forward"}</Icon></ListItemIcon>
|
||||
{runRecordScriptProcess.label}
|
||||
getGenericProcesses(metaData).map((process) =>
|
||||
(
|
||||
process &&
|
||||
<MenuItem key={process.name} onClick={() => processClicked(process)}>
|
||||
<ListItemIcon><Icon>{process.iconName ?? "arrow_forward"}</Icon></ListItemIcon>
|
||||
{process.label}
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
<MenuItem onClick={() => navigate("dev")}>
|
||||
<ListItemIcon><Icon>code</Icon></ListItemIcon>
|
||||
@ -969,7 +1004,7 @@ function RecordView({table, record: overrideRecord, launchProcess}: Props): JSX.
|
||||
{
|
||||
notFoundMessage
|
||||
?
|
||||
<Alert color="error" sx={{mb: 3}}>{notFoundMessage}</Alert>
|
||||
<Alert color="error" sx={{mb: 3}} icon={<Icon>warning</Icon>}>{notFoundMessage}</Alert>
|
||||
:
|
||||
<Box pb={3}>
|
||||
{
|
||||
|
@ -303,10 +303,15 @@ input[type="search"]::-webkit-search-results-decoration
|
||||
.MuiTablePagination-root .MuiSvgIcon-root
|
||||
{
|
||||
display: inline;
|
||||
color: gray;
|
||||
color: rgba(0, 0, 0, 0.54);
|
||||
right: 0.125rem;
|
||||
}
|
||||
|
||||
.MuiTablePagination-root .Mui-disabled .MuiSvgIcon-root
|
||||
{
|
||||
color: rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
.devDocumentation ul > li
|
||||
{
|
||||
margin-left: 30px;
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
import {QController} from "@kingsrook/qqq-frontend-core/lib/controllers/QController";
|
||||
import {QControllerV1} from "@kingsrook/qqq-frontend-core/lib/controllers/QControllerV1";
|
||||
import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException";
|
||||
|
||||
/*******************************************************************************
|
||||
@ -29,6 +30,7 @@ import {QException} from "@kingsrook/qqq-frontend-core/lib/exceptions/QException
|
||||
class Client
|
||||
{
|
||||
private static qController: QController;
|
||||
private static qControllerV1: QControllerV1;
|
||||
private static unauthorizedCallback: () => void;
|
||||
|
||||
private static handleException(exception: QException)
|
||||
@ -54,6 +56,22 @@ class Client
|
||||
return this.qController;
|
||||
}
|
||||
|
||||
public static getInstanceV1(path: string = "/qqq/v1")
|
||||
{
|
||||
if (this.qControllerV1 == null)
|
||||
{
|
||||
this.qControllerV1 = new QControllerV1(path, this.handleException);
|
||||
}
|
||||
|
||||
return this.qControllerV1;
|
||||
}
|
||||
|
||||
public static setGotAuthenticationInAllControllers()
|
||||
{
|
||||
Client.getInstance().setGotAuthentication();
|
||||
Client.getInstanceV1().setGotAuthentication();
|
||||
}
|
||||
|
||||
static setUnauthorizedCallback(unauthorizedCallback: () => void)
|
||||
{
|
||||
Client.unauthorizedCallback = unauthorizedCallback;
|
||||
|
@ -108,6 +108,12 @@ class FilterUtils
|
||||
const criteria = queryFilter.criteria[i];
|
||||
let [field, fieldTable] = TableUtils.getFieldAndTable(tableMetaData, criteria.fieldName);
|
||||
|
||||
if(!field)
|
||||
{
|
||||
console.warn(`Field ${criteria.fieldName} not found in tableMetaData - unable to clean up values for it..`);
|
||||
return;
|
||||
}
|
||||
|
||||
let values = criteria.values;
|
||||
let hasFilterVariable = false;
|
||||
|
||||
@ -401,21 +407,21 @@ class FilterUtils
|
||||
{
|
||||
const expression = new ThisOrLastPeriodExpression(value);
|
||||
let startOfPrefix = "";
|
||||
if (fieldMetaData.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS")
|
||||
if (fieldMetaData?.type == QFieldType.DATE_TIME || expression.timeUnit != "DAYS")
|
||||
{
|
||||
startOfPrefix = "start of ";
|
||||
}
|
||||
labels.push(`${startOfPrefix}${expression.toString()}`);
|
||||
}
|
||||
else if (fieldMetaData.type == QFieldType.BOOLEAN)
|
||||
else if (fieldMetaData?.type == QFieldType.BOOLEAN)
|
||||
{
|
||||
labels.push(value == true ? "yes" : "no");
|
||||
}
|
||||
else if (fieldMetaData.type == QFieldType.DATE_TIME)
|
||||
else if (fieldMetaData?.type == QFieldType.DATE_TIME)
|
||||
{
|
||||
labels.push(ValueUtils.formatDateTime(value));
|
||||
}
|
||||
else if (fieldMetaData.type == QFieldType.DATE)
|
||||
else if (fieldMetaData?.type == QFieldType.DATE)
|
||||
{
|
||||
labels.push(ValueUtils.formatDate(value));
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ function QFMDBridgeForm({fields, record, handleChange, handleSubmit}: QFMDBridge
|
||||
const value = record.values.get(field.name);
|
||||
if (field.possibleValueSourceName && value)
|
||||
{
|
||||
const possibleValues = await qController.possibleValues(null, null, field.possibleValueSourceName, null, [value], null, record.values, "form");
|
||||
const possibleValues = await qController.possibleValues(null, null, field.possibleValueSourceName, null, [value], [], record.values, "form");
|
||||
if (possibleValues && possibleValues.length > 0)
|
||||
{
|
||||
record.displayValues.set(field.name, possibleValues[0].label);
|
||||
@ -102,7 +102,7 @@ function QFMDBridgeForm({fields, record, handleChange, handleSubmit}: QFMDBridge
|
||||
|
||||
if (!loaded)
|
||||
{
|
||||
return (<div>Loading...</div>);
|
||||
return (<Box py={"1rem"}>Loading...</Box>);
|
||||
}
|
||||
|
||||
const {
|
||||
@ -111,6 +111,18 @@ function QFMDBridgeForm({fields, record, handleChange, handleSubmit}: QFMDBridge
|
||||
} = DynamicFormUtils.getFormData(fields);
|
||||
DynamicFormUtils.addPossibleValueProps(dynamicFormFields, fields, null, null, record ? record.displayValues : new Map());
|
||||
|
||||
const otherValuesMap = new Map<string, any>();
|
||||
record.values.forEach((value, key) => otherValuesMap.set(key, value));
|
||||
|
||||
for (let fieldName in dynamicFormFields)
|
||||
{
|
||||
const dynamicFormField = dynamicFormFields[fieldName];
|
||||
if (dynamicFormField.possibleValueProps)
|
||||
{
|
||||
dynamicFormField.possibleValueProps.otherValues = otherValuesMap;
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// re-introduce these two context providers, in case the child calls this //
|
||||
// method under a different root... maybe this should be optional per a param? //
|
||||
@ -190,8 +202,14 @@ function QFMDBridgeWidget({widgetName, tableName, record, entityPrimaryKey, acti
|
||||
const qController = Client.getInstance();
|
||||
const qInstance = await qController.loadMetaData();
|
||||
|
||||
const queryStringParts: string[] = [];
|
||||
for (let key of record?.values?.keys())
|
||||
{
|
||||
queryStringParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(record.values.get(key))}`);
|
||||
}
|
||||
|
||||
setWidgetMetaData(qInstance.widgets.get(widgetName));
|
||||
setWidgetData(await qController.widget(widgetName, null)); // todo queryParams... ?
|
||||
setWidgetData(await qController.widget(widgetName, queryStringParts.join("&")));
|
||||
|
||||
setReady(true);
|
||||
})();
|
||||
@ -199,7 +217,7 @@ function QFMDBridgeWidget({widgetName, tableName, record, entityPrimaryKey, acti
|
||||
|
||||
if (!ready)
|
||||
{
|
||||
return (<div>Loading...</div>);
|
||||
return (<Box py={"1rem"}>Loading...</Box>);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -59,4 +59,5 @@ module.exports = function (app)
|
||||
app.use("/*api", getRequestHandler());
|
||||
app.use("/qqq/*", getRequestHandler());
|
||||
app.use("/dynamic-qfmd-components/*", getRequestHandler());
|
||||
app.use("/material-dashboard-backend/*", getRequestHandler());
|
||||
};
|
||||
|
@ -181,7 +181,12 @@ public class QBaseSeleniumTest
|
||||
.withRouteToFile("/metaData/table/city", "metaData/table/person.json")
|
||||
.withRouteToFile("/metaData/table/script", "metaData/table/script.json")
|
||||
.withRouteToFile("/metaData/table/scriptRevision", "metaData/table/scriptRevision.json")
|
||||
.withRouteToFile("/qqq/v1/metaData/table/person", "qqq/v1/metaData/table/person.json")
|
||||
.withRouteToFile("/qqq/v1/metaData/table/city", "qqq/v1/metaData/table/city.json")
|
||||
.withRouteToFile("/qqq/v1/metaData/table/script", "qqq/v1/metaData/table/script.json")
|
||||
.withRouteToFile("/qqq/v1/metaData/table/scriptRevision", "qqq/v1/metaData/table/scriptRevision.json")
|
||||
.withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json");
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -46,7 +46,9 @@ public class AppPageNavTest extends QBaseSeleniumTest
|
||||
.withRouteToString("/widget/QuickSightChartRenderer", """
|
||||
{"url": "http://www.google.com"}""")
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/city/count", "data/city/count.json");
|
||||
.withRouteToFile("/data/city/count", "data/city/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/city/count", "qqq/v1/table/city/count.json");
|
||||
}
|
||||
|
||||
|
||||
|
@ -63,6 +63,8 @@ public class BulkEditTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/count", "data/person/count.json");
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/query", "data/person/index.json");
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/variants", "data/person/variants.json");
|
||||
qSeleniumJavalin.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json");
|
||||
qSeleniumJavalin.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json");
|
||||
qSeleniumJavalin.withRouteToString("/processes/person.bulkEdit/74a03a7d-2f53-4784-9911-3a21f7646c43/records", "[]");
|
||||
}
|
||||
|
||||
|
@ -51,12 +51,15 @@ public class SavedReportTest extends QBaseSeleniumTest
|
||||
super.addJavalinRoutes(qSeleniumJavalin);
|
||||
qSeleniumJavalin
|
||||
.withRouteToFile("/metaData/table/savedReport", "metaData/table/savedReport.json")
|
||||
.withRouteToFile("/qqq/v1/metaData/table/savedReport", "qqq/v1/metaData/table/savedReport.json")
|
||||
.withRouteToFile("/widget/reportSetupWidget", "widget/reportSetupWidget.json")
|
||||
.withRouteToFile("/widget/pivotTableSetupWidget", "widget/pivotTableSetupWidget.json")
|
||||
.withRouteToFile("/data/savedReport/possibleValues/tableName", "data/savedReport/possibleValues/tableName.json")
|
||||
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/person/query", "data/person/index.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json")
|
||||
;
|
||||
}
|
||||
|
||||
@ -93,8 +96,8 @@ public class SavedReportTest extends QBaseSeleniumTest
|
||||
////////////////////////////////////////////////////
|
||||
qSeleniumJavalin.beginCapture();
|
||||
qSeleniumLib.waitForSelectorContaining("button", "Edit Filters and Columns").click();
|
||||
qSeleniumJavalin.waitForCapturedPath("/data/person/count");
|
||||
qSeleniumJavalin.waitForCapturedPath("/data/person/query");
|
||||
qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/count");
|
||||
qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/query");
|
||||
qSeleniumJavalin.endCapture();
|
||||
|
||||
QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib);
|
||||
|
@ -53,6 +53,8 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/person/query", "data/person/index.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json")
|
||||
.withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json")
|
||||
.withRouteToFile("/data/person/variants", "data/person/variants.json")
|
||||
.withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json");
|
||||
|
@ -53,6 +53,8 @@ public class QueryScreenFilterInUrlBasicModeTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/person/query", "data/person/index.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json")
|
||||
.withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json")
|
||||
.withRouteToFile("/data/person/variants", "data/person/variants.json")
|
||||
.withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json");
|
||||
|
@ -48,6 +48,8 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin
|
||||
.withRouteToFile("/data/person/count", "data/person/count.json")
|
||||
.withRouteToFile("/data/person/query", "data/person/index.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json")
|
||||
.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json")
|
||||
.withRouteToFile("/data/person/variants", "data/person/variants.json")
|
||||
.withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json")
|
||||
.withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json");
|
||||
@ -79,8 +81,8 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
///////////////////////////////////////////////////////////////////
|
||||
String idEquals1FilterSubstring = """
|
||||
{"fieldName":"id","operator":"EQUALS","values":["1"]}""";
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/count", idEquals1FilterSubstring);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", idEquals1FilterSubstring);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/count", idEquals1FilterSubstring);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", idEquals1FilterSubstring);
|
||||
qSeleniumJavalin.endCapture();
|
||||
|
||||
///////////////////////////////////////
|
||||
@ -99,8 +101,8 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// assert that query & count both no longer have the filter value //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
CapturedContext capturedCount = qSeleniumJavalin.waitForCapturedPath("/data/person/count");
|
||||
CapturedContext capturedQuery = qSeleniumJavalin.waitForCapturedPath("/data/person/query");
|
||||
CapturedContext capturedCount = qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/count");
|
||||
CapturedContext capturedQuery = qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/query");
|
||||
assertThat(capturedCount).extracting("body").asString().doesNotContain(idEquals1FilterSubstring);
|
||||
assertThat(capturedQuery).extracting("body").asString().doesNotContain(idEquals1FilterSubstring);
|
||||
qSeleniumJavalin.endCapture();
|
||||
@ -132,9 +134,9 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
String expectedFilterContents2 = """
|
||||
"booleanOperator":"OR\"""";
|
||||
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectedFilterContents0);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectedFilterContents1);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectedFilterContents2);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectedFilterContents0);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectedFilterContents1);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectedFilterContents2);
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
||||
@ -208,7 +210,7 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.setBasicBooleanFilter(fieldLabel, operatorLabel);
|
||||
queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
||||
@ -222,7 +224,7 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
qSeleniumJavalin.beginCapture();
|
||||
queryScreenLib.setBasicFilterPossibleValues(fieldLabel, operatorLabel, values);
|
||||
queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
||||
@ -268,7 +270,7 @@ public class QueryScreenTest extends QBaseSeleniumTest
|
||||
queryScreenLib.addAdvancedQueryFilterInput(0, fieldLabel, operatorLabel, value, null);
|
||||
qSeleniumLib.clickBackdrop();
|
||||
queryScreenLib.waitForAdvancedQueryStringMatchingRegex(expectQueryStringRegex);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/qqq/v1/table/person/query", expectFilterJsonContains);
|
||||
qSeleniumJavalin.endCapture();
|
||||
queryScreenLib.clickAdvancedFilterClearIcon();
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ public class SavedViewsTest extends QBaseSeleniumTest
|
||||
super.addJavalinRoutes(qSeleniumJavalin);
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/count", "data/person/count.json");
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/query", "data/person/index.json");
|
||||
qSeleniumJavalin.withRouteToFile("/qqq/v1/table/person/count", "qqq/v1/table/person/count.json");
|
||||
qSeleniumJavalin.withRouteToFile("/qqq/v1/table/person/query", "qqq/v1/table/person/index.json");
|
||||
qSeleniumJavalin.withRouteToFile("/data/person/*", "data/person/1701.json");
|
||||
}
|
||||
|
||||
@ -135,7 +137,7 @@ public class SavedViewsTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.waitForCondition("Current URL should have filter id", () -> driver.getCurrentUrl().endsWith("/person/savedView/2"));
|
||||
queryScreenLib.assertSavedViewNameOnScreen("Some People");
|
||||
qSeleniumLib.waitForSelectorContaining("DIV", "Unsaved Changes");
|
||||
CapturedContext capturedContext = qSeleniumJavalin.waitForCapturedPath("/data/person/query");
|
||||
CapturedContext capturedContext = qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/query");
|
||||
assertTrue(capturedContext.getBody().contains("Kelkhoff"));
|
||||
qSeleniumJavalin.endCapture();
|
||||
|
||||
@ -162,7 +164,7 @@ public class SavedViewsTest extends QBaseSeleniumTest
|
||||
qSeleniumLib.waitForSelectorContaining("A", "Person").click();
|
||||
qSeleniumLib.waitForCondition("Current URL should not have filter id", () -> !driver.getCurrentUrl().endsWith("/person/savedView/2"));
|
||||
qSeleniumLib.waitForSelectorContaining("BUTTON", "Save View As");
|
||||
capturedContext = qSeleniumJavalin.waitForCapturedPath("/data/person/query");
|
||||
capturedContext = qSeleniumJavalin.waitForCapturedPath("/qqq/v1/table/person/query");
|
||||
assertTrue(capturedContext.getBody().matches("(?s).*id.*LESS_THAN.*10.*"));
|
||||
qSeleniumJavalin.endCapture();
|
||||
}
|
||||
|
166
src/test/resources/fixtures/qqq/v1/metaData/table/person.json
Normal file
166
src/test/resources/fixtures/qqq/v1/metaData/table/person.json
Normal file
@ -0,0 +1,166 @@
|
||||
{
|
||||
"name": "person",
|
||||
"label": "Person",
|
||||
"isHidden": false,
|
||||
"primaryKeyField": "id",
|
||||
"iconName": "person",
|
||||
"deletePermission": true,
|
||||
"editPermission": true,
|
||||
"insertPermission": true,
|
||||
"readPermission": true,
|
||||
"fields": {
|
||||
"firstName": {
|
||||
"name": "firstName",
|
||||
"label": "First Name",
|
||||
"type": "STRING",
|
||||
"isRequired": true,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"lastName": {
|
||||
"name": "lastName",
|
||||
"label": "Last Name",
|
||||
"type": "STRING",
|
||||
"isRequired": true,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"annualSalary": {
|
||||
"name": "annualSalary",
|
||||
"label": "Annual Salary",
|
||||
"type": "DECIMAL",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "$%,.2f"
|
||||
},
|
||||
"modifyDate": {
|
||||
"name": "modifyDate",
|
||||
"label": "Modify Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"daysWorked": {
|
||||
"name": "daysWorked",
|
||||
"label": "Days Worked",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%,d"
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"birthDate": {
|
||||
"name": "birthDate",
|
||||
"label": "Birth Date",
|
||||
"type": "DATE",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"isEmployed": {
|
||||
"name": "isEmployed",
|
||||
"label": "Is Employed",
|
||||
"type": "BOOLEAN",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"homeCityId": {
|
||||
"name": "homeCityId",
|
||||
"label": "Home City",
|
||||
"type": "INTEGER",
|
||||
"possibleValueSourceName": "city",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"label": "Email",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"createDate": {
|
||||
"name": "createDate",
|
||||
"label": "Create Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"name": "identity",
|
||||
"label": "Identity",
|
||||
"tier": "T1",
|
||||
"fieldNames": [
|
||||
"id",
|
||||
"firstName",
|
||||
"lastName"
|
||||
],
|
||||
"icon": {
|
||||
"name": "badge"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "basicInfo",
|
||||
"label": "Basic Info",
|
||||
"tier": "T2",
|
||||
"fieldNames": [
|
||||
"email",
|
||||
"birthDate"
|
||||
],
|
||||
"icon": {
|
||||
"name": "dataset"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "employmentInfo",
|
||||
"label": "Employment Info",
|
||||
"tier": "T2",
|
||||
"fieldNames": [
|
||||
"isEmployed",
|
||||
"annualSalary",
|
||||
"daysWorked"
|
||||
],
|
||||
"icon": {
|
||||
"name": "work"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "dates",
|
||||
"label": "Dates",
|
||||
"tier": "T3",
|
||||
"fieldNames": [
|
||||
"createDate",
|
||||
"modifyDate"
|
||||
],
|
||||
"icon": {
|
||||
"name": "calendar_month"
|
||||
},
|
||||
"isHidden": false
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"TABLE_COUNT",
|
||||
"TABLE_GET",
|
||||
"TABLE_QUERY",
|
||||
"TABLE_DELETE",
|
||||
"TABLE_INSERT",
|
||||
"TABLE_UPDATE"
|
||||
]
|
||||
}
|
@ -0,0 +1,216 @@
|
||||
{
|
||||
"name": "savedReport",
|
||||
"label": "Saved Report",
|
||||
"isHidden": false,
|
||||
"primaryKeyField": "id",
|
||||
"iconName": "article",
|
||||
"fields": {
|
||||
"queryFilterJson": {
|
||||
"name": "queryFilterJson",
|
||||
"label": "Query Filter",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"columnsJson": {
|
||||
"name": "columnsJson",
|
||||
"label": "Columns",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"inputFieldsJson": {
|
||||
"name": "inputFieldsJson",
|
||||
"label": "Input Fields",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"pivotTableJson": {
|
||||
"name": "pivotTableJson",
|
||||
"label": "Pivot Table",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"modifyDate": {
|
||||
"name": "modifyDate",
|
||||
"label": "Modify Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"label": {
|
||||
"name": "label",
|
||||
"label": "Report Name",
|
||||
"type": "STRING",
|
||||
"isRequired": true,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"label": "User Id",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"tableName": {
|
||||
"name": "tableName",
|
||||
"label": "Table",
|
||||
"type": "STRING",
|
||||
"isRequired": true,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"possibleValueSourceName": "tables",
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"createDate": {
|
||||
"name": "createDate",
|
||||
"label": "Create Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"name": "identity",
|
||||
"label": "Identity",
|
||||
"tier": "T1",
|
||||
"fieldNames": [
|
||||
"id",
|
||||
"label",
|
||||
"tableName"
|
||||
],
|
||||
"icon": {
|
||||
"name": "badge"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "filtersAndColumns",
|
||||
"label": "Filters and Columns",
|
||||
"tier": "T2",
|
||||
"widgetName": "reportSetupWidget",
|
||||
"icon": {
|
||||
"name": "table_chart"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "pivotTable",
|
||||
"label": "Pivot Table",
|
||||
"tier": "T2",
|
||||
"widgetName": "pivotTableSetupWidget",
|
||||
"icon": {
|
||||
"name": "pivot_table_chart"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"label": "Data",
|
||||
"tier": "T2",
|
||||
"fieldNames": [
|
||||
"queryFilterJson",
|
||||
"columnsJson",
|
||||
"pivotTableJson"
|
||||
],
|
||||
"icon": {
|
||||
"name": "text_snippet"
|
||||
},
|
||||
"isHidden": true
|
||||
},
|
||||
{
|
||||
"name": "hidden",
|
||||
"label": "Hidden",
|
||||
"tier": "T2",
|
||||
"fieldNames": [
|
||||
"inputFieldsJson",
|
||||
"userId"
|
||||
],
|
||||
"icon": {
|
||||
"name": "text_snippet"
|
||||
},
|
||||
"isHidden": true
|
||||
},
|
||||
{
|
||||
"name": "dates",
|
||||
"label": "Dates",
|
||||
"tier": "T3",
|
||||
"fieldNames": [
|
||||
"createDate",
|
||||
"modifyDate"
|
||||
],
|
||||
"icon": {
|
||||
"name": "calendar_month"
|
||||
},
|
||||
"isHidden": false
|
||||
}
|
||||
],
|
||||
"exposedJoins": [],
|
||||
"supplementalTableMetaData": {
|
||||
"materialDashboard": {
|
||||
"fieldRules": [
|
||||
{
|
||||
"trigger": "ON_CHANGE",
|
||||
"sourceField": "tableName",
|
||||
"action": "CLEAR_TARGET_FIELD",
|
||||
"targetField": "queryFilterJson"
|
||||
},
|
||||
{
|
||||
"trigger": "ON_CHANGE",
|
||||
"sourceField": "tableName",
|
||||
"action": "CLEAR_TARGET_FIELD",
|
||||
"targetField": "columnsJson"
|
||||
},
|
||||
{
|
||||
"trigger": "ON_CHANGE",
|
||||
"sourceField": "tableName",
|
||||
"action": "CLEAR_TARGET_FIELD",
|
||||
"targetField": "pivotTableJson"
|
||||
}
|
||||
],
|
||||
"type": "materialDashboard"
|
||||
}
|
||||
},
|
||||
"capabilities": [
|
||||
"TABLE_COUNT",
|
||||
"TABLE_GET",
|
||||
"TABLE_QUERY",
|
||||
"QUERY_STATS",
|
||||
"TABLE_UPDATE",
|
||||
"TABLE_INSERT",
|
||||
"TABLE_DELETE"
|
||||
],
|
||||
"readPermission": true,
|
||||
"insertPermission": true,
|
||||
"editPermission": true,
|
||||
"deletePermission": true,
|
||||
"usesVariants": false
|
||||
}
|
137
src/test/resources/fixtures/qqq/v1/metaData/table/script.json
Normal file
137
src/test/resources/fixtures/qqq/v1/metaData/table/script.json
Normal file
@ -0,0 +1,137 @@
|
||||
{
|
||||
"name": "script",
|
||||
"label": "Script",
|
||||
"isHidden": false,
|
||||
"primaryKeyField": "id",
|
||||
"iconName": "data_object",
|
||||
"fields": {
|
||||
"modifyDate": {
|
||||
"name": "modifyDate",
|
||||
"label": "Modify Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"label": "Name",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"currentScriptRevisionId": {
|
||||
"name": "currentScriptRevisionId",
|
||||
"label": "Current Script Revision",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"possibleValueSourceName": "scriptRevision",
|
||||
"displayFormat": "%s",
|
||||
"adornments": [
|
||||
{
|
||||
"type": "LINK",
|
||||
"values": {
|
||||
"toRecordFromTable": "scriptRevision"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"tableName": {
|
||||
"name": "tableName",
|
||||
"label": "Table Name",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"possibleValueSourceName": "tables",
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"createDate": {
|
||||
"name": "createDate",
|
||||
"label": "Create Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"scriptTypeId": {
|
||||
"name": "scriptTypeId",
|
||||
"label": "Script Type",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"possibleValueSourceName": "scriptType",
|
||||
"displayFormat": "%s",
|
||||
"adornments": [
|
||||
{
|
||||
"type": "LINK",
|
||||
"values": {
|
||||
"toRecordFromTable": "scriptType"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"name": "identity",
|
||||
"label": "Identity",
|
||||
"tier": "T1",
|
||||
"fieldNames": [
|
||||
"id",
|
||||
"name",
|
||||
"scriptTypeId",
|
||||
"tableName",
|
||||
"currentScriptRevisionId"
|
||||
],
|
||||
"icon": {
|
||||
"name": "badge"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "contents",
|
||||
"label": "Contents",
|
||||
"tier": "T2",
|
||||
"widgetName": "scriptViewer",
|
||||
"icon": {
|
||||
"name": "data_object"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "dates",
|
||||
"label": "Dates",
|
||||
"tier": "T3",
|
||||
"fieldNames": [
|
||||
"createDate",
|
||||
"modifyDate"
|
||||
],
|
||||
"icon": {
|
||||
"name": "calendar_month"
|
||||
},
|
||||
"isHidden": false
|
||||
}
|
||||
],
|
||||
"capabilities": [
|
||||
"TABLE_COUNT",
|
||||
"TABLE_GET",
|
||||
"TABLE_QUERY",
|
||||
"TABLE_INSERT",
|
||||
"TABLE_DELETE",
|
||||
"TABLE_UPDATE"
|
||||
],
|
||||
"readPermission": true,
|
||||
"insertPermission": true,
|
||||
"editPermission": true,
|
||||
"deletePermission": true
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
{
|
||||
"name": "scriptRevision",
|
||||
"label": "Script Revision",
|
||||
"isHidden": false,
|
||||
"primaryKeyField": "id",
|
||||
"iconName": "history_edu",
|
||||
"fields": {
|
||||
"scriptId": {
|
||||
"name": "scriptId",
|
||||
"label": "Script",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"possibleValueSourceName": "script",
|
||||
"displayFormat": "%s",
|
||||
"adornments": [
|
||||
{
|
||||
"type": "SIZE",
|
||||
"values": {
|
||||
"width": "large"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LINK",
|
||||
"values": {
|
||||
"toRecordFromTable": "script"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"apiName": {
|
||||
"name": "apiName",
|
||||
"label": "API Name",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"possibleValueSourceName": "apiName",
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"sequenceNo": {
|
||||
"name": "sequenceNo",
|
||||
"label": "Sequence No",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"apiVersion": {
|
||||
"name": "apiVersion",
|
||||
"label": "API Version",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"possibleValueSourceName": "apiVersion",
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"commitMessage": {
|
||||
"name": "commitMessage",
|
||||
"label": "Commit Message",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"modifyDate": {
|
||||
"name": "modifyDate",
|
||||
"label": "Modify Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"label": "Author",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"label": "Id",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
"createDate": {
|
||||
"name": "createDate",
|
||||
"label": "Create Date",
|
||||
"type": "DATE_TIME",
|
||||
"isRequired": false,
|
||||
"isEditable": false,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
},
|
||||
"sections": [
|
||||
{
|
||||
"name": "identity",
|
||||
"label": "Identity",
|
||||
"tier": "T1",
|
||||
"fieldNames": [
|
||||
"id",
|
||||
"scriptId",
|
||||
"sequenceNo"
|
||||
],
|
||||
"icon": {
|
||||
"name": "badge"
|
||||
},
|
||||
"isHidden": false
|
||||
},
|
||||
{
|
||||
"name": "dates",
|
||||
"label": "Dates",
|
||||
"tier": "T3",
|
||||
"fieldNames": [
|
||||
"createDate",
|
||||
"modifyDate"
|
||||
],
|
||||
"icon": {
|
||||
"name": "calendar_month"
|
||||
},
|
||||
"isHidden": false
|
||||
}
|
||||
],
|
||||
"exposedJoins": [],
|
||||
"capabilities": [
|
||||
"TABLE_COUNT",
|
||||
"TABLE_GET",
|
||||
"TABLE_QUERY",
|
||||
"TABLE_INSERT",
|
||||
"TABLE_UPDATE",
|
||||
"QUERY_STATS"
|
||||
],
|
||||
"readPermission": true,
|
||||
"insertPermission": true,
|
||||
"editPermission": true,
|
||||
"deletePermission": true,
|
||||
"usesVariants": false
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"records": []
|
||||
}
|
245
src/test/resources/fixtures/qqq/v1/table/audit/query.json
Normal file
245
src/test/resources/fixtures/qqq/v1/table/audit/query.json
Normal file
@ -0,0 +1,245 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 623577,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 278660,
|
||||
"auditDetail.auditId": 623577,
|
||||
"auditDetail.message": "Set First Name to John",
|
||||
"auditDetail.fieldName": "firstName",
|
||||
"auditDetail.newValue": "John"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "623577",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 623577,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 278661,
|
||||
"auditDetail.auditId": 623577,
|
||||
"auditDetail.message": "Removed Doe from Last Name",
|
||||
"auditDetail.fieldName": "lastName",
|
||||
"auditDetail.oldValue": "Doe"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "623577",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 623577,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 278662,
|
||||
"auditDetail.auditId": 623577,
|
||||
"auditDetail.message": "Set Client to ACME",
|
||||
"auditDetail.fieldName": "clientId",
|
||||
"auditDetail.oldValue": "BetaMax",
|
||||
"auditDetail.newValue": "ACME"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "623577",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Inserted",
|
||||
"timestamp": "2023-02-17T14:11:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 624804,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T14:13:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 278990,
|
||||
"auditDetail.auditId": 624804,
|
||||
"auditDetail.message": "Set SLA Expected Service Days to 2",
|
||||
"auditDetail.fieldName": "slaExpectedServiceDays",
|
||||
"auditDetail.newValue": "2"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "624804",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T14:13:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 624804,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T14:13:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 278991,
|
||||
"auditDetail.auditId": 624804,
|
||||
"auditDetail.message": "Set SLA Status to \"Pending\"",
|
||||
"auditDetail.fieldName": "slaStatusId",
|
||||
"auditDetail.newValue": "Pending"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "624804",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T14:13:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 624809,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Audit message here",
|
||||
"timestamp": "2023-02-17T14:13:16Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 279000,
|
||||
"auditDetail.auditId": 624809,
|
||||
"auditDetail.message": "This is a detail message"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "624809",
|
||||
"recordId": "1191682",
|
||||
"message": "Audit message here",
|
||||
"timestamp": "2023-02-17T14:13:16Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 737694,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T17:22:08Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 299222,
|
||||
"auditDetail.auditId": 737694,
|
||||
"auditDetail.message": "Set Estimated Delivery Date Time to 2023-02-18 07:00:00 PM EST",
|
||||
"auditDetail.fieldName": "estimatedDeliveryDateTime",
|
||||
"auditDetail.newValue": "2023-02-18 07:00:00 PM EST"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "737694",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T17:22:08Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 737694,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T17:22:08Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 299223,
|
||||
"auditDetail.auditId": 737694,
|
||||
"auditDetail.message": "Changed Parcel Tracking Status from \"Unknown\" to \"Pre Transit\"",
|
||||
"auditDetail.fieldName": "parcelTrackingStatusId",
|
||||
"auditDetail.oldValue": "Unknown",
|
||||
"auditDetail.newValue": "Pre Transit"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "737694",
|
||||
"recordId": "1191682",
|
||||
"message": "Record was Edited",
|
||||
"timestamp": "2023-02-17T17:22:08Z"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "audit",
|
||||
"recordLabel": "Parcel 1191682",
|
||||
"values": {
|
||||
"id": 737695,
|
||||
"auditTableId": 4,
|
||||
"auditUserId": 2,
|
||||
"recordId": 1191682,
|
||||
"message": "Updating Parcel based on updated tracking details",
|
||||
"timestamp": "2023-02-17T17:22:09Z",
|
||||
"clientId": 107,
|
||||
"auditDetail.id": 299224,
|
||||
"auditDetail.auditId": 737695,
|
||||
"auditDetail.message": "Set Parcel Tracking Status to Pre Transit based on most recent tracking update: Shipment information sent to FedEx"
|
||||
},
|
||||
"displayValues": {
|
||||
"auditTableId": "Parcel",
|
||||
"auditUserId": "QQQ User",
|
||||
"clientId": "ACME",
|
||||
"id": "737695",
|
||||
"recordId": "1191682",
|
||||
"message": "Updating Parcel based on updated tracking details",
|
||||
"timestamp": "2023-02-17T17:22:09Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
3
src/test/resources/fixtures/qqq/v1/table/city/count.json
Normal file
3
src/test/resources/fixtures/qqq/v1/table/city/count.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"count": 101406
|
||||
}
|
16
src/test/resources/fixtures/qqq/v1/table/person/1701.json
Normal file
16
src/test/resources/fixtures/qqq/v1/table/person/1701.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"tableName": "person",
|
||||
"recordLabel": "John Doe",
|
||||
"values": {
|
||||
"name": "John Doe",
|
||||
"id": 1710,
|
||||
"createDate": "2022-08-30T00:31:00Z",
|
||||
"modifyDate": "2022-08-30T00:31:00Z"
|
||||
},
|
||||
"displayValues": {
|
||||
"name": "John Doe",
|
||||
"id": 1710,
|
||||
"createDate": "2022-08-30T00:31:00Z",
|
||||
"modifyDate": "2022-08-30T00:31:00Z"
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"count": 101406
|
||||
}
|
276
src/test/resources/fixtures/qqq/v1/table/person/developer.json
Normal file
276
src/test/resources/fixtures/qqq/v1/table/person/developer.json
Normal file
@ -0,0 +1,276 @@
|
||||
{
|
||||
"record": {
|
||||
"tableName": "client",
|
||||
"recordLabel": "John Doe",
|
||||
"values": {
|
||||
"name": "John Doe",
|
||||
"id": 120,
|
||||
"deposcoOrderOptimizationCoolingScriptId": 2,
|
||||
"createDate": "2022-08-30T00:31:00Z",
|
||||
"modifyDate": "2023-02-19T01:28:30Z",
|
||||
"isFulfillmentCenter": false,
|
||||
"infoplusLobId": 18698,
|
||||
"deposcoBusinessUnitName": "TRIFECTA",
|
||||
"deposcoBusinessUnitId": 77,
|
||||
"optimizationConfigId": 1,
|
||||
"nfCode": "Client 224"
|
||||
},
|
||||
"displayValues": {
|
||||
"optimizationConfigId": "Client: 120",
|
||||
"name": "John Doe",
|
||||
"id": "120",
|
||||
"deposcoOrderOptimizationCoolingScriptId": "2",
|
||||
"createDate": "2022-08-30T00:31:00Z",
|
||||
"modifyDate": "2023-02-19T01:28:30Z",
|
||||
"isFulfillmentCenter": "No",
|
||||
"infoplusLobId": "18698",
|
||||
"deposcoBusinessUnitName": "TRIFECTA",
|
||||
"deposcoBusinessUnitId": "77",
|
||||
"nfCode": "Client 224"
|
||||
}
|
||||
},
|
||||
"associatedScripts": [
|
||||
{
|
||||
"testInputFields": [
|
||||
{
|
||||
"name": "selectedTimeInTransitDays",
|
||||
"label": "Selected Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "standardTimeInTransitDays",
|
||||
"label": "Standard Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"scriptType": {
|
||||
"tableName": "scriptType",
|
||||
"values": {
|
||||
"name": "Deposco Order Optimization Cooling",
|
||||
"id": 2,
|
||||
"createDate": "2022-10-31T19:06:50Z",
|
||||
"modifyDate": "2022-10-31T19:06:50Z"
|
||||
}
|
||||
},
|
||||
"scriptRevisions": [
|
||||
{
|
||||
"tableName": "scriptRevision",
|
||||
"values": {
|
||||
"id": 1,
|
||||
"contents": "1;",
|
||||
"createDate": "2023-02-19T01:28:30Z",
|
||||
"modifyDate": "2023-02-19T01:28:30Z",
|
||||
"scriptId": 2,
|
||||
"sequenceNo": 1,
|
||||
"commitMessage": "Initial version",
|
||||
"author": "Darin Kelkhoff"
|
||||
}
|
||||
}
|
||||
],
|
||||
"testOutputFields": [
|
||||
{
|
||||
"name": "sku",
|
||||
"label": "Sku",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "quantityPerCarton",
|
||||
"label": "Quantity Per Carton",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "useClientProvidedCoolingSolution",
|
||||
"label": "Use Client Provided Cooling Solution",
|
||||
"type": "BOOLEAN",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "reason",
|
||||
"label": "Reason",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"script": {
|
||||
"tableName": "script",
|
||||
"values": {
|
||||
"name": "John Doe - Deposco Order Optimization Cooling",
|
||||
"id": 2,
|
||||
"scriptTypeId": 2,
|
||||
"createDate": "2023-02-19T01:28:30Z",
|
||||
"modifyDate": "2023-02-19T01:28:30Z",
|
||||
"currentScriptRevisionId": 1
|
||||
}
|
||||
},
|
||||
"associatedScript": {
|
||||
"fieldName": "deposcoOrderOptimizationCoolingScriptId",
|
||||
"scriptTypeId": 2,
|
||||
"scriptTester": {
|
||||
"name": "com.coldtrack.live.processes.deposco.RunDeposcoOrderOptimizationCoolingScript",
|
||||
"codeType": "JAVA",
|
||||
"codeUsage": "SCRIPT_TESTER"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"testInputFields": [
|
||||
{
|
||||
"name": "selectedTimeInTransitDays",
|
||||
"label": "Selected Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "standardTimeInTransitDays",
|
||||
"label": "Standard Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "runtimeWeekday",
|
||||
"label": "Runtime Weekday",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"scriptType": {
|
||||
"tableName": "scriptType",
|
||||
"values": {
|
||||
"name": "Deposco Order Optimization Batch Name",
|
||||
"id": 1,
|
||||
"createDate": "2022-10-31T19:06:50Z",
|
||||
"modifyDate": "2022-10-31T19:06:50Z"
|
||||
}
|
||||
},
|
||||
"testOutputFields": [
|
||||
{
|
||||
"name": "batchName",
|
||||
"label": "Batch Name",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "reason",
|
||||
"label": "Reason",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"associatedScript": {
|
||||
"fieldName": "deposcoOrderOptimizationBatchNameScriptId",
|
||||
"scriptTypeId": 1,
|
||||
"scriptTester": {
|
||||
"name": "com.coldtrack.live.processes.deposco.RunDeposcoOrderOptimizationBatchNameScript",
|
||||
"codeType": "JAVA",
|
||||
"codeUsage": "SCRIPT_TESTER"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"testInputFields": [
|
||||
{
|
||||
"name": "selectedTimeInTransitDays",
|
||||
"label": "Selected Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "standardTimeInTransitDays",
|
||||
"label": "Standard Time In Transit Days",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "runtimeWeekday",
|
||||
"label": "Runtime Weekday",
|
||||
"type": "INTEGER",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"scriptType": {
|
||||
"tableName": "scriptType",
|
||||
"values": {
|
||||
"name": "Deposco Order Optimization Batch Name",
|
||||
"id": 1,
|
||||
"createDate": "2022-10-31T19:06:50Z",
|
||||
"modifyDate": "2022-10-31T19:06:50Z"
|
||||
}
|
||||
},
|
||||
"testOutputFields": [
|
||||
{
|
||||
"name": "batchName",
|
||||
"label": "Batch Name",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
},
|
||||
{
|
||||
"name": "reason",
|
||||
"label": "Reason",
|
||||
"type": "STRING",
|
||||
"isRequired": false,
|
||||
"isEditable": true,
|
||||
"isHeavy": false,
|
||||
"displayFormat": "%s"
|
||||
}
|
||||
],
|
||||
"associatedScript": {
|
||||
"fieldName": "deposcoOrderOptimizationCartonizationScriptId",
|
||||
"scriptTypeId": 1,
|
||||
"scriptTester": {
|
||||
"name": "com.coldtrack.live.processes.deposco.RunDeposcoOrderOptimizationBatchNameScript",
|
||||
"codeType": "JAVA",
|
||||
"codeUsage": "SCRIPT_TESTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
64
src/test/resources/fixtures/qqq/v1/table/person/index.json
Normal file
64
src/test/resources/fixtures/qqq/v1/table/person/index.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"tableName": "person",
|
||||
"values": {
|
||||
"id": 1,
|
||||
"createDate": "2022-07-23T00:17:00",
|
||||
"modifyDate": "2022-07-22T19:17:06",
|
||||
"firstName": "Jonny",
|
||||
"lastName": "Doe",
|
||||
"birthDate": "1980-05-31",
|
||||
"email": "jdoe@kingsrook.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "person",
|
||||
"values": {
|
||||
"id": 2,
|
||||
"createDate": "2022-07-23T00:17:00",
|
||||
"modifyDate": "2022-07-23T00:17:00",
|
||||
"firstName": "James",
|
||||
"lastName": "Maes",
|
||||
"birthDate": "1980-05-15",
|
||||
"email": "jmaes@mmltholdings.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "person",
|
||||
"values": {
|
||||
"id": 3,
|
||||
"createDate": "2022-07-23T00:17:00",
|
||||
"modifyDate": "2022-07-23T00:17:00",
|
||||
"firstName": "Tim",
|
||||
"lastName": "Chamberlain",
|
||||
"birthDate": "1976-05-28",
|
||||
"email": "tchamberlain@mmltholdings.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "person",
|
||||
"values": {
|
||||
"id": 4,
|
||||
"createDate": "2022-07-23T00:17:00",
|
||||
"modifyDate": "2022-07-23T00:17:00",
|
||||
"firstName": "Tyler",
|
||||
"lastName": "Samples",
|
||||
"birthDate": "1986-05-28",
|
||||
"email": "tsamples@mmltholdings.com"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "person",
|
||||
"values": {
|
||||
"id": 5,
|
||||
"createDate": "2022-07-23T00:17:00",
|
||||
"modifyDate": "2022-07-23T00:17:00",
|
||||
"firstName": "Garret",
|
||||
"lastName": "Richardson",
|
||||
"birthDate": "1981-01-01",
|
||||
"email": "grichardson@mmltholdings.com"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"label": "St. Louis"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"label": "Chesterfield"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
"options": [
|
||||
{
|
||||
"id": 1,
|
||||
"label": "St. Louis"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1 @@
|
||||
[]
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"options": [
|
||||
{
|
||||
"id": "person",
|
||||
"label": "Person"
|
||||
},
|
||||
{
|
||||
"id": "city",
|
||||
"label": "City"
|
||||
},
|
||||
{
|
||||
"id": "savedReport",
|
||||
"label": "Saved Report"
|
||||
}
|
||||
]
|
||||
}
|
22
src/test/resources/fixtures/qqq/v1/table/script/1.json
Normal file
22
src/test/resources/fixtures/qqq/v1/table/script/1.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"tableName": "script",
|
||||
"recordLabel": "Hello, Script",
|
||||
"values": {
|
||||
"name": "Hello, Script",
|
||||
"id": 1,
|
||||
"currentScriptRevisionId": 100,
|
||||
"tableName": "client",
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z",
|
||||
"scriptTypeId": 1
|
||||
},
|
||||
"displayValues": {
|
||||
"tableName": "Client",
|
||||
"scriptTypeId": "Record Script",
|
||||
"name": "Hello, Script",
|
||||
"currentScriptRevisionId": 100,
|
||||
"id": "1",
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z"
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"records": []
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
{
|
||||
"tableName": "scriptRevision",
|
||||
"recordLabel": "Hello, Script Revision",
|
||||
"values": {
|
||||
"id": "100",
|
||||
"name": "Hello, Script Revision",
|
||||
"sequenceNo": "22",
|
||||
"commitMessage": "Initial checkin",
|
||||
"author": "Jon Programmer",
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z"
|
||||
},
|
||||
"displayValues": {
|
||||
"id": "1",
|
||||
"name": "Hello, Script Revision",
|
||||
"scriptId": "1",
|
||||
"sequenceNo": "22",
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z"
|
||||
},
|
||||
"associatedRecords": {
|
||||
"files": [
|
||||
{
|
||||
"tableName": "scriptRevisionFile",
|
||||
"values": {
|
||||
"id": 101,
|
||||
"fileName": "Script.js",
|
||||
"contents": "var hello;",
|
||||
"scriptRevisionId": 100,
|
||||
"createDate": "2023-06-23T21:59:57Z",
|
||||
"modifyDate": "2023-06-23T21:59:57Z"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"tableName": "scriptRevision",
|
||||
"values": {
|
||||
"contents": "var hello;",
|
||||
"id": 100,
|
||||
"sequenceNo": 2,
|
||||
"commitMessage": "2nd commit",
|
||||
"author": "Jon Programmer",
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z"
|
||||
},
|
||||
"displayValues": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"tableName": "scriptRevision",
|
||||
"values": {
|
||||
"contents": "var goodBye;",
|
||||
"id": 99,
|
||||
"sequenceNo": 1,
|
||||
"commitMessage": "Initial checkin",
|
||||
"author": "Jane Programmer",
|
||||
"createDate": "2023-02-17T00:47:51Z",
|
||||
"modifyDate": "2023-02-17T00:47:51Z"
|
||||
},
|
||||
"displayValues": {
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
13
src/test/resources/fixtures/qqq/v1/table/scriptType/1.json
Normal file
13
src/test/resources/fixtures/qqq/v1/table/scriptType/1.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"tableName": "scriptType",
|
||||
"recordLabel": "Record Script",
|
||||
"values": {
|
||||
"name": "Record Script",
|
||||
"id": 1,
|
||||
"createDate": "2023-02-18T00:47:51Z",
|
||||
"modifyDate": "2023-02-18T00:47:51Z",
|
||||
"fileMode": 1
|
||||
},
|
||||
"displayValues": {
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user