QQQ-41: added app sections, new widgets, ability to have no tables under app, etc.

This commit is contained in:
Tim Chamberlain
2022-09-16 10:19:06 -05:00
parent 01afdaacfd
commit 8891b3e7ea
19 changed files with 1923 additions and 70 deletions

View File

@ -40,4 +40,14 @@ public abstract class AbstractWidgetRenderer
*******************************************************************************/
public abstract Object render(QInstance qInstance, QSession session, QWidgetMetaDataInterface qWidgetMetaData) throws QException;
/*******************************************************************************
**
*******************************************************************************/
public String getWidgetName()
{
return this.getClass().getSimpleName();
}
}

View File

@ -38,6 +38,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
@ -89,9 +90,12 @@ public class MetaDataAction
apps.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
for(QAppChildMetaData child : entry.getValue().getChildren())
if(CollectionUtils.nullSafeHasContents(entry.getValue().getChildren()))
{
apps.get(entry.getKey()).addChild(new AppTreeNode(child));
for(QAppChildMetaData child : entry.getValue().getChildren())
{
apps.get(entry.getKey()).addChild(new AppTreeNode(child));
}
}
}
metaDataOutput.setApps(apps);

View File

@ -36,7 +36,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
@ -211,6 +213,11 @@ public class QInstanceEnricher
{
app.setLabel(nameToLabel(app.getName()));
}
if(CollectionUtils.nullSafeIsEmpty(app.getSections()))
{
generateAppSections(app);
}
}
@ -511,6 +518,54 @@ public class QInstanceEnricher
/*******************************************************************************
** If a app didn't have any sections, generate "sensible defaults"
*******************************************************************************/
private void generateAppSections(QAppMetaData app)
{
if(CollectionUtils.nullSafeIsEmpty(app.getChildren()))
{
/////////////////////////////////////////////////////////////////////////////////////////////////
// assume this app is valid if it has no children, but surely it doesn't need any sections then. //
/////////////////////////////////////////////////////////////////////////////////////////////////
return;
}
//////////////////////////////////////////////////////////////////////////////
// create an identity section for the id and any fields in the record label //
//////////////////////////////////////////////////////////////////////////////
QAppSection defaultSection = new QAppSection(app.getName(), app.getLabel(), new QIcon("badge"), new ArrayList<>(), new ArrayList<>());
boolean foundNonAppChild = false;
if(CollectionUtils.nullSafeHasContents(app.getChildren()))
{
for(QAppChildMetaData child : app.getChildren())
{
////////////////////////////////////////////////////////////////////////////////
// only tables and processes are allowed to be in sections at this time, apps //
// might be children but not in sections so keep track if we find any non-app //
////////////////////////////////////////////////////////////////////////////////
if(child.getClass().equals(QTableMetaData.class))
{
defaultSection.getTables().add(child.getName());
foundNonAppChild = true;
}
else if(child.getClass().equals(QProcessMetaData.class))
{
defaultSection.getProcesses().add(child.getName());
foundNonAppChild = true;
}
}
}
if(foundNonAppChild)
{
app.addSection(defaultSection);
}
}
/*******************************************************************************
** If a table didn't have any sections, generate "sensible defaults"
*******************************************************************************/

View File

