From bebc5629613b478a8d34b4c2026afd5d1e684639 Mon Sep 17 00:00:00 2001 From: Francesco Date: Wed, 4 Oct 2023 16:26:32 +0200 Subject: [PATCH] Refactored the way input fields are rendered to allow further customization and easier support of custom fields like OffsetDateTime (#7). --- .../controller/DefaultDbAdminController.java | 3 + .../dbadmin/external/dbmapping/DbField.java | 19 ++++ .../external/dbmapping/DbFieldType.java | 65 ++++++++--- .../dbadmin/external/dto/FragmentContext.java | 21 ++++ .../resources/templates/fragments/forms.html | 14 +-- .../resources/templates/fragments/inputs.html | 107 ++++++++++++++++++ .../resources/templates/model/create.html | 65 ++--------- 7 files changed, 214 insertions(+), 80 deletions(-) create mode 100644 src/main/java/tech/ailef/dbadmin/external/dto/FragmentContext.java create mode 100644 src/main/resources/templates/fragments/inputs.html diff --git a/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java b/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java index 76b2726..0460296 100644 --- a/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java +++ b/src/main/java/tech/ailef/dbadmin/external/controller/DefaultDbAdminController.java @@ -53,6 +53,7 @@ import tech.ailef.dbadmin.external.dbmapping.DbObject; import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema; import tech.ailef.dbadmin.external.dto.CompareOperator; import tech.ailef.dbadmin.external.dto.FacetedSearchRequest; +import tech.ailef.dbadmin.external.dto.FragmentContext; import tech.ailef.dbadmin.external.dto.LogsSearchRequest; import tech.ailef.dbadmin.external.dto.PaginatedResult; import tech.ailef.dbadmin.external.dto.QueryFilter; @@ -271,6 +272,7 @@ public class DefaultDbAdminController { model.addAttribute("title", "Entities | " + schema.getJavaClass().getSimpleName() + " | Create"); model.addAttribute("activePage", "entities"); model.addAttribute("create", true); + model.addAttribute("fragmentContext", FragmentContext.CREATE); return "model/create"; } @@ -291,6 +293,7 @@ public class DefaultDbAdminController { model.addAttribute("schema", schema); model.addAttribute("activePage", "entities"); model.addAttribute("create", false); + model.addAttribute("fragmentContext", FragmentContext.CREATE); return "model/create"; } diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java index 62a875c..32693d1 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbField.java @@ -31,6 +31,7 @@ import tech.ailef.dbadmin.external.annotations.DisplayImage; import tech.ailef.dbadmin.external.annotations.Filterable; import tech.ailef.dbadmin.external.annotations.FilterableType; import tech.ailef.dbadmin.external.annotations.ReadOnly; +import tech.ailef.dbadmin.external.dto.FragmentContext; /** * Represent a field on the database, generated from an Entity class instance variable. @@ -182,6 +183,24 @@ public class DbField { return type == DbFieldType.TEXT; } + /** + * Returns the name of the Thymeleaf fragment used to render + * the input for this field. + * @return + */ + public String getFragmentName(FragmentContext c) { + return type.getFragmentName(c); + } + + /** + * Returns the name of the Thymeleaf fragment used to render + * the input for this field. + * @return + */ + public String getFragmentName() { + return type.getFragmentName(FragmentContext.DEFAULT); + } + public boolean isFilterable() { return getPrimitiveField().getAnnotation(Filterable.class) != null; } diff --git a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbFieldType.java b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbFieldType.java index 76546b8..1da4bf4 100644 --- a/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbFieldType.java +++ b/src/main/java/tech/ailef/dbadmin/external/dbmapping/DbFieldType.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.OffsetDateTime; import java.util.List; import org.springframework.web.multipart.MultipartFile; @@ -31,6 +32,7 @@ import jakarta.persistence.ManyToMany; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import tech.ailef.dbadmin.external.dto.CompareOperator; +import tech.ailef.dbadmin.external.dto.FragmentContext; import tech.ailef.dbadmin.external.exceptions.DbAdminException; /** @@ -39,7 +41,7 @@ import tech.ailef.dbadmin.external.exceptions.DbAdminException; public enum DbFieldType { INTEGER { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "number"; } @@ -60,7 +62,7 @@ public enum DbFieldType { }, DOUBLE { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "number"; } @@ -81,7 +83,7 @@ public enum DbFieldType { }, LONG { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "number"; } @@ -102,7 +104,7 @@ public enum DbFieldType { }, FLOAT { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "number"; } @@ -121,9 +123,32 @@ public enum DbFieldType { return List.of(CompareOperator.GT, CompareOperator.EQ, CompareOperator.LT); } }, + OFFSET_DATE_TIME { + @Override + public String getFragmentName(FragmentContext c) { + return "offset_datetime"; + } + + @Override + public Object parseValue(Object value) { + if (value == null) return null; + return OffsetDateTime.parse(value.toString()); + } + + @Override + public Class getJavaClass() { + return OffsetDateTime.class; + } + + @Override + public List getCompareOperators() { + return List.of(CompareOperator.AFTER, CompareOperator.STRING_EQ, CompareOperator.BEFORE); + } + + }, LOCAL_DATE { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "date"; } @@ -145,8 +170,8 @@ public enum DbFieldType { }, LOCAL_DATE_TIME { @Override - public String getHTMLName() { - return "datetime-local"; + public String getFragmentName(FragmentContext c) { + return "datetime"; } @Override @@ -167,7 +192,7 @@ public enum DbFieldType { }, STRING { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "text"; } @@ -188,8 +213,10 @@ public enum DbFieldType { }, TEXT { @Override - public String getHTMLName() { - return "textarea"; + public String getFragmentName(FragmentContext c) { + if (c == FragmentContext.CREATE) + return "textarea"; + return "text"; } @Override @@ -210,7 +237,7 @@ public enum DbFieldType { }, BOOLEAN { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "text"; } @@ -231,7 +258,7 @@ public enum DbFieldType { }, BIG_DECIMAL { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "number"; } @@ -252,7 +279,7 @@ public enum DbFieldType { }, BYTE_ARRAY { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { return "file"; } @@ -277,7 +304,7 @@ public enum DbFieldType { }, ONE_TO_MANY { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { throw new UnsupportedOperationException(); } @@ -308,7 +335,7 @@ public enum DbFieldType { }, ONE_TO_ONE { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { throw new UnsupportedOperationException(); } @@ -339,7 +366,7 @@ public enum DbFieldType { }, MANY_TO_MANY { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { throw new UnsupportedOperationException(); } @@ -370,7 +397,7 @@ public enum DbFieldType { }, COMPUTED { @Override - public String getHTMLName() { + public String getFragmentName(FragmentContext c) { throw new UnsupportedOperationException(); } @@ -390,7 +417,7 @@ public enum DbFieldType { } }; - public abstract String getHTMLName(); + public abstract String getFragmentName(FragmentContext c); public abstract Object parseValue(Object value); @@ -423,6 +450,8 @@ public enum DbFieldType { return BIG_DECIMAL; } else if (klass == byte[].class) { return BYTE_ARRAY; + } else if (klass == OffsetDateTime.class) { + return OFFSET_DATE_TIME; } else { throw new DbAdminException("Unsupported field type: " + klass); } diff --git a/src/main/java/tech/ailef/dbadmin/external/dto/FragmentContext.java b/src/main/java/tech/ailef/dbadmin/external/dto/FragmentContext.java new file mode 100644 index 0000000..f99b555 --- /dev/null +++ b/src/main/java/tech/ailef/dbadmin/external/dto/FragmentContext.java @@ -0,0 +1,21 @@ +package tech.ailef.dbadmin.external.dto; + + +/** + * Some fragments might need to be rendered differently depending + * on their context. For example a TEXT field is usually rendered + * as a text area, but if it has to fit in the faceted search right + * bar it's rendered as a normal input type "text" field for space + * reasons (and because the user just needs to search with a short + * query). + * + * This enum indicates the possible contexts and it is passed to the + * getFragmentName() method which determines which actual fragment + * to use. + * + */ +public enum FragmentContext { + DEFAULT, + CREATE, + SEARCH +} diff --git a/src/main/resources/templates/fragments/forms.html b/src/main/resources/templates/fragments/forms.html index 806fd72..bf6a4df 100644 --- a/src/main/resources/templates/fragments/forms.html +++ b/src/main/resources/templates/fragments/forms.html @@ -90,13 +90,13 @@