mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-06-08 21:38:21 +00:00
WIP
This commit is contained in:
parent
6597a228f0
commit
49a9d58421
@ -88,6 +88,18 @@ public class DbAdmin {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a schema by its table name
|
||||
* @param tableName the table name on the database
|
||||
* @return
|
||||
* @throws DbAdminException if corresponding schema not found
|
||||
*/
|
||||
public DbObjectSchema findSchemaByTableName(String tableName) {
|
||||
return schemas.stream().filter(s -> s.getTableName().equals(tableName)).findFirst().orElseThrow(() -> {
|
||||
return new DbAdminException("Schema " + tableName + " not found.");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a schema by its class
|
||||
* @param klass
|
||||
|
@ -11,6 +11,7 @@ import org.springframework.boot.jdbc.DataSourceBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
@ -19,6 +20,8 @@ import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import tech.ailef.dbadmin.internal.InternalDbAdminConfiguration;
|
||||
|
||||
@ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true)
|
||||
@ComponentScan
|
||||
@EnableConfigurationProperties(DbAdminProperties.class)
|
||||
@ -29,6 +32,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
basePackages = { "tech.ailef.dbadmin.internal.repository" }
|
||||
)
|
||||
@EnableTransactionManagement
|
||||
@Import(InternalDbAdminConfiguration.class)
|
||||
public class DbAdminAutoConfiguration {
|
||||
@Autowired
|
||||
Environment env;
|
||||
|
@ -13,7 +13,6 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.jdbc.UncategorizedSQLException;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@ -34,12 +33,13 @@ import tech.ailef.dbadmin.external.dbmapping.DbAdminRepository;
|
||||
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.LogsSearchRequest;
|
||||
import tech.ailef.dbadmin.external.dto.PaginatedResult;
|
||||
import tech.ailef.dbadmin.external.dto.QueryFilter;
|
||||
import tech.ailef.dbadmin.external.exceptions.InvalidPageException;
|
||||
import tech.ailef.dbadmin.external.misc.Utils;
|
||||
import tech.ailef.dbadmin.internal.model.UserAction;
|
||||
import tech.ailef.dbadmin.internal.repository.ActionRepository;
|
||||
import tech.ailef.dbadmin.internal.service.UserActionService;
|
||||
|
||||
/**
|
||||
* The main DbAdmin controller that register most of the routes of the web interface.
|
||||
@ -57,7 +57,14 @@ public class DefaultDbAdminController {
|
||||
private DbAdmin dbAdmin;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository repo;
|
||||
private UserActionService userActionService;
|
||||
|
||||
// @Autowired
|
||||
// private ActionRepository repo;
|
||||
//
|
||||
// @Autowired
|
||||
// private CustomActionRepositoryImpl customRepo;
|
||||
|
||||
|
||||
/**
|
||||
* Home page with list of schemas
|
||||
@ -299,7 +306,11 @@ public class DefaultDbAdminController {
|
||||
|
||||
if (countDeleted > 0)
|
||||
attr.addFlashAttribute("message", "Deleted " + countDeleted + " of " + ids.length + " items");
|
||||
saveAction(new UserAction(schema.getTableName(), String.join(", ", ids), "DELETE"));
|
||||
|
||||
for (String id : ids) {
|
||||
saveAction(new UserAction(schema.getTableName(), id, "DELETE"));
|
||||
}
|
||||
|
||||
return "redirect:/" + properties.getBaseUrl() + "/model/" + className;
|
||||
}
|
||||
|
||||
@ -422,8 +433,19 @@ public class DefaultDbAdminController {
|
||||
}
|
||||
|
||||
@GetMapping("/logs")
|
||||
public String logs(Model model) {
|
||||
model.addAttribute("logs", repo.findAll());
|
||||
public String logs(Model model, LogsSearchRequest searchRequest) {
|
||||
model.addAttribute("activePage", "logs");
|
||||
model.addAttribute(
|
||||
"page",
|
||||
userActionService.findActions(
|
||||
searchRequest.getTable(),
|
||||
searchRequest.getActionType(),
|
||||
searchRequest.getItemId(),
|
||||
searchRequest.toPageRequest()
|
||||
)
|
||||
);
|
||||
model.addAttribute("schemas", dbAdmin.getSchemas());
|
||||
model.addAttribute("searchRequest", searchRequest);
|
||||
return "logs";
|
||||
}
|
||||
|
||||
@ -434,8 +456,8 @@ public class DefaultDbAdminController {
|
||||
return "settings";
|
||||
}
|
||||
|
||||
@Transactional("internalTransactionManager")
|
||||
// @Transactional("internalTransactionManager")
|
||||
private UserAction saveAction(UserAction action) {
|
||||
return repo.save(action);
|
||||
return userActionService.save(action);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,12 @@ public class GlobalController {
|
||||
* @return
|
||||
*/
|
||||
@ModelAttribute("baseUrl")
|
||||
public String getBaseUrl(HttpServletRequest request) {
|
||||
public String getBaseUrl() {
|
||||
return props.getBaseUrl();
|
||||
}
|
||||
|
||||
@ModelAttribute("requestUrl")
|
||||
public String getRequestUrl(HttpServletRequest request) {
|
||||
return request.getRequestURI();
|
||||
}
|
||||
}
|
@ -117,7 +117,7 @@ public class DbAdminRepository {
|
||||
}
|
||||
|
||||
|
||||
return new PaginatedResult(
|
||||
return new PaginatedResult<DbObject>(
|
||||
new PaginationInfo(page, maxPage, pageSize, maxElement, null, new HashSet<>()),
|
||||
results
|
||||
);
|
||||
@ -227,7 +227,7 @@ public class DbAdminRepository {
|
||||
throw new InvalidPageException();
|
||||
}
|
||||
|
||||
return new PaginatedResult(
|
||||
return new PaginatedResult<DbObject>(
|
||||
new PaginationInfo(page, maxPage, pageSize, maxElement, query, queryFilters),
|
||||
jpaRepository.search(query, page, pageSize, sortKey, sortOrder, queryFilters).stream()
|
||||
.map(o -> new DbObject(o, schema))
|
||||
|
@ -1,38 +0,0 @@
|
||||
package tech.ailef.dbadmin.external.dto;
|
||||
//package tech.ailef.dbadmin.dto;
|
||||
//
|
||||
//import java.util.Set;
|
||||
//
|
||||
//public class ListModelRequest {
|
||||
// private String className;
|
||||
//
|
||||
// private String query;
|
||||
//
|
||||
// private Integer page;
|
||||
//
|
||||
// private Integer pageSize;
|
||||
//
|
||||
// private String sortKey;
|
||||
//
|
||||
// private String sortOrder;
|
||||
//
|
||||
// private Set<QueryFilter> queryFilters;
|
||||
//
|
||||
// private PaginationInfo paginationInfo;
|
||||
//
|
||||
// public ListModelRequest(String className, String query, Integer page, Integer pageSize, String sortKey,
|
||||
// String sortOrder, Set<QueryFilter> queryFilters, PaginationInfo paginationInfo) {
|
||||
// super();
|
||||
// this.className = className;
|
||||
// this.query = query;
|
||||
// this.page = page;
|
||||
// this.pageSize = pageSize;
|
||||
// this.sortKey = sortKey;
|
||||
// this.sortOrder = sortOrder;
|
||||
// this.queryFilters = queryFilters;
|
||||
// this.paginationInfo = paginationInfo;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//// @RequestParam MultiValueMap<String, String> otherParams,
|
||||
//}
|
98
src/main/java/tech/ailef/dbadmin/external/dto/LogsSearchRequest.java
vendored
Normal file
98
src/main/java/tech/ailef/dbadmin/external/dto/LogsSearchRequest.java
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
package tech.ailef.dbadmin.external.dto;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
|
||||
public class LogsSearchRequest {
|
||||
private String table;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String itemId;
|
||||
|
||||
private int page;
|
||||
|
||||
private int pageSize;
|
||||
|
||||
private String sortKey;
|
||||
|
||||
private String sortOrder;
|
||||
|
||||
public String getTable() {
|
||||
return table == null || table.isBlank() || table.equalsIgnoreCase("Any") ? null : table;
|
||||
}
|
||||
|
||||
public void setTable(String table) {
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
public String getActionType() {
|
||||
return actionType == null || actionType.isBlank() || actionType.equalsIgnoreCase("Any") ? null : actionType;
|
||||
}
|
||||
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
public String getItemId() {
|
||||
return itemId == null || itemId.isBlank() ? null : itemId;
|
||||
}
|
||||
|
||||
public void setItemId(String itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public int getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void setPage(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public int getPageSize() {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize(int pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public String getSortKey() {
|
||||
return sortKey;
|
||||
}
|
||||
|
||||
public void setSortKey(String sortKey) {
|
||||
this.sortKey = sortKey;
|
||||
}
|
||||
|
||||
public String getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
public void setSortOrder(String sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LogsSearchRequest [table=" + table + ", actionType=" + actionType + ", itemId=" + itemId + ", page="
|
||||
+ page + ", pageSize=" + pageSize + ", sortKey=" + sortKey + ", sortOrder=" + sortOrder + "]";
|
||||
}
|
||||
|
||||
public PageRequest toPageRequest() {
|
||||
int actualPage = page - 1 < 0 ? 0 : page - 1;
|
||||
int actualPageSize = pageSize <= 0 ? 50 : pageSize;
|
||||
if (sortKey == null)
|
||||
return PageRequest.of(actualPage, actualPageSize);
|
||||
|
||||
if (sortOrder == null) sortOrder = "ASC";
|
||||
|
||||
if (sortOrder.equals("DESC")) {
|
||||
return PageRequest.of(actualPage, actualPageSize, Sort.by(sortKey).descending());
|
||||
} else {
|
||||
return PageRequest.of(actualPage, actualPageSize, Sort.by(sortKey).ascending());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -4,12 +4,12 @@ import java.util.List;
|
||||
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbObject;
|
||||
|
||||
public class PaginatedResult {
|
||||
public class PaginatedResult<T> {
|
||||
private PaginationInfo pagination;
|
||||
|
||||
private List<DbObject> results;
|
||||
private List<T> results;
|
||||
|
||||
public PaginatedResult(PaginationInfo pagination, List<DbObject> page) {
|
||||
public PaginatedResult(PaginationInfo pagination, List<T> page) {
|
||||
this.pagination = pagination;
|
||||
this.results = page;
|
||||
}
|
||||
@ -18,11 +18,15 @@ public class PaginatedResult {
|
||||
return pagination;
|
||||
}
|
||||
|
||||
public List<DbObject> getResults() {
|
||||
public List<T> getResults() {
|
||||
return results;
|
||||
}
|
||||
|
||||
public int getActualResults() {
|
||||
public boolean isEmpty() {
|
||||
return results.isEmpty();
|
||||
}
|
||||
|
||||
public int getNumberOfResults() {
|
||||
return getResults().size();
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ public class PaginationInfo {
|
||||
*/
|
||||
private int pageSize;
|
||||
|
||||
// TODO: Check if used
|
||||
private long maxElement;
|
||||
|
||||
private Set<QueryFilter> queryFilters;
|
||||
|
@ -24,6 +24,8 @@ public interface Utils {
|
||||
|
||||
public static MultiValueMap<String, String> computeParams(Set<QueryFilter> filters) {
|
||||
MultiValueMap<String, String> r = new LinkedMultiValueMap<>();
|
||||
if (filters == null)
|
||||
return r;
|
||||
|
||||
r.put("filter_field", new ArrayList<>());
|
||||
r.put("filter_op", new ArrayList<>());
|
||||
|
@ -0,0 +1,12 @@
|
||||
package tech.ailef.dbadmin.internal;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@ConditionalOnProperty(name = "dbadmin.enabled", matchIfMissing = true)
|
||||
@ComponentScan
|
||||
@Configuration
|
||||
public class InternalDbAdminConfiguration {
|
||||
|
||||
}
|
@ -2,6 +2,8 @@ package tech.ailef.dbadmin.internal.model;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import org.springframework.format.datetime.standard.DateTimeFormatterFactory;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
@ -89,5 +91,8 @@ public class UserAction {
|
||||
public void setActionType(String actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
|
||||
public String getFormattedDate() {
|
||||
return new DateTimeFormatterFactory("YYYY-MM-dd HH:mm:ss").createDateTimeFormatter().format(createdAt);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
package tech.ailef.dbadmin.internal.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import tech.ailef.dbadmin.internal.model.UserAction;
|
||||
|
||||
@Repository
|
||||
public interface ActionRepository extends JpaRepository<UserAction, Integer> {
|
||||
|
||||
public interface ActionRepository extends JpaRepository<UserAction, Integer>, CustomActionRepository {
|
||||
public List<UserAction> findAllByOnTableAndActionTypeAndPrimaryKey(String table, String actionType, String primaryKey, PageRequest pageRequest);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package tech.ailef.dbadmin.internal.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
|
||||
import tech.ailef.dbadmin.internal.model.UserAction;
|
||||
|
||||
public interface CustomActionRepository {
|
||||
public List<UserAction> findActions(String table, String actionType, String itemId, PageRequest pageRequest);
|
||||
|
||||
public long countActions(String table, String actionType, String itemId);
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package tech.ailef.dbadmin.internal.repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import tech.ailef.dbadmin.external.DbAdmin;
|
||||
import tech.ailef.dbadmin.external.dbmapping.DbObjectSchema;
|
||||
import tech.ailef.dbadmin.internal.model.UserAction;
|
||||
|
||||
@Component
|
||||
public class CustomActionRepositoryImpl implements CustomActionRepository {
|
||||
|
||||
@PersistenceContext(unitName = "internal")
|
||||
private EntityManager entityManager;
|
||||
|
||||
@Autowired
|
||||
private DbAdmin dbAdmin;
|
||||
|
||||
@Override
|
||||
public List<UserAction> findActions(String table, String actionType, String itemId, PageRequest page) {
|
||||
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<UserAction> query = cb.createQuery(UserAction.class);
|
||||
Root<UserAction> userAction = query.from(UserAction.class);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
if (table != null)
|
||||
predicates.add(cb.equal(userAction.get("onTable"), table));
|
||||
if (actionType != null)
|
||||
predicates.add(cb.equal(userAction.get("actionType"), actionType));
|
||||
if (itemId != null)
|
||||
predicates.add(cb.equal(userAction.get("primaryKey"), itemId));
|
||||
|
||||
if (!predicates.isEmpty()) {
|
||||
query.select(userAction)
|
||||
.where(cb.and(
|
||||
predicates.toArray(new Predicate[predicates.size()])));
|
||||
}
|
||||
|
||||
return entityManager.createQuery(query)
|
||||
.setMaxResults(page.getPageSize())
|
||||
.setFirstResult((int)page.getOffset())
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActions(String table, String actionType, String itemId) {
|
||||
|
||||
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> query = cb.createQuery(Long.class);
|
||||
Root<UserAction> userAction = query.from(UserAction.class);
|
||||
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
if (table != null)
|
||||
predicates.add(cb.equal(userAction.get("onTable"), table));
|
||||
if (actionType != null)
|
||||
predicates.add(cb.equal(userAction.get("actionType"), actionType));
|
||||
if (itemId != null)
|
||||
predicates.add(cb.equal(userAction.get("primaryKey"), itemId));
|
||||
|
||||
if (!predicates.isEmpty()) {
|
||||
query.select(cb.count(userAction))
|
||||
.where(cb.and(
|
||||
predicates.toArray(new Predicate[predicates.size()])));
|
||||
} else {
|
||||
query.select(cb.count(userAction));
|
||||
}
|
||||
|
||||
return entityManager.createQuery(query).getSingleResult();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package tech.ailef.dbadmin.internal.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import tech.ailef.dbadmin.external.dto.PaginatedResult;
|
||||
import tech.ailef.dbadmin.external.dto.PaginationInfo;
|
||||
import tech.ailef.dbadmin.internal.model.UserAction;
|
||||
import tech.ailef.dbadmin.internal.repository.ActionRepository;
|
||||
import tech.ailef.dbadmin.internal.repository.CustomActionRepositoryImpl;
|
||||
|
||||
@Service
|
||||
public class UserActionService {
|
||||
@Autowired
|
||||
private ActionRepository repo;
|
||||
|
||||
@Autowired
|
||||
private CustomActionRepositoryImpl customRepo;
|
||||
|
||||
@Transactional("internalTransactionManager")
|
||||
public UserAction save(UserAction a) {
|
||||
return repo.save(a);
|
||||
}
|
||||
|
||||
public PaginatedResult<UserAction> findActions(String table, String actionType, String userId, PageRequest page) {
|
||||
long count = customRepo.countActions(table, actionType, userId);
|
||||
List<UserAction> actions = customRepo.findActions(table, actionType, userId, page);
|
||||
int maxPage = (int)(Math.ceil ((double)count / page.getPageSize()));
|
||||
|
||||
return new PaginatedResult<>(
|
||||
new PaginationInfo(page.getPageNumber() + 1, maxPage, page.getPageSize(), count, null, null),
|
||||
actions
|
||||
);
|
||||
}
|
||||
|
||||
}
|
12
src/main/resources/static/js/logs.js
Normal file
12
src/main/resources/static/js/logs.js
Normal file
@ -0,0 +1,12 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let form = document.getElementById('log-filter-form');
|
||||
|
||||
if (form == null) return;
|
||||
|
||||
let selects = form.querySelectorAll('select');
|
||||
selects.forEach(select => {
|
||||
select.addEventListener('change', function(e) {
|
||||
form.submit();
|
||||
});
|
||||
});
|
||||
});
|
@ -10,6 +10,7 @@
|
||||
<script type="text/javascript" src="/js/autocomplete.js"></script>
|
||||
<script type="text/javascript" src="/js/autocomplete-multi.js"></script>
|
||||
<script type="text/javascript" src="/js/filters.js"></script>
|
||||
<script type="text/javascript" src="/js/logs.js"></script>
|
||||
<script type="text/javascript" src="/js/create.js"></script>
|
||||
<title th:text="${title != null ? title + ' | Spring Boot DB Admin Panel' : 'Spring Boot DB Admin Panel'}"></title>
|
||||
<script th:inline="javascript">
|
||||
@ -64,6 +65,19 @@
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<li th:class="${#strings.equals(activePage, 'logs') ? 'active' : ''}">
|
||||
<a th:href="|/${baseUrl}/logs|">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="menu-icon">
|
||||
<i class="bi bi-file-text"></i>
|
||||
</div>
|
||||
<div class="menu-entry-text d-none d-md-block">
|
||||
Logs
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--
|
||||
<li th:class="${#strings.equals(activePage, 'console') ? 'active' : ''}">
|
||||
<a href="/live">
|
||||
@ -123,14 +137,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<nav aria-label="Results pagination" th:fragment="pagination(page)">
|
||||
<div class="d-flex justify-content-between">
|
||||
|
||||
<div th:if="${page != null && page.getPagination().getMaxPage() != 1}" class="d-flex">
|
||||
<ul class="pagination me-3">
|
||||
<li class="page-item" th:if="${page.getPagination().getCurrentPage() != 1}">
|
||||
<a class="page-link"
|
||||
th:href="@{|/${baseUrl}/model/${schema.getClassName()}${page.getPagination().getLink(page.getPagination.getCurrentPage() - 1)}|}"
|
||||
th:href="@{|${requestUrl}${page.getPagination().getLink(page.getPagination.getCurrentPage() - 1)}|}"
|
||||
aria-label="Previous">
|
||||
<span aria-hidden="true">«</span>
|
||||
<span class="sr-only">Previous</span>
|
||||
@ -139,7 +154,7 @@
|
||||
|
||||
<li class="page-item" th:each="p : ${page.getPagination().getBeforePages()}">
|
||||
<a class="page-link"
|
||||
th:href="@{|/${baseUrl}/model/${schema.getClassName()}${page.getPagination().getLink(p)}|}" th:text="${p}"></a>
|
||||
th:href="@{|${requestUrl}${page.getPagination().getLink(p)}|}" th:text="${p}"></a>
|
||||
</li>
|
||||
|
||||
<li class="page-item active">
|
||||
@ -148,13 +163,13 @@
|
||||
|
||||
<li class="page-item" th:each="p : ${page.getPagination().getAfterPages()}">
|
||||
<a class="page-link"
|
||||
th:href="@{|/${baseUrl}/model/${schema.getClassName()}${page.getPagination().getLink(p)}|}"
|
||||
th:href="@{|${requestUrl}${page.getPagination().getLink(p)}|}"
|
||||
th:text="${p}"></a>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link"
|
||||
th:if="${!page.getPagination().isLastPage()}"
|
||||
th:href="@{|/${baseUrl}/model/${schema.getClassName()}${page.getPagination().getLink(page.getPagination.getCurrentPage() + 1)}|}"
|
||||
th:href="@{|${requestUrl}${page.getPagination().getLink(page.getPagination.getCurrentPage() + 1)}|}"
|
||||
aria-label="Next">
|
||||
<span class="sr-only">Next</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
@ -162,15 +177,15 @@
|
||||
</li>
|
||||
</ul>
|
||||
<div class="me-3">
|
||||
<form method="GET" th:action="@{|/${baseUrl}/model/${schema.getClassName()}|}">
|
||||
<form method="GET" th:action="@{|${requestUrl}|}">
|
||||
<input type="hidden" th:value="${page.getPagination().getCurrentPage()}" th:name="page">
|
||||
<input type="hidden" th:value="${query}" th:name="query">
|
||||
<input type="hidden" name="pageSize">
|
||||
<th:block th:each="p : ${queryParams.keySet()}">
|
||||
<input th:each="v : ${queryParams.get(p)}"
|
||||
th:name="${p}" th:value="${v}" type="hidden"
|
||||
th:if="${p.startsWith('filter_')}">
|
||||
</th:block>
|
||||
<input th:each="v : ${queryParams.get(p)}"
|
||||
th:name="${p}" th:value="${v}" type="hidden"
|
||||
th:if="${p.startsWith('filter_')}">
|
||||
</th:block>
|
||||
<select class="form-select page-size">
|
||||
<option disabled>Page size</option>
|
||||
<option th:selected="${page.getPagination().getPageSize() == 50}">50</option>
|
||||
@ -183,14 +198,14 @@
|
||||
|
||||
<div class="d-flex align-items-center" th:if="${page.getPagination().getMaxPage() > 1}">
|
||||
<p class="m-0 p-0">
|
||||
<i>Showing [[ ${page.getActualResults()} ]] of [[ ${page.getPagination().getMaxElement()} ]] results</i>
|
||||
<i>Showing [[ ${page.getNumberOfResults()} ]] of [[ ${page.getPagination().getMaxElement()} ]] results</i>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center" th:if="${page.getPagination().getMaxPage() == 1}">
|
||||
<div class="me-3">
|
||||
<form method="GET" th:action="@{|/${baseUrl}/model/${schema.getClassName()}|}">
|
||||
<form method="GET" th:action="@{|${requestUrl}|}">
|
||||
<input type="hidden" th:value="${page.getPagination().getCurrentPage()}" th:name="page">
|
||||
<input type="hidden" th:value="${query}" th:name="query">
|
||||
<input type="hidden" name="pageSize">
|
||||
@ -204,13 +219,14 @@
|
||||
</form>
|
||||
</div>
|
||||
<p class="m-0 p-0">
|
||||
<i>Showing [[ ${page.getActualResults()} ]] of [[ ${page.getPagination().getMaxElement()} ]] results</i>
|
||||
<i>Showing [[ ${page.getNumberOfResults()} ]] of [[ ${page.getPagination().getMaxElement()} ]] results</i>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="bulk-actions">
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</html>
|
||||
|
@ -14,31 +14,81 @@
|
||||
</h1>
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<div class="w-100 d-flex inner-navigation">
|
||||
<a href="#" class="active">
|
||||
<div class="ui-tab ps-5 pe-5 p-3">
|
||||
<i class="bi bi-database pe-2"></i> APPEARANCE
|
||||
</div>
|
||||
</a>
|
||||
<a href="#">
|
||||
<div class="ui-tab ps-5 pe-5 p-3">
|
||||
<i class="bi bi-table pe-2"></i> DATA
|
||||
</div>
|
||||
</a>
|
||||
<div class="inner-navigation-border flex-grow-1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box with-navigation">
|
||||
<div class="row" th:each="entry : ${logs}">
|
||||
<div class="col-3" th:text="${entry.getCreatedAt()}">
|
||||
</div>
|
||||
<div class="col-3" th:text="${entry.getActionType()}">
|
||||
</div>
|
||||
<div class="col-3" th:text="${entry.getOnTable()}">
|
||||
</div>
|
||||
<div class="col-3" th:text="${entry.getPrimaryKey()}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<h3 class="fw-bold">Logs</h3>
|
||||
<div class="w-75">
|
||||
<form th:action="|/${baseUrl}/logs|" class="mt-3" id="log-filter-form" method="GET">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">Action type</span>
|
||||
<select name="actionType" class="form-select">
|
||||
<option>Any</option>
|
||||
<option value="CREATE"
|
||||
th:selected="${searchRequest.getActionType() != null
|
||||
&& searchRequest.getActionType().equalsIgnoreCase('CREATE') }">Create</option>
|
||||
<option value="EDIT"
|
||||
th:selected="${searchRequest.getActionType() != null
|
||||
&& searchRequest.getActionType().equalsIgnoreCase('EDIT') }">Edit</option>
|
||||
<option value="DELETE"
|
||||
th:selected="${searchRequest.getActionType() != null
|
||||
&& searchRequest.getActionType().equalsIgnoreCase('DELETE') }">Delete</option>
|
||||
</select>
|
||||
<span class="input-group-text ms-3">Table</span>
|
||||
<select name="table" class="form-select">
|
||||
<option value="Any">Any</option>
|
||||
<option th:each="schema : ${schemas}"
|
||||
th:value="${schema.getTableName()}"
|
||||
th:text="${schema.getJavaClass().getSimpleName()}"
|
||||
th:selected="${schema.getTableName().equals(searchRequest.getTable())}">
|
||||
</option>
|
||||
</select>
|
||||
<span class="input-group-text ms-3">Item ID</span>
|
||||
<input type="text" class="form-control" name="itemId"
|
||||
th:value="${searchRequest.getItemId()}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="separator mt-3 mb-3"></div>
|
||||
|
||||
|
||||
<div class="mt-3" th:if="${page.isEmpty()}">
|
||||
<div class="alert alert-warning">There are no results for your filtering criteria</div>
|
||||
</div>
|
||||
<div class="table-responsive mt-3" th:if="${!page.isEmpty()}">
|
||||
<nav th:replace="~{fragments/resources :: pagination(page=${page})}"></nav>
|
||||
|
||||
<table class="table table-striped mt-3">
|
||||
<tr class="table-data-row">
|
||||
<th>Action type</th>
|
||||
<th>Table</th>
|
||||
<th>Item ID</th>
|
||||
<th>Time</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<tr th:each="entry : ${page.getResults()}" class="table-data-row align-middle">
|
||||
<td th:text="${entry.getActionType()}">
|
||||
</td>
|
||||
<td th:text="${entry.getOnTable()}">
|
||||
</td>
|
||||
<td th:text="${entry.getPrimaryKey()}">
|
||||
</td>
|
||||
<td th:text="${entry.getFormattedDate()}">
|
||||
</td>
|
||||
<td>
|
||||
<!--
|
||||
<a href="#" class="ui-btn btn btn-primary">
|
||||
Diff <i class="text-white bi bi-search"></i>
|
||||
</a>
|
||||
-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<nav th:replace="~{fragments/resources :: pagination(page=${page})}"></nav>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user