@ -40,6 +40,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
@ -213,7 +214,7 @@ public class QInstanceValidator
{
for(QFieldSection section : table.getSections())
{
validateSection(table, section, fieldNamesInSections);
validateFieldSection(table, section, fieldNamesInSections);
if(section.getTier().equals(Tier.T1))
{
assertCondition(tier1Section == null, "Table " + tableName + " has more than 1 section listed as Tier 1");
@ -479,7 +480,7 @@ public class QInstanceValidator
/*******************************************************************************
**
*******************************************************************************/
private void validateSection(QTableMetaData table, QFieldSection section, Set<String> fieldNamesInSections)
private void validateFieldSection(QTableMetaData table, QFieldSection section, Set<String> fieldNamesInSections)
{
assertCondition(StringUtils.hasContent(section.getName()), "Missing a name for field section in table " + table.getName() + ".");
assertCondition(StringUtils.hasContent(section.getLabel()), "Missing a label for field section in table " + table.getLabel() + ".");
@ -500,6 +501,42 @@ public class QInstanceValidator
/*******************************************************************************
**
*******************************************************************************/
private void validateAppSection(QAppMetaData app, QAppSection section, Set<String> childNamesInSections)
{
assertCondition(StringUtils.hasContent(section.getName()), "Missing a name for a section in app " + app.getName() + ".");
assertCondition(StringUtils.hasContent(section.getLabel()), "Missing a label for a section in app " + app.getLabel() + ".");
boolean hasTables = CollectionUtils.nullSafeHasContents(section.getTables());
boolean hasProcesses = CollectionUtils.nullSafeHasContents(section.getProcesses());
if(assertCondition(hasTables || hasProcesses, "App " + app.getName() + " section " + section.getName() + " does not have any children."))
{
if(hasTables)
{
for(String tableName : section.getTables())
{
assertCondition(app.getChildren().stream().anyMatch(c -> c.getName().equals(tableName)), "App " + app.getName() + " section " + section.getName() + " specifies table " + tableName + ", which is not a child of this app.");
assertCondition(!childNamesInSections.contains(tableName), "App " + app.getName() + " has table " + tableName + " listed more than once in its sections.");
childNamesInSections.add(tableName);
}
}
if(hasProcesses)
{
for(String processName : section.getProcesses())
{
assertCondition(app.getChildren().stream().anyMatch(c -> c.getName().equals(processName)), "App " + app.getName() + " section " + section.getName() + " specifies process " + processName + ", which is not a child of this app.");
assertCondition(!childNamesInSections.contains(processName), "App " + app.getName() + " has process " + processName + " listed more than once in its sections.");
childNamesInSections.add(processName);
}
}
}
}
/*******************************************************************************
**
*******************************************************************************/
@ -565,6 +602,29 @@ public class QInstanceValidator
childNames.add(child.getName());
}
}
//////////////////////////////////////////
// validate field sections in the table //
//////////////////////////////////////////
Set<String> childNamesInSections = new HashSet<>();
if(app.getSections() != null)
{
for(QAppSection section : app.getSections())
{
validateAppSection(app, section, childNamesInSections);
}
}
if(CollectionUtils.nullSafeHasContents(app.getChildren()))
{
for(QAppChildMetaData child : app.getChildren())
{
if(!child.getClass().equals(QAppMetaData.class))
{
assertCondition(childNamesInSections.contains(child.getName()), "App " + appName + " child " + child.getName() + " is not listed in any app sections.");
}
}
}
});
}
}

View File

