mirror of
https://github.com/dalbodeule/snap-admin.git
synced 2025-08-13 15:11:14 +00:00
0.0.3
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
.separator-light {
|
||||
opacity: 25%;
|
||||
}
|
||||
|
||||
form.delete-form {
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -52,7 +56,7 @@ tr.table-data-row td:last-child, tr.table-data-row th:last-child {
|
||||
|
||||
.row-icons {
|
||||
font-size: 1.2rem;
|
||||
width: 128px;
|
||||
width: 96px;
|
||||
}
|
||||
|
||||
h1 .bi {
|
||||
@@ -112,6 +116,8 @@ h1 a:hover {
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
z-index: 999;
|
||||
-webkit-box-shadow: 0px 11px 12px -1px rgba(0,0,0,0.13);
|
||||
box-shadow: 0px 11px 12px -1px rgba(0,0,0,0.13);
|
||||
}
|
||||
|
||||
.suggestion {
|
||||
@@ -124,7 +130,7 @@ h1 a:hover {
|
||||
|
||||
.suggestion:hover {
|
||||
cursor: pointer;
|
||||
background-color: #FFF;
|
||||
background-color: #EBF7FF;
|
||||
border-bottom: 2px solid #ADDEFF;
|
||||
}
|
||||
|
||||
@@ -173,4 +179,15 @@ AUTOCOMPLETE
|
||||
|
||||
.filterable-field .card-header:hover {
|
||||
background-color: #F0F0F0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Images
|
||||
*/
|
||||
.thumb-image {
|
||||
max-width: 128px;
|
||||
}
|
||||
.img-muted {
|
||||
filter: brightness(50%);
|
||||
|
||||
}
|
43
src/main/resources/static/js/create.js
Normal file
43
src/main/resources/static/js/create.js
Normal file
@@ -0,0 +1,43 @@
|
||||
function showFileInput(inputElement) {
|
||||
inputElement.classList.add('d-block');
|
||||
inputElement.classList.remove('d-none');
|
||||
inputElement.value = '';
|
||||
|
||||
let img = document.getElementById(`__thumb_${inputElement.name}`);
|
||||
if (img != null) {
|
||||
img.classList.add('img-muted');
|
||||
}
|
||||
}
|
||||
|
||||
function hideFileInput(inputElement) {
|
||||
inputElement.classList.add('d-none');
|
||||
inputElement.classList.remove('d-block');
|
||||
|
||||
let img = document.getElementById(`__thumb_${inputElement.name}`);
|
||||
if (img != null) {
|
||||
img.classList.remove('img-muted');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let checkboxes = document.querySelectorAll(".binary-field-checkbox");
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
let fieldName = checkbox.dataset.fieldname;
|
||||
|
||||
if (!checkbox.checked) {
|
||||
showFileInput(document.querySelector(`input[name="${fieldName}"]`));
|
||||
} else {
|
||||
hideFileInput(document.querySelector(`input[name="${fieldName}"]`));
|
||||
}
|
||||
|
||||
checkbox.addEventListener('change', function(e) {
|
||||
if (!e.target.checked) {
|
||||
showFileInput(document.querySelector(`input[name="${fieldName}"]`));
|
||||
} else {
|
||||
hideFileInput(document.querySelector(`input[name="${fieldName}"]`));
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,15 +1,23 @@
|
||||
function updateBulkActions(table, selected) {
|
||||
let divs = document.querySelectorAll(".bulk-actions");
|
||||
divs.forEach(div => {
|
||||
div.innerHTML = `${selected} items selected <input type="submit" form="delete-form" class="ui-btn btn btn-secondary" value="Delete">`;
|
||||
div.innerHTML = `${selected} items selected <input type="submit" form="multi-delete-form" class="ui-btn btn btn-secondary" value="Delete">`;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
let selected = 0;
|
||||
|
||||
if (document.getElementById('delete-form') != null) {
|
||||
document.getElementById('delete-form').addEventListener('submit', function(e) {
|
||||
document.querySelectorAll(".delete-form").forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
if (!confirm('Are you sure you want to delete this item?')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (document.getElementById('multi-delete-form') != null) {
|
||||
document.getElementById('multi-delete-form').addEventListener('submit', function(e) {
|
||||
if (selected == 0) {
|
||||
e.preventDefault();
|
||||
alert('No items selected');
|
||||
|
@@ -5,9 +5,18 @@
|
||||
<tr th:fragment="data_row(row, selectable)" class="table-data-row">
|
||||
<td th:if=${selectable} class="table-checkbox">
|
||||
<input type="checkbox" class="form-check-input" name="ids"
|
||||
th:value="${row.getPrimaryKeyValue()}" form="delete-form">
|
||||
th:value="${row.getPrimaryKeyValue()}" form="multi-delete-form">
|
||||
</td>
|
||||
<td th:each="field : ${schema.getSortedFields()}">
|
||||
<td class="text-center row-icons">
|
||||
<a class="ps-1" th:href="|/dbadmin/model/${schema.getJavaClass().getName()}/edit/${row.getPrimaryKeyValue()}|">
|
||||
<i class="bi bi-pencil-square"></i></a>
|
||||
<form class="delete-form" method="POST"
|
||||
th:action="|/dbadmin/model/${schema.getJavaClass().getName()}/delete/${row.getPrimaryKeyValue()}|">
|
||||
<button><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
<td th:each="field : ${schema.getSortedFields()}"
|
||||
th:classAppend="${field.isBinary() ? 'text-center' : ''}">
|
||||
<th:block th:if="${!row.has(field)}">
|
||||
<span class="font-monospace null-label">NULL</span>
|
||||
</th:block>
|
||||
@@ -19,21 +28,12 @@
|
||||
<td th:each="colName : ${schema.getComputedColumnNames()}">
|
||||
<span th:text="${row.compute(colName)}"></span>
|
||||
</td>
|
||||
|
||||
<td class="text-center row-icons" th:if="${selectable}">
|
||||
<a class="ps-1" th:href="|/dbadmin/model/${schema.getJavaClass().getName()}/edit/${row.getPrimaryKeyValue()}|">
|
||||
<i class="bi bi-pencil-square"></i></a>
|
||||
<form class="delete-form" method="POST"
|
||||
th:action="|/dbadmin/model/${schema.getJavaClass().getName()}/delete/${row.getPrimaryKeyValue()}|">
|
||||
<button><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<!-- data-row-field fragment -->
|
||||
<th:block th:fragment="data_row_field(field, object)">
|
||||
<th:block th:if="${field.getConnectedType() != null}">
|
||||
<th:block th:if="${field.getConnectedType() != null && object.traverse(field) != null}">
|
||||
<a th:href="|/dbadmin/model/${field.getConnectedType().getName()}/show/${object.traverse(field).getPrimaryKeyValue()}|">
|
||||
<span th:text="${object.has(field) ? object.traverse(field).getPrimaryKeyValue() : 'NULL'}"></span>
|
||||
</a>
|
||||
@@ -61,12 +61,19 @@
|
||||
</th:block>
|
||||
<span th:unless="${!field.isBinary()}">
|
||||
<th:block th:if="${object.get(field).getValue()}">
|
||||
<div th:if="${field.isImage()}" class="mb-2">
|
||||
<img class="thumb-image"
|
||||
th:src="|/dbadmin/download/${schema.getClassName()}/${field.getJavaName()}/${object.getPrimaryKeyValue()}/image|">
|
||||
</div>
|
||||
|
||||
<a class="text-decoration-none null-label"
|
||||
th:href="|/dbadmin/download/${schema.getJavaClass().getName()}/${field.getName()}/${object.get(schema.getPrimaryKey()).getValue()}|">
|
||||
th:href="|/dbadmin/download/${schema.getClassName()}/${field.getJavaName()}/${object.getPrimaryKeyValue()}|">
|
||||
<i class="align-middle bi bi-box-arrow-down"></i><span class="align-middle"> Download
|
||||
<!--/*--> <span class="text-muted">([[ ${object.get(field).getValue().length} ]] bytes)</span> <!--*/-->
|
||||
</span>
|
||||
</a>
|
||||
|
||||
|
||||
</th:block>
|
||||
<th:block th:unless="${object.get(field).getValue()}">
|
||||
<span class="font-monospace null-label">NULL</span>
|
||||
|
@@ -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/create.js"></script>
|
||||
<title th:text="${title != null ? title + ' | Spring Boot DB Admin Panel' : 'Spring Boot DB Admin Panel'}"></title>
|
||||
</head>
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
<div th:if="${results != null && results.size() > 0}">
|
||||
<table class="table table-striped align-middle mt-3">
|
||||
<tr class="table-data-row">
|
||||
<th class="row-icons"></th>
|
||||
<th th:each="field : ${schema.getSortedFields()}">
|
||||
<div class="m-0 p-0 d-flex justify-content-between">
|
||||
<div class="column-title">
|
||||
|
@@ -2,18 +2,19 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
|
||||
<head></head>
|
||||
<body>
|
||||
<div class="table-selectable" th:fragment="table(results, schema)">
|
||||
<div class="table-selectable table-responsive" th:fragment="table(results, schema)">
|
||||
<div th:if="${results.isEmpty()}">
|
||||
<p>This table contains no data.</p>
|
||||
</div>
|
||||
<div th:if="${results.size() > 0}">
|
||||
<form id="delete-form" th:action="|/dbadmin/model/${schema.getClassName()}/delete|" method="POST">
|
||||
<form id="multi-delete-form" th:action="|/dbadmin/model/${schema.getClassName()}/delete|" method="POST">
|
||||
</form>
|
||||
<nav th:replace="~{fragments/resources :: pagination(${page})}">
|
||||
</nav>
|
||||
<table class="table table-striped align-middle mt-3">
|
||||
<tr class="table-data-row">
|
||||
<th class="table-checkbox"><input type="checkbox" class="form-check-input check-all"></th>
|
||||
<th></th>
|
||||
<th class="table-data-row" th:each="field : ${schema.getSortedFields()}">
|
||||
<div class="m-0 p-0 d-flex justify-content-between">
|
||||
<div class="column-title">
|
||||
@@ -56,7 +57,6 @@
|
||||
</div>
|
||||
<p class="m-0 p-0 dbfieldtype"><small>COMPUTED</small></p>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
<th:block th:each="r : ${results}">
|
||||
<tr th:replace="~{fragments/data_row :: data_row(row=${r},selectable=${true})}"></tr>
|
||||
|
@@ -13,9 +13,10 @@
|
||||
|
||||
<h1 class="fw-bold mb-4">
|
||||
<i class="align-middle bi bi-database"></i>
|
||||
<span class="align-middle">Entities</span>
|
||||
<span class="align-middle"><a href="/dbadmin">Entities</a></span>
|
||||
<i class="align-middle bi bi-chevron-double-right"></i>
|
||||
<span class="align-middle"> [[ ${schema.getJavaClass().getSimpleName()} ]] </span>
|
||||
<a class="align-middle" th:href="|/dbadmin/model/${schema.getJavaClass().getName()}|">
|
||||
[[ ${schema.getJavaClass().getSimpleName()} ]] </a>
|
||||
<i class="align-middle bi bi-chevron-double-right"></i>
|
||||
<span class="align-middle" th:text="${create ? 'Create' : 'Edit'}"></span>
|
||||
<th:block th:if="${!create}">
|
||||
@@ -30,8 +31,9 @@
|
||||
<form class="form" enctype="multipart/form-data" method="post" th:action="|/dbadmin/model/${className}/create|">
|
||||
<input type="hidden" name="__dbadmin_create" th:value="${create}">
|
||||
<div th:each="field : ${schema.getSortedFields()}" class="mt-2">
|
||||
<label th:for="|__id_${field.getName()}|" class="mb-1">[[ ${field.getName()} ]]</label>
|
||||
|
||||
<label th:for="|__id_${field.getName()}|" class="mb-1 fw-bold">
|
||||
[[ ${field.getName()} ]]
|
||||
</label>
|
||||
|
||||
<th:block th:if="${field.isForeignKey()}">
|
||||
<div th:replace="~{fragments/forms :: input_autocomplete(field=${field}, value=${
|
||||
@@ -39,15 +41,14 @@
|
||||
: (object != null ? object.traverse(field).getPrimaryKeyValue() : '' )
|
||||
})}">
|
||||
</div>
|
||||
<!-- <input type="hidden" th:value="${field.getType()}" th:name="|__dbadmin_${field.getName()}_type|"> -->
|
||||
</th:block>
|
||||
<th:block th:unless="${field.isForeignKey()}">
|
||||
<input placeholder="NULL" th:type="${field.getType().getHTMLName()}"
|
||||
|
||||
<input th:if="${!field.isBinary()}" placeholder="NULL" th:type="${field.getType().getHTMLName()}"
|
||||
th:name="${field.getName()}"
|
||||
th:value="
|
||||
${create ? (params != null ? params.getOrDefault(field.getName(), '') : '')
|
||||
: (object != null ? object.get(field).getValue() : '' )}
|
||||
|
||||
"
|
||||
class="form-control" th:id="|__id_${field.getName()}|"
|
||||
th:classAppend="${field.isPrimaryKey() && object != null ? 'disable' : ''}"
|
||||
@@ -55,8 +56,33 @@
|
||||
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|"> -->
|
||||
|
||||
<!--/*--> Binary field <!--*/-->
|
||||
<th:block th:if="${field.isBinary()}">
|
||||
<!--/*--> Edit options <!--*/-->
|
||||
<div th:if="${!create && object.get(field).getValue() != null}">
|
||||
<input type="checkbox"
|
||||
class="binary-field-checkbox"
|
||||
th:data-fieldname="${field.getName()}"
|
||||
th:id="|__keep_${field.getName()}|"
|
||||
checked
|
||||
th:name="|__keep_${field.getName()}|">
|
||||
<span>Keep current data</span>
|
||||
<div th:if="${field.isImage()}" class="mb-2">
|
||||
<img class="thumb-image" th:id="|__thumb_${field.getName()}|"
|
||||
th:src="|/dbadmin/download/${schema.getClassName()}/${field.getJavaName()}/${object.getPrimaryKeyValue()}/image|">
|
||||
</div>
|
||||
</div>
|
||||
<!--/*--> File input <!--*/-->
|
||||
<input th:if="${field.isBinary()}" placeholder="NULL" th:type="${field.getType().getHTMLName()}"
|
||||
th:name="${field.getName()}"
|
||||
class="form-control mt-2" th:id="|__id_${field.getName()}|"
|
||||
th:required="${!field.isNullable()}"
|
||||
oninvalid="this.setCustomValidity('This field is not nullable.')"
|
||||
oninput="this.setCustomValidity('')">
|
||||
</th:block>
|
||||
</th:block>
|
||||
<div class="separator mt-3 mb-2 separator-light"></div>
|
||||
</div>
|
||||
|
||||
<div th:each="field : ${schema.getManyToManyOwnedFields()}" class="mt-3">
|
||||
|
@@ -12,7 +12,7 @@
|
||||
<h1 class="fw-bold mb-4"><i class="align-middle bi bi-database"></i>
|
||||
<a class="align-middle" href="/dbadmin">Entities</a>
|
||||
<i class="align-middle bi bi-chevron-double-right"></i>
|
||||
<a class="align-middle "th:href="|/dbadmin/model/${schema.getJavaClass().getName()}|">
|
||||
<a class="align-middle" th:href="|/dbadmin/model/${schema.getJavaClass().getName()}|">
|
||||
[[ ${schema.getJavaClass().getSimpleName()} ]]</a>
|
||||
<i class="align-middle bi bi-chevron-double-right"></i>
|
||||
<span class="align-middle"> [[ ${object.getDisplayName()} ]]</span>
|
||||
@@ -44,7 +44,7 @@
|
||||
<i title="Foreign Key" class="bi bi-link"></i>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<td class="fw-bold">
|
||||
<span class="m-0 p-0" th:text="${field.getName()}"></span>
|
||||
</td>
|
||||
<td>
|
||||
|
Reference in New Issue
Block a user