From 61d493a4f55d022be6ade483113d27cb6407ce53 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 8 Dec 2022 09:13:25 -0600 Subject: [PATCH] Add FieldValueList widget type, more html-helper methods --- .../dashboard/AbstractHTMLWidgetRenderer.java | 60 +++- .../actions/dashboard/RenderWidgetAction.java | 8 +- .../widgets/ChildRecordListRenderer.java | 21 +- .../core/actions/values/QValueFormatter.java | 48 +-- .../core/instances/QInstanceValidator.java | 8 + .../widgets/ChildRecordListData.java | 36 ++ .../dashboard/widgets/FieldValueListData.java | 310 ++++++++++++++++++ .../model/dashboard/widgets/StepperData.java | 74 ++++- .../model/dashboard/widgets/WidgetType.java | 3 +- .../dashboard/ParentWidgetMetaData.java | 2 +- .../dashboard/QWidgetMetaDataInterface.java | 24 ++ .../dashboard/QuickSightChartMetaData.java | 57 ++++ .../model/metadata/fields/AdornmentType.java | 1 + .../model/metadata/fields/QFieldMetaData.java | 16 + .../actions/values/QValueFormatterTest.java | 75 ++--- 15 files changed, 666 insertions(+), 77 deletions(-) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/FieldValueListData.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/AbstractHTMLWidgetRenderer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/AbstractHTMLWidgetRenderer.java index 672b4aaa..c461eda1 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/AbstractHTMLWidgetRenderer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/AbstractHTMLWidgetRenderer.java @@ -31,10 +31,12 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; +import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; +import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat; import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData; import com.kingsrook.qqq.backend.core.utils.JsonUtils; @@ -125,9 +127,9 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer /******************************************************************************* ** *******************************************************************************/ - public static String linkTableBulkLoadChildren(String baseHref, String tableName) throws QException + public static String linkTableBulkLoadChildren(String tableName) throws QException { - return (baseHref + "#/launchProcess=" + tableName + ".bulkInsert"); + return ("#/launchProcess=" + tableName + ".bulkInsert"); } @@ -165,6 +167,17 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer + /******************************************************************************* + ** + *******************************************************************************/ + public static String aHrefTableFilterNoOfRecords(RenderWidgetInput input, String tableName, QQueryFilter filter, Integer noOfRecords, String singularLabel, String pluralLabel) throws QException + { + String href = linkTableFilter(input, tableName, filter); + return ("" + QValueFormatter.formatValue(DisplayFormat.COMMAS, noOfRecords) + " " + pluralize(noOfRecords, singularLabel, pluralLabel) + ""); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -203,9 +216,9 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer /******************************************************************************* ** *******************************************************************************/ - public static String linkTableCreateChild(String baseHref, String childTableName, Map defaultValues) + public static String linkTableCreateChild(String childTableName, Map defaultValues) { - return (linkTableCreateChild(baseHref, childTableName, defaultValues, defaultValues.keySet())); + return (linkTableCreateChild(childTableName, defaultValues, defaultValues.keySet())); } @@ -213,13 +226,48 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer /******************************************************************************* ** *******************************************************************************/ - public static String linkTableCreateChild(String baseHref, String childTableName, Map defaultValues, Set disabledFields) + public static String aHrefTableCreateChild(String childTableName, Map defaultValues) + { + return (aHrefTableCreateChild(childTableName, defaultValues, defaultValues.keySet())); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static String linkTableCreateChild(String childTableName, Map defaultValues, Set disabledFields) { Map disabledFieldsMap = disabledFields.stream().collect(Collectors.toMap(k -> k, k -> 1)); - return (baseHref + "#/createChild=" + childTableName + return ("#/createChild=" + childTableName + "/defaultValues=" + URLEncoder.encode(JsonUtils.toJson(defaultValues), StandardCharsets.UTF_8).replaceAll("\\+", "%20") + "/disabledFields=" + URLEncoder.encode(JsonUtils.toJson(disabledFieldsMap), StandardCharsets.UTF_8).replaceAll("\\+", "%20")); } + + + /******************************************************************************* + ** + *******************************************************************************/ + public static String aHrefTableCreateChild(String childTableName, Map defaultValues, Set disabledFields) + { + return ("Create new"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static String pluralize(Integer count, String singular, String plural) + { + if(count != null && count.equals(1)) + { + return (singular); + } + + return (plural); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/RenderWidgetAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/RenderWidgetAction.java index 225d3f19..b1c1d46e 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/RenderWidgetAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/RenderWidgetAction.java @@ -30,7 +30,6 @@ import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRe import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput; import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput; -import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData; import com.kingsrook.qqq.backend.core.utils.ValueUtils; @@ -53,12 +52,9 @@ public class RenderWidgetAction /////////////////////////////////////////////////////////////// // move default values from meta data into this render input // /////////////////////////////////////////////////////////////// - if(input.getWidgetMetaData() instanceof QWidgetMetaData widgetMetaData) + for(Map.Entry entry : input.getWidgetMetaData().getDefaultValues().entrySet()) { - for(Map.Entry entry : widgetMetaData.getDefaultValues().entrySet()) - { - input.addQueryParam(entry.getKey(), ValueUtils.getValueAsString(entry.getValue())); - } + input.addQueryParam(entry.getKey(), ValueUtils.getValueAsString(entry.getValue())); } return (widgetRenderer.render(input)); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java index c2918111..c5e83eb2 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/ChildRecordListRenderer.java @@ -26,8 +26,10 @@ import java.io.Serializable; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.kingsrook.qqq.backend.core.actions.tables.GetAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.exceptions.QException; @@ -117,11 +119,22 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer /******************************************************************************* ** *******************************************************************************/ - public AbstractWidgetMetaDataBuilder withCanAddChildRecord(boolean b) + public Builder withCanAddChildRecord(boolean b) { widgetMetaData.withDefaultValue("canAddChildRecord", true); return (this); } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public Builder withDisabledFieldsForNewChildRecords(Set disabledFieldsForNewChildRecords) + { + widgetMetaData.withDefaultValue("disabledFieldsForNewChildRecords", new HashSet<>(disabledFieldsForNewChildRecords)); + return (this); + } } @@ -191,6 +204,12 @@ public class ChildRecordListRenderer extends AbstractWidgetRenderer defaultValuesForNewChildRecords.put(joinOn.getRightField(), record.getValue(joinOn.getLeftField())); } widgetData.setDefaultValuesForNewChildRecords(defaultValuesForNewChildRecords); + + Map widgetValues = input.getWidgetMetaData().getDefaultValues(); + if(widgetValues.containsKey("disabledFieldsForNewChildRecords")) + { + widgetData.setDisabledFieldsForNewChildRecords((Set) widgetValues.get("disabledFieldsForNewChildRecords")); + } } return (new RenderWidgetOutput(widgetData)); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java index e6772489..cf43ab44 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatter.java @@ -26,6 +26,7 @@ import java.io.Serializable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Collection; import java.util.List; import java.util.Map; import com.kingsrook.qqq.backend.core.model.data.QRecord; @@ -54,7 +55,7 @@ public class QValueFormatter /******************************************************************************* ** For a field, and its value, apply the field's displayFormat. *******************************************************************************/ - public String formatValue(QFieldMetaData field, Serializable value) + public static String formatValue(QFieldMetaData field, Serializable value) { if(QFieldType.BOOLEAN.equals(field.getType())) { @@ -81,7 +82,7 @@ public class QValueFormatter /******************************************************************************* ** For a display format string (e.g., %d), and a value, apply the displayFormat. *******************************************************************************/ - public String formatValue(String displayFormat, Serializable value) + public static String formatValue(String displayFormat, Serializable value) { return (formatValue(displayFormat, "", value)); } @@ -92,7 +93,7 @@ public class QValueFormatter ** For a display format string, an optional fieldName (only used for logging), ** and a value, apply the format. *******************************************************************************/ - private String formatValue(String displayFormat, String fieldName, Serializable value) + private static String formatValue(String displayFormat, String fieldName, Serializable value) { ////////////////////////////////// // null values get null results // @@ -151,7 +152,7 @@ public class QValueFormatter /******************************************************************************* ** *******************************************************************************/ - public String formatDate(LocalDate date) + public static String formatDate(LocalDate date) { return (dateFormatter.format(date)); } @@ -161,7 +162,7 @@ public class QValueFormatter /******************************************************************************* ** *******************************************************************************/ - public String formatDateTime(LocalDateTime dateTime) + public static String formatDateTime(LocalDateTime dateTime) { return (dateTimeFormatter.format(dateTime)); } @@ -171,7 +172,7 @@ public class QValueFormatter /******************************************************************************* ** Make a string from a table's recordLabelFormat and fields, for a given record. *******************************************************************************/ - public String formatRecordLabel(QTableMetaData table, QRecord record) + public static String formatRecordLabel(QTableMetaData table, QRecord record) { if(!StringUtils.hasContent(table.getRecordLabelFormat())) { @@ -195,7 +196,7 @@ public class QValueFormatter ** For a given format string, and a list of fields, look in displayValueMap and ** rawValueMap to get the values to apply to the format. *******************************************************************************/ - private String formatStringWithFields(String formatString, List formatFields, Map displayValueMap, Map rawValueMap) + private static String formatStringWithFields(String formatString, List formatFields, Map displayValueMap, Map rawValueMap) { List values = formatFields.stream() .map(fieldName -> @@ -221,7 +222,7 @@ public class QValueFormatter ** For a given format string, and a list of values, apply the format. Note, null ** values in the list become "". *******************************************************************************/ - public String formatStringWithValues(String formatString, List formatValues) + public static String formatStringWithValues(String formatString, List formatValues) { List values = formatValues.stream() .map(v -> v == null ? "" : v) @@ -234,7 +235,7 @@ public class QValueFormatter /******************************************************************************* ** Deal with non-happy-path cases for making a record label. *******************************************************************************/ - private String formatRecordLabelExceptionalCases(QTableMetaData table, QRecord record) + private static String formatRecordLabelExceptionalCases(QTableMetaData table, QRecord record) { /////////////////////////////////////////////////////////////////////////////////////// // if there's no record label format, then just return the primary key display value // @@ -262,7 +263,7 @@ public class QValueFormatter /******************************************************************************* ** For a list of records, set their recordLabels and display values *******************************************************************************/ - public void setDisplayValuesInRecords(QTableMetaData table, List records) + public static void setDisplayValuesInRecords(QTableMetaData table, List records) { if(records == null) { @@ -271,17 +272,26 @@ public class QValueFormatter for(QRecord record : records) { - for(QFieldMetaData field : table.getFields().values()) - { - if(record.getDisplayValue(field.getName()) == null) - { - String formattedValue = formatValue(field, record.getValue(field.getName())); - record.setDisplayValue(field.getName(), formattedValue); - } - } - + setDisplayValuesInRecord(table.getFields().values(), record); record.setRecordLabel(formatRecordLabel(table, record)); } } + + + /******************************************************************************* + ** For a list of records, set their display values + *******************************************************************************/ + public static void setDisplayValuesInRecord(Collection fields, QRecord record) + { + for(QFieldMetaData field : fields) + { + if(record.getDisplayValue(field.getName()) == null) + { + String formattedValue = formatValue(field, record.getValue(field.getName())); + record.setDisplayValue(field.getName(), formattedValue); + } + } + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java index 2a76e5b7..6d1c1834 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java @@ -186,6 +186,14 @@ public class QInstanceValidator assertNoException(() -> qInstance.getTable(join.getRightTable()).getField(joinOn.getRightField()), "Right field name in joinOn " + joinName + " is not a defined field in table " + join.getRightTable()); } } + + for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(join.getOrderBys())) + { + if(rightTableExists) + { + assertNoException(() -> qInstance.getTable(join.getRightTable()).getField(orderBy.getFieldName()), "Field name " + orderBy.getFieldName() + " in orderBy for join " + joinName + " is not a defined field in the right-table " + join.getRightTable()); + } + } }); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java index 2b1116bf..b4b6870d 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/ChildRecordListData.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.dashboard.widgets; import java.io.Serializable; import java.util.Map; +import java.util.Set; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; @@ -43,6 +44,7 @@ public class ChildRecordListData implements QWidget private boolean canAddChildRecord = false; private Map defaultValuesForNewChildRecords; + private Set disabledFieldsForNewChildRecords; @@ -283,4 +285,38 @@ public class ChildRecordListData implements QWidget return (this); } + + + /******************************************************************************* + ** Getter for disabledFieldsForNewChildRecords + ** + *******************************************************************************/ + public Set getDisabledFieldsForNewChildRecords() + { + return disabledFieldsForNewChildRecords; + } + + + + /******************************************************************************* + ** Setter for disabledFieldsForNewChildRecords + ** + *******************************************************************************/ + public void setDisabledFieldsForNewChildRecords(Set disabledFieldsForNewChildRecords) + { + this.disabledFieldsForNewChildRecords = disabledFieldsForNewChildRecords; + } + + + + /******************************************************************************* + ** Fluent setter for disabledFieldsForNewChildRecords + ** + *******************************************************************************/ + public ChildRecordListData withDisabledFieldsForNewChildRecords(Set disabledFieldsForNewChildRecords) + { + this.disabledFieldsForNewChildRecords = disabledFieldsForNewChildRecords; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/FieldValueListData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/FieldValueListData.java new file mode 100644 index 00000000..553d5015 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/FieldValueListData.java @@ -0,0 +1,310 @@ +/* + * 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 . + */ + +package com.kingsrook.qqq.backend.core.model.dashboard.widgets; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter; +import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher; +import com.kingsrook.qqq.backend.core.model.data.QRecord; +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.utils.Pair; +import com.kingsrook.qqq.backend.core.utils.StringUtils; + + +/******************************************************************************* + ** Model containing data structure expected by frontend FieldValueListData widget + ** + *******************************************************************************/ +public class FieldValueListData implements QWidget +{ + private List fields; + private QRecord record; + + private Map fieldLabelPrefixIconNames; + private Map fieldLabelPrefixIconColors; + private Map fieldIndentLevels; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public FieldValueListData() + { + this.fields = new ArrayList<>(); + this.record = new QRecord(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public QFieldMetaData addField(QFieldMetaData field) + { + fields.add(field); + return (field); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setValue(String fieldName, Serializable value) + { + record.setValue(fieldName, value); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setDisplayValue(String fieldName, String displayValue) + { + record.setDisplayValue(fieldName, displayValue); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public QFieldMetaData addFieldWithValue(String fieldName, QFieldType type, Serializable value) + { + return (addFieldWithValue(fieldName, type, value, null)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public QFieldMetaData addFieldWithValue(String fieldName, QFieldType type, Serializable value, String displayValue) + { + QFieldMetaData field = new QFieldMetaData(fieldName, type); + addField(field); + + record.setValue(fieldName, value); + if(displayValue != null) + { + record.setDisplayValue(fieldName, displayValue); + } + + return (field); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public FieldValueListData(List fields, QRecord record) + { + this.fields = fields; + this.record = record; + enrich(); + } + + + + /******************************************************************************* + ** do some enrichment on fields (e.g., name -> label) and set display values in the record. + *******************************************************************************/ + public void enrich() + { + for(QFieldMetaData field : fields) + { + if(!StringUtils.hasContent(field.getLabel())) + { + field.setLabel(QInstanceEnricher.nameToLabel(field.getName())); + } + } + + QValueFormatter.setDisplayValuesInRecord(fields, record); + } + + + + /******************************************************************************* + ** Getter for type + ** + *******************************************************************************/ + public String getType() + { + return WidgetType.FIELD_VALUE_LIST.getType(); + } + + + + /******************************************************************************* + ** Getter for fields + ** + *******************************************************************************/ + public List getFields() + { + return fields; + } + + + + /******************************************************************************* + ** Setter for fields + ** + *******************************************************************************/ + public void setFields(List fields) + { + this.fields = fields; + } + + + + /******************************************************************************* + ** Fluent setter for fields + ** + *******************************************************************************/ + public FieldValueListData withFields(List fields) + { + this.fields = fields; + return (this); + } + + + + /******************************************************************************* + ** Getter for record + ** + *******************************************************************************/ + public QRecord getRecord() + { + return record; + } + + + + /******************************************************************************* + ** Setter for record + ** + *******************************************************************************/ + public void setRecord(QRecord record) + { + this.record = record; + } + + + + /******************************************************************************* + ** Fluent setter for record + ** + *******************************************************************************/ + public FieldValueListData withRecord(QRecord record) + { + this.record = record; + return (this); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setFieldLabelPrefixIconAndColor(String fieldName, Pair iconAndColorPair) + { + setFieldLabelPrefixIconAndColor(fieldName, iconAndColorPair.getA(), iconAndColorPair.getB()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setFieldLabelPrefixIconAndColor(String fieldName, String iconName, String color) + { + if(fieldLabelPrefixIconNames == null) + { + fieldLabelPrefixIconNames = new HashMap<>(); + } + + if(fieldLabelPrefixIconColors == null) + { + fieldLabelPrefixIconColors = new HashMap<>(); + } + + fieldLabelPrefixIconNames.put(fieldName, iconName); + fieldLabelPrefixIconColors.put(fieldName, color); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setFieldIndentLevel(String fieldName, Integer indentLevel) + { + if(fieldIndentLevels == null) + { + fieldIndentLevels = new HashMap<>(); + } + + fieldIndentLevels.put(fieldName, indentLevel); + } + + + + /******************************************************************************* + ** Getter for fieldLabelPrefixIconNames + ** + *******************************************************************************/ + public Map getFieldLabelPrefixIconNames() + { + return fieldLabelPrefixIconNames; + } + + + + /******************************************************************************* + ** Getter for fieldLabelPrefixIconColors + ** + *******************************************************************************/ + public Map getFieldLabelPrefixIconColors() + { + return fieldLabelPrefixIconColors; + } + + + + /******************************************************************************* + ** Getter for fieldIndentLevels + ** + *******************************************************************************/ + public Map getFieldIndentLevels() + { + return fieldIndentLevels; + } +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StepperData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StepperData.java index 409e0fbe..213991b2 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StepperData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/StepperData.java @@ -171,6 +171,9 @@ public class StepperData implements QWidget private String linkText; private String linkURL; + private String iconOverride; + private String colorOverride; + /******************************************************************************* @@ -178,9 +181,6 @@ public class StepperData implements QWidget *******************************************************************************/ public Step() { - this.label = label; - this.linkText = linkText; - this.linkURL = linkURL; } @@ -297,6 +297,74 @@ public class StepperData implements QWidget return (this); } + + + /******************************************************************************* + ** Getter for iconOverride + ** + *******************************************************************************/ + public String getIconOverride() + { + return iconOverride; + } + + + + /******************************************************************************* + ** Setter for iconOverride + ** + *******************************************************************************/ + public void setIconOverride(String iconOverride) + { + this.iconOverride = iconOverride; + } + + + + /******************************************************************************* + ** Fluent setter for iconOverride + ** + *******************************************************************************/ + public Step withIconOverride(String iconOverride) + { + this.iconOverride = iconOverride; + return (this); + } + + + + /******************************************************************************* + ** Getter for colorOverride + ** + *******************************************************************************/ + public String getColorOverride() + { + return colorOverride; + } + + + + /******************************************************************************* + ** Setter for colorOverride + ** + *******************************************************************************/ + public void setColorOverride(String colorOverride) + { + this.colorOverride = colorOverride; + } + + + + /******************************************************************************* + ** Fluent setter for colorOverride + ** + *******************************************************************************/ + public Step withColorOverride(String colorOverride) + { + this.colorOverride = colorOverride; + return (this); + } + } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java index 22baab2d..00ccee71 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/dashboard/widgets/WidgetType.java @@ -41,7 +41,8 @@ public enum WidgetType QUICK_SIGHT_CHART("quickSightChart"), STATISTICS("statistics"), STEPPER("stepper"), - TABLE("table"); + TABLE("table"), + FIELD_VALUE_LIST("fieldValueList"); private final String type; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java index d8ff1fe8..589cc570 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/ParentWidgetMetaData.java @@ -29,7 +29,7 @@ import java.util.List; ** Specific meta data for frontend parent widget ** *******************************************************************************/ -public class ParentWidgetMetaData extends QWidgetMetaData implements QWidgetMetaDataInterface +public class ParentWidgetMetaData extends QWidgetMetaData { private String title; private List childWidgetNameList; diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QWidgetMetaDataInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QWidgetMetaDataInterface.java index 280886d0..a6d1df1f 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QWidgetMetaDataInterface.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QWidgetMetaDataInterface.java @@ -22,6 +22,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.dashboard; +import java.io.Serializable; +import java.util.Map; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; @@ -115,4 +117,26 @@ public interface QWidgetMetaDataInterface ** Setter for type *******************************************************************************/ void setIcon(String type); + + /******************************************************************************* + ** Getter for defaultValues + *******************************************************************************/ + Map getDefaultValues(); + + /******************************************************************************* + ** Setter for defaultValues + *******************************************************************************/ + void setDefaultValues(Map defaultValues); + + /******************************************************************************* + ** Fluent setter for defaultValues + *******************************************************************************/ + QWidgetMetaData withDefaultValues(Map defaultValues); + + /******************************************************************************* + ** Fluent setter for a single defaultValue + *******************************************************************************/ + QWidgetMetaData withDefaultValue(String key, Serializable value); + } + diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QuickSightChartMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QuickSightChartMetaData.java index d8ded20e..a70c64d6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QuickSightChartMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/dashboard/QuickSightChartMetaData.java @@ -22,7 +22,10 @@ package com.kingsrook.qqq.backend.core.model.metadata.dashboard; +import java.io.Serializable; import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; /******************************************************************************* @@ -39,6 +42,8 @@ public class QuickSightChartMetaData extends QWidgetMetaData implements QWidgetM private String region; private Collection allowedDomains; + protected Map defaultValues = new LinkedHashMap<>(); + /******************************************************************************* @@ -323,4 +328,56 @@ public class QuickSightChartMetaData extends QWidgetMetaData implements QWidgetM this.allowedDomains = allowedDomains; return this; } + + + + /******************************************************************************* + ** Getter for defaultValues + ** + *******************************************************************************/ + public Map getDefaultValues() + { + return defaultValues; + } + + + + /******************************************************************************* + ** Setter for defaultValues + ** + *******************************************************************************/ + public void setDefaultValues(Map defaultValues) + { + this.defaultValues = defaultValues; + } + + + + /******************************************************************************* + ** Fluent setter for defaultValues + ** + *******************************************************************************/ + public QWidgetMetaData withDefaultValues(Map defaultValues) + { + this.defaultValues = defaultValues; + return (this); + } + + + + /******************************************************************************* + ** Fluent setter for a single defaultValue + ** + *******************************************************************************/ + public QWidgetMetaData withDefaultValue(String key, Serializable value) + { + if(this.defaultValues == null) + { + this.defaultValues = new LinkedHashMap<>(); + } + + this.defaultValues.put(key, value); + + return (this); + } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java index 61dbfee0..a35ae4f5 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/AdornmentType.java @@ -37,6 +37,7 @@ public enum AdornmentType CHIP, SIZE, CODE_EDITOR, + RENDER_HTML, ERROR; ////////////////////////////////////////////////////////////////////////// // keep these values in sync with AdornmentType.ts in qqq-frontend-core // diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java index ca11da98..7147d6a2 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/fields/QFieldMetaData.java @@ -561,6 +561,22 @@ public class QFieldMetaData implements Cloneable + /******************************************************************************* + ** Fluent setter for adornments + ** + *******************************************************************************/ + public QFieldMetaData withFieldAdornment(AdornmentType adornmentType) + { + if(this.adornments == null) + { + this.adornments = new ArrayList<>(); + } + this.adornments.add(new FieldAdornment(adornmentType)); + return (this); + } + + + /******************************************************************************* ** Getter for maxLength ** diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatterTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatterTest.java index 3620ba0b..97fe57ab 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatterTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/QValueFormatterTest.java @@ -48,39 +48,37 @@ class QValueFormatterTest @Test void testFormatValue() { - QValueFormatter qValueFormatter = new QValueFormatter(); + assertNull(QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), null)); - assertNull(qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), null)); + assertEquals("1", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1)); + assertEquals("1,000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1000)); + assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(null), 1000)); + assertEquals("$1,000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.CURRENCY), 1000)); + assertEquals("1,000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2_COMMAS), 1000)); + assertEquals("1000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2), 1000)); - assertEquals("1", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1)); - assertEquals("1,000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1000)); - assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(null), 1000)); - assertEquals("$1,000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.CURRENCY), 1000)); - assertEquals("1,000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2_COMMAS), 1000)); - assertEquals("1000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2), 1000)); + assertEquals("1", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1"))); + assertEquals("1,000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000"))); + assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), new BigDecimal("1000"))); + assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), 1000)); - assertEquals("1", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1"))); - assertEquals("1,000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000"))); - assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), new BigDecimal("1000"))); - assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), 1000)); + assertEquals("1%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT), 1)); + assertEquals("1%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT), new BigDecimal("1.0"))); + assertEquals("1.0%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), 1)); + assertEquals("1.1%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), new BigDecimal("1.1"))); + assertEquals("1.1%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), new BigDecimal("1.12"))); + assertEquals("1.00%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), 1)); + assertEquals("1.10%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), new BigDecimal("1.1"))); + assertEquals("1.12%", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), new BigDecimal("1.12"))); - assertEquals("1%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT), 1)); - assertEquals("1%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT), new BigDecimal("1.0"))); - assertEquals("1.0%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), 1)); - assertEquals("1.1%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), new BigDecimal("1.1"))); - assertEquals("1.1%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT1), new BigDecimal("1.12"))); - assertEquals("1.00%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), 1)); - assertEquals("1.10%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), new BigDecimal("1.1"))); - assertEquals("1.12%", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.PERCENT_POINT2), new BigDecimal("1.12"))); - - assertNull(qValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), null)); - assertEquals("Yes", qValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), true)); - assertEquals("No", qValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), false)); + assertNull(QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), null)); + assertEquals("Yes", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), true)); + assertEquals("No", QValueFormatter.formatValue(new QFieldMetaData().withType(QFieldType.BOOLEAN), false)); ////////////////////////////////////////////////// // this one flows through the exceptional cases // ////////////////////////////////////////////////// - assertEquals("1000.01", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000.01"))); + assertEquals("1000.01", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000.01"))); } @@ -91,42 +89,40 @@ class QValueFormatterTest @Test void testFormatRecordLabel() { - QValueFormatter qValueFormatter = new QValueFormatter(); - QTableMetaData table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("firstName", "lastName")); - assertEquals("Darin Kelkhoff", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff"))); - assertEquals("Darin ", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin"))); - assertEquals("Darin ", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", null))); + assertEquals("Darin Kelkhoff", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff"))); + assertEquals("Darin ", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin"))); + assertEquals("Darin ", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", null))); table = new QTableMetaData().withRecordLabelFormat("%s " + DisplayFormat.CURRENCY).withRecordLabelFields("firstName", "price"); - assertEquals("Darin $10,000.00", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("price", new BigDecimal(10000)))); + assertEquals("Darin $10,000.00", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("price", new BigDecimal(10000)))); table = new QTableMetaData().withRecordLabelFormat(DisplayFormat.DEFAULT).withRecordLabelFields(List.of("id")); - assertEquals("123456", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", "123456"))); + assertEquals("123456", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", "123456"))); /////////////////////////////////////////////////////// // exceptional flow: no recordLabelFormat specified // /////////////////////////////////////////////////////// table = new QTableMetaData().withPrimaryKeyField("id"); - assertEquals("42", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 42))); + assertEquals("42", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 42))); ///////////////////////////////////////////////// // exceptional flow: no fields for the format // ///////////////////////////////////////////////// table = new QTableMetaData().withRecordLabelFormat("%s %s").withPrimaryKeyField("id"); - assertEquals("128", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 128))); + assertEquals("128", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 128))); ///////////////////////////////////////////////////////// // exceptional flow: not enough fields for the format // ///////////////////////////////////////////////////////// table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields("a").withPrimaryKeyField("id"); - assertEquals("256", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("id", 256))); + assertEquals("256", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("id", 256))); ////////////////////////////////////////////////////////////////////////////////////////////////////////// // exceptional flow (kinda): too many fields for the format (just get the ones that are in the format) // ////////////////////////////////////////////////////////////////////////////////////////////////////////// table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("a", "b", "c")).withPrimaryKeyField("id"); - assertEquals("47 48", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("b", 48).withValue("c", 49).withValue("id", 256))); + assertEquals("47 48", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("b", 48).withValue("c", 49).withValue("id", 256))); } @@ -149,9 +145,8 @@ class QValueFormatterTest ///////////////////////////////////////////////////////////////// // first, make sure it doesn't crash with null or empty inputs // ///////////////////////////////////////////////////////////////// - QValueFormatter qValueFormatter = new QValueFormatter(); - qValueFormatter.setDisplayValuesInRecords(table, null); - qValueFormatter.setDisplayValuesInRecords(table, Collections.emptyList()); + QValueFormatter.setDisplayValuesInRecords(table, null); + QValueFormatter.setDisplayValuesInRecords(table, Collections.emptyList()); List records = List.of( new QRecord() @@ -168,7 +163,7 @@ class QValueFormatterTest .withValue("homeStateId", 2) ); - qValueFormatter.setDisplayValuesInRecords(table, records); + QValueFormatter.setDisplayValuesInRecords(table, records); assertEquals("Tim Chamberlain", records.get(0).getRecordLabel()); assertEquals("$3.50", records.get(0).getDisplayValue("price"));