0.0.1 Alpha version

This commit is contained in:
Francesco
2023-09-18 09:25:25 +02:00
commit 348408a3e1
45 changed files with 4339 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
form.delete-form {
display: inline-block;
}
form.delete-form button {
background: transparent;
border: none;
}
.dbfieldtype {
font-weight: normal;
font-family: monospace;
font-size: 0.9rem;
}
a .bi, button .bi {
color: #007fd0;
}
a {
color: #007fd0;
}
.disable {
pointer-events: none;
background: #EDECEF;
}
.null-label {
background-color: #EEE;
border-radius: 5px;
border: 1px solid #DDD;
display: inline-block;
padding: 3px;
color: #333;
padding-left: 6px;
padding-right: 6px;
}
ul.pagination {
padding-bottom: 0;
margin-bottom: 0;
}
tr.table-data-row td, tr.table-data-row th {
border-right: 1px solid #DDD;
}
tr.table-data-row td:last-child, tr.table-data-row th:last-child {
border-right: 0px;
}
.row-icons {
font-size: 1.2rem;
width: 128px;
}
h1 .bi {
font-size: 2rem;
}
h1 a {
color: #222;
text-decoration: none;
}
h1 a:hover {
color: #007fd0;
}
.inner-navigation {
border-top-right-radius: 5px;
border-top-left-radius: 5px;
background-color: #FAFAFA;
}
.inner-navigation a:first-child {
border-top-left-radius: 5px;
background-color: #FAFAFA;
}
.inner-navigation-border {
border-bottom: 4px solid #F0F0F0;
}
.inner-navigation a {
text-decoration: none;
font-weight: bold;
border-bottom: 4px solid #F0F0F0;
}
.inner-navigation a.active {
border-bottom: 4px solid #007fd0 !important;
}
.inner-navigation a:hover {
background-color: #FFF;
border-bottom: 4px solid #ADDEFF;
}
.suggestions {
position: absolute;
width: 100%;
background-color: white;
border: 1px solid #DADADA;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
top: 100%;
max-height: 300px;
overflow: auto;
z-index: 999;
}
.suggestion {
padding: 1rem;
background-color: #FAFAFA;
padding-bottom: 0;
margin: 0;
border-bottom: 2px solid #FAFAFA;
}
.suggestion:hover {
cursor: pointer;
background-color: #FFF;
border-bottom: 2px solid #ADDEFF;
}
td.table-checkbox, th.table-checkbox {
width: 36px;
}
/**
AUTOCOMPLETE
**/
.badge-checkbox {
display: none;
}
.value-badge {
cursor: pointer; -webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.value-badge .badge {
font-size: 1rem;
padding: 8px;
margin-top: 5px;
}
.clear-all-badge {
padding: 0.4rem;
}

View File

@@ -0,0 +1,372 @@
body {
font-family: "Poppins", "Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
font-weight: 300;
}
h1, h2, h3, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
font-family: "Roboto", "Segoe UI", sans-serif;
}
.bg-lighter {
background-color: #F2F2F2;
}
#map { height: 100%; }
.bg-lightest {
background-color: #F8F8F8;
}
.bg-red {
background-color: red;
}
.bg-white {
background-color: white;
}
.box {
border-radius: 5px;
width: 100%;
background-color: #FAFAFA;
padding: 25px;
-webkit-box-shadow: 10px 10px 84px -44px rgba(0,0,0,0.27);
-moz-box-shadow: 10px 10px 84px -44px rgba(0,0,0,0.27);
box-shadow: 10px 10px 84px -44px rgba(0,0,0,0.27);
}
.box.with-navigation {
border-radius: 5px;
border-top-right-radius: 0px;
border-top-left-radius: 0px;
width: 100%;
background-color: #FAFAFA;
padding: 25px;
-webkit-box-shadow: 10px 10px 84px -44px rgba(0,0,0,0.27);
-moz-box-shadow: 10px 10px 84px -74px rgba(0,0,0,0.27);
box-shadow: 10px 10px 84px -74px rgba(0,0,0,0.27);
}
.box.with-telegram-embed {
padding: 10px;
padding-left: 5px;
}
.box.with-footer-button {
position: relative;
padding-bottom: 70px;
}
.box.box-h-300 {
height: 300px;
overflow: auto;
}
.navbar-brand .bi-activity {
color: green;
}
.navbar {
opacity: 0.90;
}
.navbar-brand .bi-hexagon-fill {
color: #3fb95f;
}
.bg-complementary {
background-color: #3fb95f;
}
div.main-wrapper {
height: 100vh;
overflow: auto;
}
div.sidebar {
width: 200px;
min-height: 100vh;
padding: 56px 0px 25px 0px;
-webkit-box-shadow: 10px 2px 67px -30px rgba(0,0,0,0.25);
-moz-box-shadow: 10px 2px 67px -30px rgba(0,0,0,0.25);
box-shadow: 10px 2px 67px -30px rgba(0,0,0,0.25);
}
.sidebar-top {
width: 200px;
position: fixed;
}
div.main-content {
padding: 50px;
width: calc(100% - 200px);
padding-top: 80px;
}
div.main-content.full-screen {
padding: 0px;
padding-top: 56px;
}
ul.sidebar-menu {
list-style-type: none;
padding-left: 0px;
}
ul.sidebar-menu li {
background-color: #F8F8F8;
border-left: 5px solid #F2F2F2;
}
ul.sidebar-menu li.active {
border-left: 5px solid #0092ee;
}
ul.sidebar-menu li:hover {
border-left: 5px solid #7fc8f6;
background-color: white;
}
ul.sidebar-menu li a {
text-decoration: none;
color: #444;
}
ul.sidebar-menu .menu-icon {
padding: 8px;
font-size: 1.6rem;
}
.navbar.fixed-top {
-webkit-box-shadow: 0px 5px 67px -30px rgba(0,0,0,1);
-moz-box-shadow: 0px 5px 67px -30px rgba(0,0,0,1);
box-shadow: 0px 5px 67px -30px rgba(0,0,0,1);
}
em {
background-color: #f1f4d0;
}
.label {
font-weight: bold;
color: #444
}
.label a {
text-decoration: none;
color: #0293c9;
}
.label-gray {
background-color: #EEE;
border-radius: 5px;
border: 1px solid #DDD;
padding: 3px 6px 0px 6px;
display: inline-block;
}
.message-text {
color: #333;
}
.form-date {
border: 1px solid #CCC;
}
.form-select {
border: 1px solid #CCC;
}
.separator {
height: 2px;
border-bottom: 1px solid #CCC;
}
.menu-subheading {
text-transform: uppercase;
color: #444;
font-size: 0.8rem;
}
.btn-outline-left {
border-top-left-radius: 0px;
border-bottom-left-radius: 0px;
border-left: 0px;
}
.channel-about {
font-size: 0.9rem;
color: #555;
text-align: left;
}
p.fine {
font-size: 0.9rem;
color: #444;
}
div.sidebar-bottom {
position: fixed;
bottom: 0px;
width: 200px;
}
.fs-smaller {
font-size: 0.8rem;
}
.fs-bigger {
text-align: center;
color: #0092ee;
font-weight: bold;
font-size: 2.3rem;
}
.fs-big {
font-size: 1.1rem;
}
.form-control:focus {
box-shadow: none !important;
}
.btn-check:focus+.btn-outline-primary, .btn-outline-primary:focus {
box-shadow: none !important;
}
.color-telegram {
color: #0092ee;
}
.bg-accent {
background-color: #007fd0;
}
.bg-accent a.navbar-brand {
color: #EEE;
}
.bg-accent a.navbar-brand:hover {
color: white;
}
/**
* Utility height classes
*/
.h-200 {
height: 200px;
}
.h-300 {
height: 300px;
}
.h-400 {
height: 400px;
}
.h-500 {
height: 500px;
}
.h-600 {
height: 600px;
}
.h-700 {
height: 700px;
}
.h-800 {
height: 800px;
}
.h-1000 {
height: 1000px;
}
.h-fill {
height: calc(100vh - 56px);
}
.overflow-auto {
overflow: auto;
}
.color-white {
color:white !important;
}
.ui-text-input {
border: 0px;
border-bottom: 2px solid #52b5f3;
background-color: white;
border-radius: 0px;
}
span.input-group-text {
background-color: #F0F0F0;
border:0px;
border-bottom: 2px solid #0768a5;
border-radius: 0px;
}
.ui-btn {
border-radius: 0px;
font-weight: bold;
}
.btn-primary {
background-color: #21a0f0;
border: 1px solid #21a0f0;
}
.w-10 {
width: 10%;
}
.w-5 {
width: 5%;
}
@media (max-width: 767px) {
div.sidebar {
width: 53px;
}
.sidebar-top {
width: 53px;
position: fixed;
}
div.main-content {
width: calc(100% - 53px);
padding: 15px;
padding-top: 70px;
}
div.box {
padding: 15px;
overflow: auto;
}
div.sidebar-bottom {
width: 53px;
}
.extra-toolbar {
left: 53px;
}
.extra-toolbar ul {
padding-left: 0px;
}
}

