mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-06-09 05:48:20 +00:00
WIP
This commit is contained in:
parent
a91cddc7a2
commit
2db84d9996
@ -22,11 +22,13 @@ import org.springframework.web.multipart.MultipartFile;
|
|||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
|
||||||
|
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import tech.ailef.dbadmin.DbAdmin;
|
import tech.ailef.dbadmin.DbAdmin;
|
||||||
import tech.ailef.dbadmin.dbmapping.DbAdminRepository;
|
import tech.ailef.dbadmin.dbmapping.DbAdminRepository;
|
||||||
import tech.ailef.dbadmin.dbmapping.DbObject;
|
import tech.ailef.dbadmin.dbmapping.DbObject;
|
||||||
import tech.ailef.dbadmin.dbmapping.DbObjectSchema;
|
import tech.ailef.dbadmin.dbmapping.DbObjectSchema;
|
||||||
import tech.ailef.dbadmin.dto.PaginatedResult;
|
import tech.ailef.dbadmin.dto.PaginatedResult;
|
||||||
|
import tech.ailef.dbadmin.exceptions.DbAdminException;
|
||||||
import tech.ailef.dbadmin.exceptions.InvalidPageException;
|
import tech.ailef.dbadmin.exceptions.InvalidPageException;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@ -90,7 +92,9 @@ public class DefaultDbAdminController {
|
|||||||
public String list(Model model, @PathVariable String className,
|
public String list(Model model, @PathVariable String className,
|
||||||
@RequestParam(required=false) Integer page, @RequestParam(required=false) String query,
|
@RequestParam(required=false) Integer page, @RequestParam(required=false) String query,
|
||||||
@RequestParam(required=false) Integer pageSize, @RequestParam(required=false) String sortKey,
|
@RequestParam(required=false) Integer pageSize, @RequestParam(required=false) String sortKey,
|
||||||
@RequestParam(required=false) String sortOrder) {
|
@RequestParam(required=false) String sortOrder, @RequestParam MultiValueMap<String, String> otherParams) {
|
||||||
|
System.out.println(otherParams);
|
||||||
|
|
||||||
if (page == null) page = 1;
|
if (page == null) page = 1;
|
||||||
if (pageSize == null) pageSize = 50;
|
if (pageSize == null) pageSize = 50;
|
||||||
|
|
||||||
@ -98,8 +102,8 @@ public class DefaultDbAdminController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
PaginatedResult result = null;
|
PaginatedResult result = null;
|
||||||
if (query != null) {
|
if (query != null || !otherParams.isEmpty()) {
|
||||||
result = repository.search(schema, query, page, pageSize, sortKey, sortOrder);
|
result = repository.search(schema, query, page, pageSize, sortKey, sortOrder, otherParams);
|
||||||
} else {
|
} else {
|
||||||
result = repository.findAll(schema, page, pageSize, sortKey, sortOrder);
|
result = repository.findAll(schema, page, pageSize, sortKey, sortOrder);
|
||||||
}
|
}
|
||||||
@ -337,9 +341,12 @@ public class DefaultDbAdminController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("/settings")
|
@GetMapping("/settings")
|
||||||
public String settings(Model model) {
|
public String settings(Model model) {
|
||||||
model.addAttribute("activePage", "settings");
|
model.addAttribute("activePage", "settings");
|
||||||
return "settings";
|
return "settings";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
package tech.ailef.dbadmin.controller;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class registers some ModelAttribute objects that are
|
||||||
|
* used in all templates.
|
||||||
|
*/
|
||||||
|
@ControllerAdvice
|
||||||
|
public class GlobalController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A multi valued map containing the query parameters. It is used primarily
|
||||||
|
* in building complex URL when performing faceted search with multiple filters.
|
||||||
|
* @param request the incoming request
|
||||||
|
* @return multi valued map of request parameters
|
||||||
|
*/
|
||||||
|
@ModelAttribute("queryParams")
|
||||||
|
public Map<String, String[]> getQueryParams(HttpServletRequest request) {
|
||||||
|
return request.getParameterMap();
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
@ -12,6 +13,7 @@ import jakarta.persistence.criteria.CriteriaQuery;
|
|||||||
import jakarta.persistence.criteria.Path;
|
import jakarta.persistence.criteria.Path;
|
||||||
import jakarta.persistence.criteria.Predicate;
|
import jakarta.persistence.criteria.Predicate;
|
||||||
import jakarta.persistence.criteria.Root;
|
import jakarta.persistence.criteria.Root;
|
||||||
|
import tech.ailef.dbadmin.exceptions.DbAdminException;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
public class AdvancedJpaRepository extends SimpleJpaRepository {
|
public class AdvancedJpaRepository extends SimpleJpaRepository {
|
||||||
@ -27,47 +29,40 @@ public class AdvancedJpaRepository extends SimpleJpaRepository {
|
|||||||
this.schema = schema;
|
this.schema = schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long count(String q) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public long count(String q, MultiValueMap<String, String> filteringParams) {
|
||||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery query = cb.createQuery(Long.class);
|
CriteriaQuery query = cb.createQuery(Long.class);
|
||||||
Root root = query.from(schema.getJavaClass());
|
Root root = query.from(schema.getJavaClass());
|
||||||
|
|
||||||
List<DbField> stringFields =
|
List<Predicate> finalPredicates = buildPredicates(q, filteringParams, cb, root);
|
||||||
schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
System.out.println("STRING F = " + stringFields);
|
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
|
||||||
for (DbField f : stringFields) {
|
|
||||||
Path path = root.get(f.getJavaName());
|
|
||||||
predicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%"));
|
|
||||||
}
|
|
||||||
|
|
||||||
query.select(cb.count(root.get(schema.getPrimaryKey().getName())))
|
query.select(cb.count(root.get(schema.getPrimaryKey().getName())))
|
||||||
.where(cb.or(predicates.toArray(new Predicate[predicates.size()])));
|
.where(
|
||||||
|
cb.and(
|
||||||
|
finalPredicates.toArray(new Predicate[finalPredicates.size()])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
Object o = entityManager.createQuery(query).getSingleResult();
|
Object o = entityManager.createQuery(query).getSingleResult();
|
||||||
return (Long)o;
|
return (Long)o;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public List<Object> search(String q, int page, int pageSize, String sortKey, String sortOrder) {
|
public List<Object> search(String q, int page, int pageSize, String sortKey, String sortOrder, MultiValueMap<String, String> filteringParams) {
|
||||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||||
CriteriaQuery query = cb.createQuery(schema.getJavaClass());
|
CriteriaQuery query = cb.createQuery(schema.getJavaClass());
|
||||||
Root root = query.from(schema.getJavaClass());
|
Root root = query.from(schema.getJavaClass());
|
||||||
|
|
||||||
List<DbField> stringFields =
|
List<Predicate> finalPredicates = buildPredicates(q, filteringParams, cb, root);
|
||||||
schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING)
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
List<Predicate> predicates = new ArrayList<>();
|
|
||||||
for (DbField f : stringFields) {
|
|
||||||
Path path = root.get(f.getJavaName());
|
|
||||||
predicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%"));
|
|
||||||
}
|
|
||||||
|
|
||||||
query.select(root)
|
query.select(root)
|
||||||
.where(cb.or(predicates.toArray(new Predicate[predicates.size()])));
|
.where(
|
||||||
|
cb.and(
|
||||||
|
finalPredicates.toArray(new Predicate[finalPredicates.size()]) // query search on String fields
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
if (sortKey != null)
|
if (sortKey != null)
|
||||||
query.orderBy(sortOrder.equals("DESC") ? cb.desc(root.get(sortKey)) : cb.asc(root.get(sortKey)));
|
query.orderBy(sortOrder.equals("DESC") ? cb.desc(root.get(sortKey)) : cb.asc(root.get(sortKey)));
|
||||||
|
|
||||||
@ -75,19 +70,72 @@ public class AdvancedJpaRepository extends SimpleJpaRepository {
|
|||||||
.setFirstResult((page - 1) * pageSize).getResultList();
|
.setFirstResult((page - 1) * pageSize).getResultList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> distinctFieldValues(DbField field) {
|
private List<Predicate> buildPredicates(String q, MultiValueMap<String, String> filteringParams,
|
||||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
CriteriaBuilder cb, Path root) {
|
||||||
|
List<Predicate> finalPredicates = new ArrayList<>();
|
||||||
|
|
||||||
Class<?> outputType = field.getType().getJavaClass();
|
List<DbField> stringFields =
|
||||||
if (field.getConnectedType() != null) {
|
schema.getSortedFields().stream().filter(f -> f.getType() == DbFieldType.STRING)
|
||||||
outputType = field.getConnectedSchema().getPrimaryKey().getType().getJavaClass();
|
.collect(Collectors.toList());
|
||||||
}
|
|
||||||
|
|
||||||
CriteriaQuery query = cb.createQuery(outputType);
|
List<Predicate> queryPredicates = new ArrayList<>();
|
||||||
Root root = query.from(schema.getJavaClass());
|
if (q != null) {
|
||||||
|
for (DbField f : stringFields) {
|
||||||
|
Path path = root.get(f.getJavaName());
|
||||||
|
queryPredicates.add(cb.like(cb.lower(cb.toString(path)), "%" + q.toLowerCase() + "%"));
|
||||||
|
}
|
||||||
|
|
||||||
query.select(root.get(field.getJavaName()).as(outputType)).distinct(true);
|
Predicate queryPredicate = cb.or(queryPredicates.toArray(new Predicate[queryPredicates.size()]));
|
||||||
|
finalPredicates.add(queryPredicate);
|
||||||
|
}
|
||||||
|
|
||||||
return entityManager.createQuery(query).getResultList();
|
/*
|
||||||
|
* Compute filtering predicates
|
||||||
|
*/
|
||||||
|
if (filteringParams != null) {
|
||||||
|
List<String> ops = filteringParams.get("filter_op[]");
|
||||||
|
List<String> fields = filteringParams.get("filter_field[]");
|
||||||
|
List<String> values = filteringParams.get("filter_value[]");
|
||||||
|
|
||||||
|
|
||||||
|
if (ops != null && fields != null && values != null) {
|
||||||
|
if (ops.size() != fields.size() || fields.size() != values.size()
|
||||||
|
|| ops.size() != values.size()) {
|
||||||
|
throw new DbAdminException("Filtering parameters must have the same size");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ops.size(); i++) {
|
||||||
|
String op = ops.get(i);
|
||||||
|
String field = fields.get(i);
|
||||||
|
String value = values.get(i);
|
||||||
|
|
||||||
|
if (op.equalsIgnoreCase("equals")) {
|
||||||
|
finalPredicates.add(cb.equal(cb.toString(root.get(field)), value));
|
||||||
|
} else if (op.equalsIgnoreCase("contains")) {
|
||||||
|
System.out.println("CONTAINS");
|
||||||
|
finalPredicates.add(cb.like(cb.toString(root.get(field)), "%" + value + "%"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return finalPredicates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @SuppressWarnings("unchecked")
|
||||||
|
// public List<Object> distinctFieldValues(DbField field) {
|
||||||
|
// CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||||
|
//
|
||||||
|
// Class<?> outputType = field.getType().getJavaClass();
|
||||||
|
// if (field.getConnectedType() != null) {
|
||||||
|
// outputType = field.getConnectedSchema().getPrimaryKey().getType().getJavaClass();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// CriteriaQuery query = cb.createQuery(outputType);
|
||||||
|
// Root root = query.from(schema.getJavaClass());
|
||||||
|
//
|
||||||
|
// query.select(root.get(field.getJavaName()).as(outputType)).distinct(true);
|
||||||
|
//
|
||||||
|
// return entityManager.createQuery(query).getResultList();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
|
|||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
|
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
@ -63,8 +64,8 @@ public class DbAdminRepository {
|
|||||||
* @param query
|
* @param query
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public long count(DbObjectSchema schema, String query) {
|
public long count(DbObjectSchema schema, String query, MultiValueMap<String, String> filteringParams) {
|
||||||
return schema.getJpaRepository().count(query);
|
return schema.getJpaRepository().count(query, filteringParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -223,10 +224,11 @@ public class DbAdminRepository {
|
|||||||
* @param query
|
* @param query
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public PaginatedResult search(DbObjectSchema schema, String query, int page, int pageSize, String sortKey, String sortOrder) {
|
public PaginatedResult search(DbObjectSchema schema, String query, int page, int pageSize, String sortKey,
|
||||||
|
String sortOrder, MultiValueMap<String, String> filteringParams) {
|
||||||
AdvancedJpaRepository jpaRepository = schema.getJpaRepository();
|
AdvancedJpaRepository jpaRepository = schema.getJpaRepository();
|
||||||
|
|
||||||
long maxElement = count(schema, query);
|
long maxElement = count(schema, query, filteringParams);
|
||||||
int maxPage = (int)(Math.ceil ((double)maxElement / pageSize));
|
int maxPage = (int)(Math.ceil ((double)maxElement / pageSize));
|
||||||
|
|
||||||
if (page <= 0) page = 1;
|
if (page <= 0) page = 1;
|
||||||
@ -236,7 +238,7 @@ public class DbAdminRepository {
|
|||||||
|
|
||||||
return new PaginatedResult(
|
return new PaginatedResult(
|
||||||
new PaginationInfo(page, maxPage, pageSize, maxElement),
|
new PaginationInfo(page, maxPage, pageSize, maxElement),
|
||||||
jpaRepository.search(query, page, pageSize, sortKey, sortOrder).stream()
|
jpaRepository.search(query, page, pageSize, sortKey, sortOrder, filteringParams).stream()
|
||||||
.map(o -> new DbObject(o, schema))
|
.map(o -> new DbObject(o, schema))
|
||||||
.toList()
|
.toList()
|
||||||
);
|
);
|
||||||
@ -251,7 +253,7 @@ public class DbAdminRepository {
|
|||||||
public List<DbObject> search(DbObjectSchema schema, String query) {
|
public List<DbObject> search(DbObjectSchema schema, String query) {
|
||||||
AdvancedJpaRepository jpaRepository = schema.getJpaRepository();
|
AdvancedJpaRepository jpaRepository = schema.getJpaRepository();
|
||||||
|
|
||||||
return jpaRepository.search(query, 1, 50, null, null).stream()
|
return jpaRepository.search(query, 1, 50, null, null, null).stream()
|
||||||
.map(o -> new DbObject(o, schema))
|
.map(o -> new DbObject(o, schema))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
@ -183,9 +183,9 @@ public class DbObjectSchema {
|
|||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> getFieldValues(DbField field) {
|
// public List<Object> getFieldValues(DbField field) {
|
||||||
return jpaRepository.distinctFieldValues(field);
|
// return jpaRepository.distinctFieldValues(field);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public Object[] getInsertArray(Map<String, String> params, Map<String, MultipartFile> files) {
|
public Object[] getInsertArray(Map<String, String> params, Map<String, MultipartFile> files) {
|
||||||
int currentIndex = 0;
|
int currentIndex = 0;
|
||||||
|
@ -4,4 +4,5 @@ spring.datasource.username=sa
|
|||||||
spring.datasource.password=password
|
spring.datasource.password=password
|
||||||
#spring.h2.console.enabled=true
|
#spring.h2.console.enabled=true
|
||||||
|
|
||||||
|
spring.jpa.show-sql=true
|
||||||
|
|
||||||
|
@ -38,5 +38,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class="card mb-3 filterable-field" th:fragment="filter_field(field)">
|
||||||
|
<div class="card-header noselect cursor-pointer">
|
||||||
|
<i class="bi bi-caret-right filter-icon align-middle"></i>
|
||||||
|
<span class="fw-bold align-middle" th:text="${field.getName()}"></span>
|
||||||
|
</div>
|
||||||
|
<div class="card-body d-none">
|
||||||
|
|
||||||
|
<form action="" method="GET">
|
||||||
|
<th:block th:each="p : ${queryParams.keySet()}">
|
||||||
|
<input th:each="v : ${queryParams.get(p)}" th:name="${p}" th:value="${v}" type="hidden">
|
||||||
|
</th:block>
|
||||||
|
<div class="input-group pe-2">
|
||||||
|
<th:block th:if="${field.isForeignKey()}">
|
||||||
|
<div th:replace="~{fragments/forms :: input_autocomplete(field=${field}, value='')}">
|
||||||
|
</div>
|
||||||
|
<input type="hidden" th:value="${field.getType()}" th:name="|__dbadmin_${field.getName()}_type|">
|
||||||
|
</th:block>
|
||||||
|
<th:block th:unless="${field.isForeignKey()}">
|
||||||
|
<div class="container w-25">
|
||||||
|
<select class="form-select w-auto" name="filter_op[]">
|
||||||
|
<option value="equals">Equals</option>
|
||||||
|
<option value="contains">Contains</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="filter_field[]" th:value="${field.getName()}">
|
||||||
|
<input placeholder="NULL" th:type="${field.getType().getHTMLName()}"
|
||||||
|
name="filter_value[]"
|
||||||
|
class="form-control" th:id="|__id_${field.getName()}|"
|
||||||
|
th:classAppend="${field.isPrimaryKey() && object != null ? 'disable' : ''}"
|
||||||
|
th:required="${!field.isNullable() && !field.isPrimaryKey()}"
|
||||||
|
step="any"
|
||||||
|
oninvalid="this.setCustomValidity('This field is not nullable.')"
|
||||||
|
oninput="this.setCustomValidity('')">
|
||||||
|
<input type="hidden" th:value="${field.getType()}" th:name="|__dbadmin_${field.getName()}_type|">
|
||||||
|
</th:block>
|
||||||
|
<button class="ui-btn btn btn-primary">Filter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<th:block th:if="${field.getConnectedType() != null}">
|
||||||
|
<div th:each="val : ${schema.getFieldValues(field)}">
|
||||||
|
<span th:text="${val}"></span>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
|
|
||||||
|
|
||||||
<h3 class="fw-bold mb-4 align-baseline w-100">
|
<h3 class="fw-bold mb-4 align-baseline flex-grow-1">
|
||||||
<span title="Java class name"> [[ ${schema.getJavaClass().getSimpleName()} ]] </span>
|
<span title="Java class name"> [[ ${schema.getJavaClass().getSimpleName()} ]] </span>
|
||||||
<span title="Database table name" class="ms-3 label label-primary label-gray font-monospace">
|
<span title="Database table name" class="ms-3 label label-primary label-gray font-monospace">
|
||||||
[[ ${schema.getTableName()} ]]
|
[[ ${schema.getTableName()} ]]
|
||||||
@ -55,7 +55,8 @@
|
|||||||
|
|
||||||
</h3>
|
</h3>
|
||||||
<h3><a title="Create new item"
|
<h3><a title="Create new item"
|
||||||
th:href="|/dbadmin/model/${schema.getClassName()}/create|"><i class="bi bi-plus-square"></i></a></h3>
|
th:href="|/dbadmin/model/${schema.getClassName()}/create|"><i class="bi bi-plus-square"></i></a>
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div th:replace="~{fragments/table_selectable :: table(results=${page.getResults()}, schema=${schema})}">
|
<div th:replace="~{fragments/table_selectable :: table(results=${page.getResults()}, schema=${schema})}">
|
||||||
@ -66,20 +67,11 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<h3 class="fw-bold mb-3"><i class="bi bi-funnel"></i> Filters</h3>
|
<h3 class="fw-bold mb-3"><i class="bi bi-funnel"></i> Filters</h3>
|
||||||
|
|
||||||
<div class="card mb-3 filterable-field" th:each="field : ${schema.getFilterableFields()}">
|
|
||||||
<div class="card-header noselect cursor-pointer">
|
|
||||||
<i class="bi bi-caret-right filter-icon align-middle"></i>
|
|
||||||
<span class="fw-bold align-middle" th:text="${field.getName()}"></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body d-none">
|
|
||||||
<th:block th:if="${field.getConnectedType() != null}">
|
<th:block th:each="field : ${schema.getFilterableFields()}">
|
||||||
<div th:each="val : ${schema.getFieldValues(field)}">
|
<div th:replace="~{fragments/forms :: filter_field(field=${field})}"></div>
|
||||||
<span th:text="${val}"></span>
|
</th:block>
|
||||||
</div>
|
|
||||||
</th:block>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user