console
This commit is contained in:
97
modules/pi_control/assets/commands.js
Normal file
97
modules/pi_control/assets/commands.js
Normal file
@@ -0,0 +1,97 @@
|
||||
(() => {
|
||||
const form = document.querySelector('[data-command-form]');
|
||||
const list = document.querySelector('[data-command-list]');
|
||||
if (!form) return;
|
||||
|
||||
const idInput = form.querySelector('input[name="id"]');
|
||||
const labelInput = form.querySelector('input[name="label"]');
|
||||
const commandInput = form.querySelector('textarea[name="command"]');
|
||||
const timeoutInput = form.querySelector('input[name="timeout_sec"]');
|
||||
const adminInput = form.querySelector('input[name="admin_only"]');
|
||||
const submitBtn = form.querySelector('[data-command-submit]');
|
||||
const cancelBtn = form.querySelector('[data-command-cancel]');
|
||||
|
||||
const resetForm = () => {
|
||||
if (idInput) idInput.value = '';
|
||||
if (labelInput) labelInput.value = '';
|
||||
if (commandInput) commandInput.value = '';
|
||||
if (timeoutInput) timeoutInput.value = '';
|
||||
if (adminInput) adminInput.checked = false;
|
||||
if (submitBtn) submitBtn.textContent = 'Speichern';
|
||||
};
|
||||
|
||||
document.querySelectorAll('[data-command-edit]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const item = btn.closest('.command-item');
|
||||
if (!item) return;
|
||||
if (idInput) idInput.value = item.dataset.commandId || '';
|
||||
if (labelInput) labelInput.value = item.dataset.label || '';
|
||||
if (commandInput) commandInput.value = item.dataset.command || '';
|
||||
if (timeoutInput) timeoutInput.value = item.dataset.timeout || '';
|
||||
if (adminInput) adminInput.checked = item.dataset.admin === '1';
|
||||
if (submitBtn) submitBtn.textContent = 'Aktualisieren';
|
||||
const details = btn.closest('details');
|
||||
if (details) details.removeAttribute('open');
|
||||
form.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
});
|
||||
});
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
resetForm();
|
||||
});
|
||||
}
|
||||
|
||||
if (!list) return;
|
||||
|
||||
let dragging = null;
|
||||
|
||||
list.querySelectorAll('.command-item').forEach((item) => {
|
||||
item.addEventListener('dragstart', () => {
|
||||
dragging = item;
|
||||
item.classList.add('is-dragging');
|
||||
});
|
||||
item.addEventListener('dragend', () => {
|
||||
item.classList.remove('is-dragging');
|
||||
dragging = null;
|
||||
saveOrder();
|
||||
});
|
||||
});
|
||||
|
||||
list.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
if (!dragging) return;
|
||||
const after = getDragAfterElement(list, e.clientY);
|
||||
if (after == null) {
|
||||
list.appendChild(dragging);
|
||||
} else if (after !== dragging) {
|
||||
list.insertBefore(dragging, after);
|
||||
}
|
||||
});
|
||||
|
||||
const getDragAfterElement = (container, y) => {
|
||||
const elements = [...container.querySelectorAll('.command-item:not(.is-dragging)')];
|
||||
return elements.reduce(
|
||||
(closest, child) => {
|
||||
const box = child.getBoundingClientRect();
|
||||
const offset = y - box.top - box.height / 2;
|
||||
if (offset < 0 && offset > closest.offset) {
|
||||
return { offset, element: child };
|
||||
}
|
||||
return closest;
|
||||
},
|
||||
{ offset: Number.NEGATIVE_INFINITY, element: null }
|
||||
).element;
|
||||
};
|
||||
|
||||
const saveOrder = () => {
|
||||
const order = [...list.querySelectorAll('.command-item')].map((el) => el.dataset.commandId);
|
||||
fetch(window.location.pathname + '?reorder_json=1', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ order }),
|
||||
cache: 'no-store',
|
||||
}).catch(() => {});
|
||||
};
|
||||
})();
|
||||
56
modules/pi_control/assets/hosts.js
Normal file
56
modules/pi_control/assets/hosts.js
Normal file
@@ -0,0 +1,56 @@
|
||||
(() => {
|
||||
const form = document.querySelector('[data-host-form]');
|
||||
if (!form) return;
|
||||
|
||||
const idInput = form.querySelector('input[name="id"]');
|
||||
const nameInput = form.querySelector('input[name="name"]');
|
||||
const hostInput = form.querySelector('input[name="host"]');
|
||||
const portInput = form.querySelector('input[name="port"]');
|
||||
const userInput = form.querySelector('input[name="username"]');
|
||||
const authSelect = form.querySelector('select[name="auth_type"]');
|
||||
const keyInput = form.querySelector('input[name="key_path"]');
|
||||
const passInput = form.querySelector('input[name="password"]');
|
||||
const imageInput = form.querySelector('input[name="image_url"]');
|
||||
const submitBtn = form.querySelector('[data-host-submit]');
|
||||
const cancelBtn = form.querySelector('[data-host-cancel]');
|
||||
|
||||
const resetForm = () => {
|
||||
if (idInput) idInput.value = '';
|
||||
if (nameInput) nameInput.value = '';
|
||||
if (hostInput) hostInput.value = '';
|
||||
if (portInput) portInput.value = '22';
|
||||
if (userInput) userInput.value = '';
|
||||
if (authSelect) authSelect.value = 'key';
|
||||
if (keyInput) keyInput.value = '';
|
||||
if (passInput) passInput.value = '';
|
||||
if (imageInput) imageInput.value = '';
|
||||
if (submitBtn) submitBtn.textContent = 'Speichern';
|
||||
};
|
||||
|
||||
document.querySelectorAll('[data-host-edit]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const card = btn.closest('.host-card');
|
||||
if (!card) return;
|
||||
if (idInput) idInput.value = card.dataset.hostId || '';
|
||||
if (nameInput) nameInput.value = card.dataset.name || '';
|
||||
if (hostInput) hostInput.value = card.dataset.host || '';
|
||||
if (portInput) portInput.value = card.dataset.port || '22';
|
||||
if (userInput) userInput.value = card.dataset.username || '';
|
||||
if (authSelect) authSelect.value = card.dataset.auth || 'key';
|
||||
if (keyInput) keyInput.value = card.dataset.keyPath || '';
|
||||
if (passInput) passInput.value = '';
|
||||
if (imageInput) imageInput.value = card.dataset.imageUrl || '';
|
||||
if (submitBtn) submitBtn.textContent = 'Aktualisieren';
|
||||
const details = btn.closest('details');
|
||||
if (details) details.removeAttribute('open');
|
||||
form.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
});
|
||||
});
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
resetForm();
|
||||
});
|
||||
}
|
||||
})();
|
||||
@@ -73,6 +73,119 @@
|
||||
background: #0b0f17;
|
||||
}
|
||||
|
||||
.host-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
.host-card {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: 120px 1fr;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.host-card-image {
|
||||
background: linear-gradient(135deg, #2b3a67 0%, #3b2f5c 45%, #1c2b3f 100%);
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
position: relative;
|
||||
}
|
||||
.host-card-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(10, 16, 28, 0.35);
|
||||
}
|
||||
.host-card-body {
|
||||
padding: 12px 14px 14px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
.host-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.host-card-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 999px;
|
||||
display: inline-block;
|
||||
}
|
||||
.status-ok { background: #31c48d; }
|
||||
.status-auth { background: #fbbf24; }
|
||||
.status-down { background: #ef4444; }
|
||||
|
||||
.action-menu {
|
||||
position: relative;
|
||||
}
|
||||
.action-menu summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
border-radius: 10px;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid var(--line);
|
||||
background: var(--panel-2);
|
||||
}
|
||||
.action-menu summary::-webkit-details-marker { display: none; }
|
||||
.action-menu[open] summary {
|
||||
background: var(--panel);
|
||||
}
|
||||
.action-menu-panel {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 6px);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
padding: 6px;
|
||||
min-width: 160px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
z-index: 5;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
.action-menu-panel form { margin: 0; }
|
||||
|
||||
.command-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.command-item {
|
||||
display: grid;
|
||||
grid-template-columns: 28px 1fr auto;
|
||||
gap: 10px;
|
||||
align-items: start;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
background: var(--panel);
|
||||
}
|
||||
.command-item.is-dragging {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.command-drag {
|
||||
cursor: grab;
|
||||
color: var(--muted);
|
||||
font-size: 1.1rem;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.command-body code {
|
||||
display: inline-block;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.queue-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user