View File

@@ -0,0 +1,123 @@
/* Request to the autocomplete REST endpoit */
async function getSuggestions(className, query) {
const response = await fetch(`/dbadmin/api/autocomplete/${className}?query=${query}`);
const suggestions = await response.json();
return suggestions;
}
function hideSuggestions(inputElement) {
let suggestionsDiv = inputElement.parentElement.querySelector("div.suggestions");
suggestionsDiv.classList.remove('d-block');
suggestionsDiv.classList.add('d-none');
}
function showSuggestions(inputElement) {
let suggestionsDiv = inputElement.parentElement.querySelector("div.suggestions");
suggestionsDiv.classList.remove('d-none');
suggestionsDiv.classList.add('d-block');
}
document.addEventListener("DOMContentLoaded", () => {
let rootElements = document.querySelectorAll(".autocomplete-multi-input");
/* Instead of using onBlur, which takes precedence over onClick
/* and causes the click event to disappear, we detect click
/* on outside elements and close all the autocomplete manually */
document.querySelector("body").addEventListener('click', function(e) {
if (!e.target.classList.contains("suggestion") && !e.target.classList.contains("autocomplete")) {
rootElements.forEach(root => {
hideSuggestions(root.querySelector("input.autocomplete"));
});
}
});
rootElements.forEach(root => {
/* Event listener to delete badge on click
*/
root.querySelectorAll(".selected-values .value-badge").forEach(badge => {
badge.addEventListener('click', function() {
badge.remove();
});
});
root.querySelector(".clear-all-badge").addEventListener('click', function(e) {
e.target.classList.add('d-none');
e.target.classList.remove('d-inline-block');
root.querySelectorAll(".selected-values .value-badge").forEach(badge => {
badge.remove();
});
});
let input = root.querySelector("input.autocomplete");
if (input == undefined) return;
input.addEventListener('focus', function() {
showSuggestions(input);
});
let fieldName = input.dataset.fieldname;
input.parentElement.querySelector("div.suggestions").innerHTML =
`<div class="suggestion p-2 m-0">Start typing for suggestions</div>`;
input.addEventListener('keyup', async function(e) {
let suggestions = await getSuggestions(e.target.dataset.classname, e.target.value);
input.parentElement.querySelector("div.suggestions").innerHTML = "";
if (e.target.value.length <= 1) {
input.parentElement.querySelector("div.suggestions").innerHTML =
`<div class="suggestion p-2 m-0">Start typing for suggestions</div>`;
return;
}
suggestions.forEach(suggestion => {
let suggestionDiv = document.createElement('div');
suggestionDiv.innerHTML =
`<div class="suggestion p-2 m-0">
<strong>${suggestion.id}</strong>
<p class="p-0 m-0">${suggestion.value}</p>
</div>`;
input.parentElement.querySelector("div.suggestions").appendChild(suggestionDiv);
suggestionDiv.addEventListener('click', function(e) {
hideSuggestions(input);
input.value = '';
// Check if we need to add the 'Clear all' button back
root.querySelector(".clear-all-badge").classList.add('d-inline-block');
root.querySelector(".clear-all-badge").classList.remove('d-none');
root.querySelector(".selected-values")
.innerHTML += `
<span class="value-badge">
<input type="checkbox" class="badge-checkbox" checked="checked"
name="${fieldName}" value="${suggestion.id}">
<span class="badge bg-primary me-2">
${suggestion.value}
</span>
</span>`
root.querySelectorAll(".selected-values .value-badge").forEach(badge => {
badge.addEventListener('click', function() {
badge.remove();
});
});
});
});
if (suggestions.length == 0) {
let suggestionDiv = document.createElement('div');
suggestionDiv.innerHTML =
`<div class="suggestion p-2 m-0">
<p class="p-0 m-0">No results</p>
</div>`;
input.parentElement.querySelector("div.suggestions").appendChild(suggestionDiv);
}
});
});
});