@ -29,32 +29,34 @@ import java.util.List;
** Model containing datastructure expected by frontend bar chart widget
**
*******************************************************************************/
public class BarChart implements QWidget
public class ChartData implements QWidget
{
/*
type: "barChart",
title: "Parcel Invoice Lines per Month",
barChartData: {
labels: ["Feb 22", "Mar 22", "Apr 22", "May 22", "Jun 22", "Jul 22", "Aug 22"],
datasets: {label: "Parcel Invoice Lines", data: [50000, 22000, 11111, 22333, 40404, 9876, 2355]},
},
interface BarChartData{
labels: string[];
datasets: {
label: string;
data: number[];
}[];
}
*/
private String title;
private Data barChartData;
private String description;
private Data chartData;
/*******************************************************************************
**
*******************************************************************************/
public BarChart(String title, String seriesLabel, List<String> labels, List<Number> data)
public ChartData(String title, String description, String seriesLabel, List<String> labels, List<Number> data)
{
setTitle(title);
setBarChartData(new BarChart.Data()
setDescription(description);
setChartData(new ChartData.Data()
.withLabels(labels)
.withDatasets(new BarChart.Data.DataSet()
.withDatasets(new ChartData.Data.Dataset()
.withLabel(seriesLabel)
.withData(data)));
}
@ -98,7 +100,7 @@ public class BarChart implements QWidget
** Fluent setter for title
**
*******************************************************************************/
public BarChart withTitle(String title)
public ChartData withTitle(String title)
{
this.title = title;
return (this);
@ -107,34 +109,68 @@ public class BarChart implements QWidget
/*******************************************************************************
** Getter for barChartData
** Getter for description
**
*******************************************************************************/
public Data getBarChartData()
public String getDescription()
{
return barChartData;
return description;
}
/*******************************************************************************
** Setter for barChartData
** Setter for description
**
*******************************************************************************/
public void setBarChartData(Data barChartData)
public void setDescription(String description)
{
this.barChartData = barChartData;
this.description = description;
}
/*******************************************************************************
** Fluent setter for barChartData
** Fluent setter for description
**
*******************************************************************************/
public BarChart withBarChartData(Data barChartData)
public ChartData withDescription(String description)
{
this.barChartData = barChartData;
this.description = description;
return (this);
}
/*******************************************************************************
** Getter for chartData
**
*******************************************************************************/
public Data getChartData()
{
return chartData;
}
/*******************************************************************************
** Setter for chartData
**
*******************************************************************************/
public void setChartData(Data chartData)
{
this.chartData = chartData;
}
/*******************************************************************************
** Fluent setter for chartData
**
*******************************************************************************/
public ChartData withChartData(Data chartData)
{
this.chartData = chartData;
return (this);
}
@ -146,7 +182,7 @@ public class BarChart implements QWidget
public static class Data
{
private List<String> labels;
private DataSet datasets;
private Dataset dataset;
@ -188,9 +224,9 @@ public class BarChart implements QWidget
** Getter for datasets
**
*******************************************************************************/
public DataSet getDatasets()
public Dataset getDataset()
{
return datasets;
return dataset;
}
@ -199,9 +235,9 @@ public class BarChart implements QWidget
** Setter for datasets
**
*******************************************************************************/
public void setDatasets(DataSet datasets)
public void setDataset(Dataset dataset)
{
this.datasets = datasets;
this.dataset = dataset;
}
@ -210,9 +246,9 @@ public class BarChart implements QWidget
** Fluent setter for datasets
**
*******************************************************************************/
public Data withDatasets(DataSet datasets)
public Data withDatasets(Dataset datasets)
{
this.datasets = datasets;
this.dataset = datasets;
return (this);
}
@ -221,9 +257,9 @@ public class BarChart implements QWidget
/*******************************************************************************
**
*******************************************************************************/
public static class DataSet
public static class Dataset
{
private String label;
private String label;
private List<Number> data;
@ -254,7 +290,7 @@ public class BarChart implements QWidget
** Fluent setter for label
**
*******************************************************************************/
public DataSet withLabel(String label)
public Dataset withLabel(String label)
{
this.label = label;
return (this);
@ -288,7 +324,7 @@ public class BarChart implements QWidget
** Fluent setter for data
**
*******************************************************************************/
public DataSet withData(List<Number> data)
public Dataset withData(List<Number> data)
{
this.data = data;
return (this);

View File

@ -0,0 +1,344 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
import java.util.List;
/*******************************************************************************
** Model containing datastructure expected by frontend bar chart widget
**
*******************************************************************************/
public class LineChartData implements QWidget
{
/*
export interface DefaultLineChartData
{
labels: string[]; // monday, tues...
lineLabels: string[]; // axle, cdl...
datasets: {
label: string; // axle, cdl...
color?: "primary" | "secondary" | "info" | "success" | "warning" | "error" | "light" | "dark";
data: number[];
}[];
};
*/
private String title;
private Data chartData;
/*******************************************************************************
**
*******************************************************************************/
public LineChartData()
{
}
/*******************************************************************************
**
*******************************************************************************/
public LineChartData(String title, List<String> labels, List<String> lineLabels, List<Data.Dataset> datasets)
{
setTitle(title);
setChartData(new LineChartData.Data()
.withLabels(labels)
.withLineLabels(lineLabels)
.withDatasets(datasets));
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return "lineChart";
}
/*******************************************************************************
** Getter for title
**
*******************************************************************************/
public String getTitle()
{
return title;
}
/*******************************************************************************
** Setter for title
**
*******************************************************************************/
public void setTitle(String title)
{
this.title = title;
}
/*******************************************************************************
** Fluent setter for title
**
*******************************************************************************/
public LineChartData withTitle(String title)
{
this.title = title;
return (this);
}
/*******************************************************************************
** Getter for chartData
**
*******************************************************************************/
public Data getChartData()
{
return chartData;
}
/*******************************************************************************
** Setter for chartData
**
*******************************************************************************/
public void setChartData(Data chartData)
{
this.chartData = chartData;
}
/*******************************************************************************
** Fluent setter for chartData
**
*******************************************************************************/
public LineChartData withChartData(Data chartData)
{
this.chartData = chartData;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public static class Data
{
private List<String> labels;
private List<String> lineLabels;
private List<Dataset> datasets;
/*******************************************************************************
** Getter for labels
**
*******************************************************************************/
public List<String> getLabels()
{
return labels;
}
/*******************************************************************************
** Setter for labels
**
*******************************************************************************/
public void setLabels(List<String> labels)
{
this.labels = labels;
}
/*******************************************************************************
** Fluent setter for labels
**
*******************************************************************************/
public Data withLabels(List<String> labels)
{
this.labels = labels;
return (this);
}
/*******************************************************************************
** Getter for lineLabels
**
*******************************************************************************/
public List<String> getLineLabels()
{
return lineLabels;
}
/*******************************************************************************
** Setter for lineLabels
**
*******************************************************************************/
public void setLineLabels(List<String> lineLabels)
{
this.lineLabels = lineLabels;
}
/*******************************************************************************
** Fluent setter for lineLabels
**
*******************************************************************************/
public Data withLineLabels(List<String> lineLabels)
{
this.lineLabels = lineLabels;
return (this);
}
/*******************************************************************************
** Getter for datasets
**
*******************************************************************************/
public List<Dataset> getDatasets()
{
return datasets;
}
/*******************************************************************************
** Setter for datasets
**
*******************************************************************************/
public void setDatasets(List<Dataset> datasets)
{
this.datasets = datasets;
}
/*******************************************************************************
** Fluent setter for datasets
**
*******************************************************************************/
public Data withDatasets(List<Dataset> datasets)
{
this.datasets = datasets;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public static class Dataset
{
private String label;
private List<Number> data;
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Fluent setter for label
**
*******************************************************************************/
public Dataset withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for data
**
*******************************************************************************/
public List<Number> getData()
{
return data;
}
/*******************************************************************************
** Setter for data
**
*******************************************************************************/
public void setData(List<Number> data)
{
this.data = data;
}
/*******************************************************************************
** Fluent setter for data
**
*******************************************************************************/
public Dataset withData(List<Number> data)
{
this.data = data;
return (this);
}
}
}
}

View File

@ -0,0 +1,233 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
/*******************************************************************************
** Model containing datastructure expected by frontend location widget
**
*******************************************************************************/
public class LocationData implements QWidget
{
private String imageUrl;
private String title;
private String description;
private String footerText;
private String location;
/*******************************************************************************
**
*******************************************************************************/
public LocationData(String imageUrl, String title, String description, String location, String footerText)
{
this.imageUrl = imageUrl;
this.title = title;
this.description = description;
this.location = location;
this.footerText = footerText;
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return "location";
}
/*******************************************************************************
** Getter for imageUrl
**
*******************************************************************************/
public String getImageUrl()
{
return imageUrl;
}
/*******************************************************************************
** Setter for imageUrl
**
*******************************************************************************/
public void setImageUrl(String imageUrl)
{
this.imageUrl = imageUrl;
}
/*******************************************************************************
** Fluent setter for imageUrl
**
*******************************************************************************/
public LocationData withImageUrl(String imageUrl)
{
this.imageUrl = imageUrl;
return (this);
}
/*******************************************************************************
** Getter for description
**
*******************************************************************************/
public String getDescription()
{
return description;
}
/*******************************************************************************
** Setter for description
**
*******************************************************************************/
public void setDescription(String description)
{
this.description = description;
}
/*******************************************************************************
** Fluent setter for description
**
*******************************************************************************/
public LocationData withDescription(String description)
{
this.description = description;
return (this);
}
/*******************************************************************************
** Getter for footerText
**
*******************************************************************************/
public String getFooterText()
{
return footerText;
}
/*******************************************************************************
** Setter for footerText
**
*******************************************************************************/
public void setFooterText(String footerText)
{
this.footerText = footerText;
}
/*******************************************************************************
** Fluent setter for footerText
**
*******************************************************************************/
public LocationData withFooterText(String footerText)
{
this.footerText = footerText;
return (this);
}
/*******************************************************************************
** Getter for title
**
*******************************************************************************/
public String getTitle()
{
return title;
}
/*******************************************************************************
** Setter for title
**
*******************************************************************************/
public void setTitle(String title)
{
this.title = title;
}
/*******************************************************************************
** Fluent setter for title
**
*******************************************************************************/
public LocationData withTitle(String title)
{
this.title = title;
return (this);
}
/*******************************************************************************
** Getter for location
**
*******************************************************************************/
public String getLocation()
{
return location;
}
/*******************************************************************************
** Setter for location
**
*******************************************************************************/
public void setLocation(String location)
{
this.location = location;
}
/*******************************************************************************
** Fluent setter for location
**
*******************************************************************************/
public LocationData withLocation(String location)
{
this.location = location;
return (this);
}
}

View File

@ -0,0 +1,206 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
/*******************************************************************************
** Model containing datastructure expected by frontend bar chart widget
**
*******************************************************************************/
public class StatisticsData implements QWidget
{
/*
interface BarChartData{
labels: string[];
datasets: {
label: string;
data: number[];
}[];
}
*/
private String title;
private int count;
private Number percentageAmount;
private String percentageLabel;
/*******************************************************************************
**
*******************************************************************************/
public StatisticsData(String title, int count, Number percentageAmount, String percentageLabel)
{
this.title = title;
this.count = count;
this.percentageLabel = percentageLabel;
this.percentageAmount = percentageAmount;
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return "statistics";
}
/*******************************************************************************
** Getter for title
**
*******************************************************************************/
public String getTitle()
{
return title;
}
/*******************************************************************************
** Setter for title
**
*******************************************************************************/
public void setTitle(String title)
{
this.title = title;
}
/*******************************************************************************
** Fluent setter for title
**
*******************************************************************************/
public StatisticsData withTitle(String title)
{
this.title = title;
return (this);
}
/*******************************************************************************
** Getter for count
**
*******************************************************************************/
public int getCount()
{
return count;
}
/*******************************************************************************
** Setter for count
**
*******************************************************************************/
public void setCount(int count)
{
this.count = count;
}
/*******************************************************************************
** Fluent setter for count
**
*******************************************************************************/
public StatisticsData withCount(int count)
{
this.count = count;
return (this);
}
/*******************************************************************************
** Getter for percentageAmount
**
*******************************************************************************/
public Number getPercentageAmount()
{
return percentageAmount;
}
/*******************************************************************************
** Setter for percentageAmount
**
*******************************************************************************/
public void setPercentageAmount(Number percentageAmount)
{
this.percentageAmount = percentageAmount;
}
/*******************************************************************************
** Fluent setter for percentageAmount
**
*******************************************************************************/
public StatisticsData withPercentageAmount(Number percentageAmount)
{
this.percentageAmount = percentageAmount;
return (this);
}
/*******************************************************************************
** Getter for percentageLabel
**
*******************************************************************************/
public String getPercentageLabel()
{
return percentageLabel;
}
/*******************************************************************************
** Setter for percentageLabel
**
*******************************************************************************/
public void setPercentageLabel(String percentageLabel)
{
this.percentageLabel = percentageLabel;
}
/*******************************************************************************
** Fluent setter for percentageLabel
**
*******************************************************************************/
public StatisticsData withPercentageLabel(String percentageLabel)
{
this.percentageLabel = percentageLabel;
return (this);
}
}

View File

@ -0,0 +1,417 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.dashboard.widgets;
import java.util.List;
import java.util.Map;
/*******************************************************************************
** Model containing datastructure expected by frontend bar chart widget
**
*******************************************************************************/
public class TableData implements QWidget
{
/*
const carrierSpendData = {
columns: [
{Header: "carrier", accessor: "product", width: "55%"},
{Header: "total YTD", accessor: "value"},
{Header: "monthly average", accessor: "adsSpent", align: "center"},
{Header: "service failures", accessor: "refunds", align: "center"},
],
rows: [
{
product: <ProductCell image={axlehire} name="AxleHire" orders="921" />,
value: <DefaultCell>$140,925</DefaultCell>,
adsSpent: <DefaultCell>$24,531</DefaultCell>,
refunds: <RefundsCell value={121} icon={{color: "success", name: "keyboard_arrow_up"}} />,
},
]
}
*/
private String title;
private List<Column> columns;
private List<Map<String, Object>> rows;
private List<String> dropdownOptions;
/*******************************************************************************
**
*******************************************************************************/
public TableData(String title, List<Column> columns, List<Map<String, Object>> rows, List<String> dropdownOptions)
{
setTitle(title);
setColumns(columns);
setRows(rows);
setDropdownOptions(dropdownOptions);
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return "table";
}
/*******************************************************************************
** Getter for title
**
*******************************************************************************/
public String getTitle()
{
return title;
}
/*******************************************************************************
** Setter for title
**
*******************************************************************************/
public void setTitle(String title)
{
this.title = title;
}
/*******************************************************************************
** Fluent setter for title
**
*******************************************************************************/
public TableData withTitle(String title)
{
this.title = title;
return (this);
}
/*******************************************************************************
** Getter for columns
**
*******************************************************************************/
public List<Column> getColumns()
{
return columns;
}
/*******************************************************************************
** Setter for columns
**
*******************************************************************************/
public void setColumns(List<Column> columns)
{
this.columns = columns;
}
/*******************************************************************************
** Fluent setter for columns
**
*******************************************************************************/
public TableData withColumns(List<Column> columns)
{
this.columns = columns;
return (this);
}
/*******************************************************************************
** Getter for rows
**
*******************************************************************************/
public List<Map<String, Object>> getRows()
{
return rows;
}
/*******************************************************************************
** Setter for rows
**
*******************************************************************************/
public void setRows(List<Map<String, Object>> rows)
{
this.rows = rows;
}
/*******************************************************************************
** Fluent setter for rows
**
*******************************************************************************/
public TableData withRows(List<Map<String, Object>> rows)
{
this.rows = rows;
return (this);
}
/*******************************************************************************
** Getter for dropdownOptions
**
*******************************************************************************/
public List<String> getDropdownOptions()
{
return dropdownOptions;
}
/*******************************************************************************
** Setter for dropdownOptions
**
*******************************************************************************/
public void setDropdownOptions(List<String> dropdownOptions)
{
this.dropdownOptions = dropdownOptions;
}
/*******************************************************************************
** Fluent setter for dropdownOptions
**
*******************************************************************************/
public TableData withDropdownOptions(List<String> dropdownOptions)
{
this.dropdownOptions = dropdownOptions;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public static class Column
{
private String type;
private String header;
private String accessor;
private String width;
private String align;
/*******************************************************************************
**
*******************************************************************************/
public Column(String type, String header, String accessor, String width, String align)
{
this.type = type;
this.header = header;
this.accessor = accessor;
this.width = width;
this.align = align;
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return type;
}
/*******************************************************************************
** Setter for type
**
*******************************************************************************/
public void setType(String type)
{
this.type = type;
}
/*******************************************************************************
** Getter for header
**
*******************************************************************************/
public String getHeader()
{
return header;
}
/*******************************************************************************
** Setter for header
**
*******************************************************************************/
public void setHeader(String header)
{
this.header = header;
}
/*******************************************************************************
** Getter for accessor
**
*******************************************************************************/
public String getAccessor()
{
return accessor;
}
/*******************************************************************************
** Setter for accessor
**
*******************************************************************************/
public void setAccessor(String accessor)
{
this.accessor = accessor;
}
/*******************************************************************************
** Getter for width
**
*******************************************************************************/
public String getWidth()
{
return width;
}
/*******************************************************************************
** Setter for width
**
*******************************************************************************/
public void setWidth(String width)
{
this.width = width;
}
/*******************************************************************************
** Getter for align
**
*******************************************************************************/
public String getAlign()
{
return align;
}
/*******************************************************************************
** Setter for align
**
*******************************************************************************/
public void setAlign(String align)
{
this.align = align;
}
/*******************************************************************************
** fluent setter for header
**
*******************************************************************************/
public Column withHeader(String header)
{
this.header = header;
return this;
}
/*******************************************************************************
** fluent setter for accessor
**
*******************************************************************************/
public Column withAccessor(String accessor)
{
this.accessor = accessor;
return this;
}
/*******************************************************************************
** fluent setter for width
**
*******************************************************************************/
public Column withWidth(String width)
{
this.width = width;
return this;
}
/*******************************************************************************
** fluent setter for align
**
*******************************************************************************/
public Column withAlign(String align)
{
this.align = align;
return this;
}
/*******************************************************************************
** fluent setter for type
**
*******************************************************************************/
public Column withType(String type)
{
this.type = type;
return this;
}
}
}

View File

@ -23,10 +23,13 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
@ -39,12 +42,14 @@ public class QFrontendAppMetaData
{
private String name;
private String label;
private List<AppTreeNode> children = new ArrayList<>();
private List<String> widgets = new ArrayList<>();
private String iconName;
private List<String> widgets = new ArrayList<>();
private List<AppTreeNode> children = new ArrayList<>();
private Map<String, AppTreeNode> childMap = new HashMap<>();
private List<QAppSection> sections;
/*******************************************************************************
@ -64,6 +69,11 @@ public class QFrontendAppMetaData
{
this.widgets = appMetaData.getWidgets();
}
if(CollectionUtils.nullSafeHasContents(appMetaData.getSections()))
{
this.sections = appMetaData.getSections();
}
}
@ -101,6 +111,17 @@ public class QFrontendAppMetaData
/*******************************************************************************
** Getter for childMap
**
*******************************************************************************/
public Map<String, AppTreeNode> getChildMap()
{
return childMap;
}
/*******************************************************************************
** Getter for iconName
**
@ -132,6 +153,7 @@ public class QFrontendAppMetaData
{
children = new ArrayList<>();
}
childMap.put(childAppTreeNode.getName(), childAppTreeNode);
children.add(childAppTreeNode);
}
@ -145,4 +167,15 @@ public class QFrontendAppMetaData
{
return widgets;
}
/*******************************************************************************
** Getter for sections
**
*******************************************************************************/
public List<QAppSection> getSections()
{
return sections;
}
}

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
@ -40,7 +41,9 @@ public class QAppMetaData implements QAppChildMetaData
private String parentAppName;
private QIcon icon;
private List<String> widgets;
private List<String> widgets;
private List<QAppSection> sections;
/*******************************************************************************
@ -137,7 +140,13 @@ public class QAppMetaData implements QAppChildMetaData
*******************************************************************************/
public void setChildren(List<QAppChildMetaData> children)
{
this.children = children;
if(CollectionUtils.nullSafeHasContents(children))
{
for(QAppChildMetaData child : children)
{
addChild(child);
}
}
}
@ -176,7 +185,7 @@ public class QAppMetaData implements QAppChildMetaData
*******************************************************************************/
public QAppMetaData withChildren(List<QAppChildMetaData> children)
{
this.children = children;
setChildren(children);
return (this);
}
@ -205,6 +214,7 @@ public class QAppMetaData implements QAppChildMetaData
}
/*******************************************************************************
** Getter for icon
**
@ -226,6 +236,7 @@ public class QAppMetaData implements QAppChildMetaData
}
/*******************************************************************************
** Fluent setter for icon
**
@ -238,7 +249,6 @@ public class QAppMetaData implements QAppChildMetaData
/*******************************************************************************
** Getter for widgets
**
@ -260,6 +270,7 @@ public class QAppMetaData implements QAppChildMetaData
}
/*******************************************************************************
** Fluent setter for widgets
**
@ -270,4 +281,63 @@ public class QAppMetaData implements QAppChildMetaData
return (this);
}
/*******************************************************************************
** Getter for sections
**
*******************************************************************************/
public List<QAppSection> getSections()
{
return sections;
}
/*******************************************************************************
** Setter for sections
**
*******************************************************************************/
public void setSections(List<QAppSection> sections)
{
this.sections = sections;
}
/*******************************************************************************
** Fluent setter for sections
**
*******************************************************************************/
public QAppMetaData withSections(List<QAppSection> sections)
{
this.sections = sections;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public void addSection(QAppSection section)
{
if(this.sections == null)
{
this.sections = new ArrayList<>();
}
this.sections.add(section);
}
/*******************************************************************************
** Fluent setter for sections
**
*******************************************************************************/
public QAppMetaData withSection(QAppSection section)
{
this.addSection(section);
return (this);
}
}

View File

@ -0,0 +1,233 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.layout;
import java.util.List;
/*******************************************************************************
** A section of apps/tables/processes - a logical grouping.
*******************************************************************************/
public class QAppSection
{
private String name;
private String label;
private QIcon icon;
private List<String> tables;
private List<String> processes;
/*******************************************************************************
**
*******************************************************************************/
public QAppSection()
{
}
/*******************************************************************************
**
*******************************************************************************/
public QAppSection(String name, String label, QIcon icon, List<String> tables, List<String> processes)
{
this.name = name;
this.label = label;
this.icon = icon;
this.tables = tables;
this.processes = processes;
}
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Setter for name
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
** Fluent setter for name
**
*******************************************************************************/
public QAppSection withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
** Fluent setter for label
**
*******************************************************************************/
public QAppSection withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for tables
**
*******************************************************************************/
public List<String> getTables()
{
return tables;
}
/*******************************************************************************
** Setter for tables
**
*******************************************************************************/
public void setTables(List<String> tables)
{
this.tables = tables;
}
/*******************************************************************************
** Fluent setter for tables
**
*******************************************************************************/
public QAppSection withTables(List<String> tables)
{
this.tables = tables;
return (this);
}
/*******************************************************************************
** Getter for processes
**
*******************************************************************************/
public List<String> getProcesses()
{
return processes;
}
/*******************************************************************************
** Setter for processes
**
*******************************************************************************/
public void setProcesses(List<String> processes)
{
this.processes = processes;
}
/*******************************************************************************
** Fluent setter for processes
**
*******************************************************************************/
public QAppSection withProcesses(List<String> processes)
{
this.processes = processes;
return (this);
}
/*******************************************************************************
** Getter for icon
**
*******************************************************************************/
public QIcon getIcon()
{
return icon;
}
/*******************************************************************************
** Setter for icon
**
*******************************************************************************/
public void setIcon(QIcon icon)
{
this.icon = icon;
}
/*******************************************************************************
** Fluent setter for icon
**
*******************************************************************************/
public QAppSection withIcon(QIcon icon)
{
this.icon = icon;
return (this);
}
}

View File

@ -25,7 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.dashboard;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.BarChart;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.ChartData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
import com.kingsrook.qqq.backend.core.model.session.QSession;
@ -45,7 +45,7 @@ public class PersonsByCreateDateBarChart extends AbstractWidgetRenderer
try
{
List<String> labels = new ArrayList<>();
List<Number> data = new ArrayList<>();
List<Number> data = new ArrayList<>();
labels.add("Jan. 2022");
data.add(17);
@ -62,7 +62,7 @@ public class PersonsByCreateDateBarChart extends AbstractWidgetRenderer
labels.add("May 2022");
data.add(64);
return (new BarChart("Persons created per Month", "Person records", labels, data));
return (new ChartData("Persons created per Month", null, "Person records", labels, data));
}
catch(Exception e)
{

View File

@ -23,7 +23,7 @@ package com.kingsrook.qqq.backend.core.actions.dashboard;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.BarChart;
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.ChartData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@ -43,11 +43,11 @@ class WidgetDataLoaderTest
void test() throws QException
{
Object widgetData = new WidgetDataLoader().execute(TestUtils.defineInstance(), TestUtils.getMockSession(), PersonsByCreateDateBarChart.class.getSimpleName());
assertThat(widgetData).isInstanceOf(BarChart.class);
BarChart barChart = (BarChart) widgetData;
assertEquals("barChart", barChart.getType());
assertThat(barChart.getTitle()).isNotBlank();
assertNotNull(barChart.getBarChartData());
assertThat(widgetData).isInstanceOf(ChartData.class);
ChartData chartData = (ChartData) widgetData;
assertEquals("chartData", chartData.getType());
assertThat(chartData.getTitle()).isNotBlank();
assertNotNull(chartData.getChartData());
}
}
}

View File

@ -30,7 +30,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_GREETINGS;
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_MISCELLANEOUS;
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_PEOPLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@ -163,6 +167,35 @@ class QInstanceEnricherTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testGenerateAppSections()
{
QInstance qInstance = TestUtils.defineInstance();
new QInstanceEnricher().enrich(qInstance);
assertNotNull(qInstance.getApp(APP_NAME_GREETINGS).getSections());
assertEquals(1, qInstance.getApp(APP_NAME_GREETINGS).getSections().size(), "App should automatically have one section");
assertEquals(0, qInstance.getApp(APP_NAME_GREETINGS).getSections().get(0).getTables().size(), "Section should not have tables");
assertEquals(2, qInstance.getApp(APP_NAME_GREETINGS).getSections().get(0).getProcesses().size(), "Section should have two processes");
assertEquals(qInstance.getApp(APP_NAME_GREETINGS).getName(), qInstance.getApp(APP_NAME_GREETINGS).getSections().get(0).getName(), "Section name should default to app's");
assertNotNull(qInstance.getApp(APP_NAME_PEOPLE).getSections());
assertEquals(1, qInstance.getApp(APP_NAME_PEOPLE).getSections().size(), "App should automatically have one section");
assertEquals(2, qInstance.getApp(APP_NAME_PEOPLE).getSections().get(0).getTables().size(), "Section should have two tables");
assertEquals(0, qInstance.getApp(APP_NAME_PEOPLE).getSections().get(0).getProcesses().size(), "Section should not have processes");
assertEquals(qInstance.getApp(APP_NAME_PEOPLE).getName(), qInstance.getApp(APP_NAME_PEOPLE).getSections().get(0).getName(), "Section name should default to app's");
assertNotNull(qInstance.getApp(APP_NAME_MISCELLANEOUS).getSections());
assertEquals(1, qInstance.getApp(APP_NAME_MISCELLANEOUS).getSections().size(), "App should automatically have one section");
assertEquals(1, qInstance.getApp(APP_NAME_MISCELLANEOUS).getSections().get(0).getTables().size(), "Section should have one table");
assertEquals(1, qInstance.getApp(APP_NAME_MISCELLANEOUS).getSections().get(0).getProcesses().size(), "Section should have one process");
assertEquals(qInstance.getApp(APP_NAME_MISCELLANEOUS).getName(), qInstance.getApp(APP_NAME_MISCELLANEOUS).getSections().get(0).getName(), "Section name should default to app's");
}
/*******************************************************************************
**
*******************************************************************************/
@ -175,12 +208,12 @@ class QInstanceEnricherTest
assertNull(table.getRecordLabelFormat());
qInstance = TestUtils.defineInstance();
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName");
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName");
new QInstanceEnricher().enrich(qInstance);
assertEquals("%s", table.getRecordLabelFormat());
qInstance = TestUtils.defineInstance();
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName", "lastName");
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName", "lastName");
new QInstanceEnricher().enrich(qInstance);
assertEquals("%s %s", table.getRecordLabelFormat());
}

View File

@ -41,13 +41,14 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@ -575,6 +576,123 @@ class QInstanceValidatorTest
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAppSectionsMissingName()
{
QAppMetaData app = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection(null, "Section 1", new QIcon("person"), List.of("test"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app), "Missing a name");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAppSectionsMissingLabel()
{
QAppMetaData app = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("Section 1", null, new QIcon("person"), List.of("test"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app), "Missing a label");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAppSectionsNoFields()
{
QAppMetaData app1 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of(), List.of()));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app1), "section1 does not have any children", "child test is not listed in any app sections");
QAppMetaData app2 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), null, null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app2), "section1 does not have any children", "child test is not listed in any app sections");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAppSectionsUnrecognizedFieldName()
{
QAppMetaData app1 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test", "tset"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app1), "not a child of this app");
QAppMetaData app2 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test"), List.of("tset")));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app2), "not a child of this app");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testAppSectionsDuplicatedFieldName()
{
QAppMetaData app1 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test", "test"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app1), "more than once");
QAppMetaData app2 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test"), null))
.withSection(new QAppSection("section2", "Section 2", new QIcon("person"), List.of("test"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app2), "more than once");
QAppMetaData app3 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test"), List.of("test")));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app3), "more than once");
QAppMetaData app4 = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), null, List.of("test", "test")));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app4), "more than once");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testChildNotInAnySections()
{
QTableMetaData table = new QTableMetaData().withName("test")
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
.withSection(new QFieldSection("section1", "Section 1", new QIcon("person"), Tier.T1, List.of("id")))
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
.withField(new QFieldMetaData("name", QFieldType.STRING));
assertValidationFailureReasons((qInstance) -> qInstance.addTable(table), "not listed in any field sections");
QAppMetaData app = new QAppMetaData().withName("test")
.withChild(new QTableMetaData().withName("tset"))
.withChild(new QTableMetaData().withName("test"))
.withSection(new QAppSection("section1", "Section 1", new QIcon("person"), List.of("test"), null));
assertValidationFailureReasons((qInstance) -> qInstance.addApp(app), "not listed in any app sections");
}
/*******************************************************************************
**
*******************************************************************************/
@ -825,6 +943,7 @@ class QInstanceValidatorTest
}
/*******************************************************************************
**
*******************************************************************************/