View File

@@ -0,0 +1,83 @@
/* Request to the autocomplete REST endpoit */
async function getSuggestions(className, query) {
const response = await fetch(`/dbadmin/api/autocomplete/${className}?query=${query}`);
const suggestions = await response.json();
return suggestions;
}
function hideSuggestions(inputElement) {
let suggestionsDiv = inputElement.parentElement.querySelector("div.suggestions");
suggestionsDiv.classList.remove('d-block');
suggestionsDiv.classList.add('d-none');
}
function showSuggestions(inputElement) {
let suggestionsDiv = inputElement.parentElement.querySelector("div.suggestions");
suggestionsDiv.classList.remove('d-none');
suggestionsDiv.classList.add('d-block');
}
document.addEventListener("DOMContentLoaded", () => {
/* Instead of using onBlur, which takes precedence over onClick
/* and causes the click event to disappear, we detect click
/* on outside elements and close all the autocomplete manually */
document.querySelector("body").addEventListener('click', function(e) {
if (!e.target.classList.contains("suggestion") && !e.target.classList.contains("autocomplete")) {
rootElements.forEach(root => {
hideSuggestions(root.querySelector("input.autocomplete"));
});
}
});
let rootElements = document.querySelectorAll(".autocomplete-input");
rootElements.forEach(root => {
let input = root.querySelector("input.autocomplete");
if (input == undefined) return;
input.addEventListener('focus', function() {
showSuggestions(input);
});
input.parentElement.querySelector("div.suggestions").innerHTML =
`<div class="suggestion p-2 m-0">Enter a valid ID or start typing for suggestions</div>`;
input.addEventListener('keyup', async function(e) {
let suggestions = await getSuggestions(e.target.dataset.classname, e.target.value);
input.parentElement.querySelector("div.suggestions").innerHTML = "";
if (e.target.value.length <= 0) {
input.parentElement.querySelector("div.suggestions").innerHTML =
`<div class="suggestion p-2 m-0">Enter a valid ID or start typing for suggestions</div>`;
return;
}
suggestions.forEach(suggestion => {
let suggestionDiv = document.createElement('div');
suggestionDiv.innerHTML =
`<div class="suggestion p-2 m-0">
<strong>${suggestion.id}</strong>
<p class="p-0 m-0">${suggestion.value}</p>
</div>`;
input.parentElement.querySelector("div.suggestions").appendChild(suggestionDiv);
suggestionDiv.addEventListener('click', function(e) {
input.value = suggestion.id;
hideSuggestions(input);
});
});
if (suggestions.length == 0) {
let suggestionDiv = document.createElement('div');
suggestionDiv.innerHTML =
`<div class="suggestion p-2 m-0">
<p class="p-0 m-0">No results</p>
</div>`;
input.parentElement.querySelector("div.suggestions").appendChild(suggestionDiv);
}
});
});
});

View File

@@ -0,0 +1,66 @@
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">`;
});
}
document.addEventListener("DOMContentLoaded", () => {
let selected = 0;
if (document.getElementById('delete-form') != null) {
document.getElementById('delete-form').addEventListener('submit', function(e) {
if (selected == 0) {
e.preventDefault();
alert('No items selected');
return;
}
if (!confirm('Are you sure you want to delete these items?')) {
e.preventDefault();
}
});
}
document.querySelectorAll("div.table-selectable").forEach(table => {
let tableInputs = table.querySelectorAll("table input[type=\"checkbox\"]");
tableInputs.forEach(input => {
if (input.checked && !input.classList.contains('check-all')) selected++;
input.addEventListener('change', function(e) {
if (e.target.classList.contains('check-all')) {
if (e.target.checked) {
selected = tableInputs.length - 1;
tableInputs.forEach(input => {
input.checked = true;
});
} else {
selected = 0;
tableInputs.forEach(input => {
input.checked = false;
});
}
} else {
if (e.target.checked) {
selected++;
} else {
selected--;
}
}
updateBulkActions(table, selected);
});
});
updateBulkActions(table, selected);
});
if (document.querySelector("div.table-selectable select.page-size") != null) {
document.querySelector("div.table-selectable select.page-size").addEventListener('change', function(e) {
this.parentElement.querySelector("input[name=\"pageSize\"]").value = e.target.value;
this.parentElement.submit();
});
}
});