adssad
This commit is contained in:
@@ -13,6 +13,14 @@
|
|||||||
const imageInput = form.querySelector('input[name="image_url"]');
|
const imageInput = form.querySelector('input[name="image_url"]');
|
||||||
const submitBtn = form.querySelector('[data-host-submit]');
|
const submitBtn = form.querySelector('[data-host-submit]');
|
||||||
const cancelBtn = form.querySelector('[data-host-cancel]');
|
const cancelBtn = form.querySelector('[data-host-cancel]');
|
||||||
|
const modal = document.querySelector('[data-host-modal]');
|
||||||
|
const modalTitle = document.querySelector('[data-host-modal-title]');
|
||||||
|
const closeBtn = document.querySelector('[data-host-close]');
|
||||||
|
const newBtn = document.querySelector('[data-host-new]');
|
||||||
|
const unsavedBar = document.querySelector('[data-host-unsaved]');
|
||||||
|
const discardBtn = document.querySelector('[data-host-discard]');
|
||||||
|
|
||||||
|
let initialSnapshot = '';
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
if (idInput) idInput.value = '';
|
if (idInput) idInput.value = '';
|
||||||
@@ -25,6 +33,41 @@
|
|||||||
if (passInput) passInput.value = '';
|
if (passInput) passInput.value = '';
|
||||||
if (imageInput) imageInput.value = '';
|
if (imageInput) imageInput.value = '';
|
||||||
if (submitBtn) submitBtn.textContent = 'Speichern';
|
if (submitBtn) submitBtn.textContent = 'Speichern';
|
||||||
|
if (unsavedBar) unsavedBar.style.display = 'none';
|
||||||
|
};
|
||||||
|
|
||||||
|
const snapshot = () => {
|
||||||
|
return JSON.stringify({
|
||||||
|
id: idInput ? idInput.value : '',
|
||||||
|
name: nameInput ? nameInput.value : '',
|
||||||
|
host: hostInput ? hostInput.value : '',
|
||||||
|
port: portInput ? portInput.value : '',
|
||||||
|
user: userInput ? userInput.value : '',
|
||||||
|
auth: authSelect ? authSelect.value : '',
|
||||||
|
key: keyInput ? keyInput.value : '',
|
||||||
|
pass: passInput ? passInput.value : '',
|
||||||
|
image: imageInput ? imageInput.value : '',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDirty = () => snapshot() !== initialSnapshot;
|
||||||
|
|
||||||
|
const openModal = () => {
|
||||||
|
if (!modal) return;
|
||||||
|
modal.classList.add('is-open');
|
||||||
|
modal.setAttribute('aria-hidden', 'false');
|
||||||
|
initialSnapshot = snapshot();
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = (force = false) => {
|
||||||
|
if (!modal) return;
|
||||||
|
if (!force && isDirty()) {
|
||||||
|
if (unsavedBar) unsavedBar.style.display = 'flex';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
modal.classList.remove('is-open');
|
||||||
|
modal.setAttribute('aria-hidden', 'true');
|
||||||
|
if (unsavedBar) unsavedBar.style.display = 'none';
|
||||||
};
|
};
|
||||||
|
|
||||||
document.querySelectorAll('[data-host-edit]').forEach((btn) => {
|
document.querySelectorAll('[data-host-edit]').forEach((btn) => {
|
||||||
@@ -41,9 +84,10 @@
|
|||||||
if (passInput) passInput.value = '';
|
if (passInput) passInput.value = '';
|
||||||
if (imageInput) imageInput.value = card.dataset.imageUrl || '';
|
if (imageInput) imageInput.value = card.dataset.imageUrl || '';
|
||||||
if (submitBtn) submitBtn.textContent = 'Aktualisieren';
|
if (submitBtn) submitBtn.textContent = 'Aktualisieren';
|
||||||
|
if (modalTitle) modalTitle.textContent = 'Host bearbeiten';
|
||||||
const details = btn.closest('details');
|
const details = btn.closest('details');
|
||||||
if (details) details.removeAttribute('open');
|
if (details) details.removeAttribute('open');
|
||||||
form.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
openModal();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,6 +98,31 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newBtn) {
|
||||||
|
newBtn.addEventListener('click', () => {
|
||||||
|
resetForm();
|
||||||
|
if (modalTitle) modalTitle.textContent = 'Neuer Host';
|
||||||
|
openModal();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeBtn) {
|
||||||
|
closeBtn.addEventListener('click', () => closeModal(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discardBtn) {
|
||||||
|
discardBtn.addEventListener('click', () => {
|
||||||
|
resetForm();
|
||||||
|
closeModal(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener('input', () => {
|
||||||
|
if (unsavedBar && isDirty()) {
|
||||||
|
unsavedBar.style.display = 'flex';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const updateStatus = (card, status) => {
|
const updateStatus = (card, status) => {
|
||||||
const dot = card.querySelector('[data-host-status]');
|
const dot = card.querySelector('[data-host-status]');
|
||||||
if (!dot) return;
|
if (!dot) return;
|
||||||
|
|||||||
@@ -155,6 +155,18 @@
|
|||||||
}
|
}
|
||||||
.action-menu-panel form { margin: 0; }
|
.action-menu-panel form { margin: 0; }
|
||||||
|
|
||||||
|
.host-unsaved {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid #f3c3b8;
|
||||||
|
background: #fff5f3;
|
||||||
|
color: #7a2114;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.command-list {
|
.command-list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|||||||
@@ -177,7 +177,10 @@ function hostAuthOk(array $host, bool $strictHostKey): bool
|
|||||||
?>
|
?>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="pill">Pi Control</div>
|
<div class="pill">Pi Control</div>
|
||||||
<h1 style="margin-top:.75rem;">Hosts</h1>
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; margin-top:.75rem;">
|
||||||
|
<h1 style="margin:0;">Hosts</h1>
|
||||||
|
<button class="cta-button" type="button" data-host-new>+ Neuer Host</button>
|
||||||
|
</div>
|
||||||
<p class="muted">Verwalte die Raspberry Pis, die du steuern möchtest.</p>
|
<p class="muted">Verwalte die Raspberry Pis, die du steuern möchtest.</p>
|
||||||
|
|
||||||
<?php if ($error): ?>
|
<?php if ($error): ?>
|
||||||
@@ -191,52 +194,6 @@ function hostAuthOk(array $host, bool $strictHostKey): bool
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="grid" style="margin-top:1rem;">
|
<div class="grid" style="margin-top:1rem;">
|
||||||
<div class="card form-card" style="background:var(--panel-2);">
|
|
||||||
<strong>Neuer Host</strong>
|
|
||||||
<form method="post" class="form-grid" style="margin-top:.75rem;" data-host-form>
|
|
||||||
<input type="hidden" name="id" value="">
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Name</span>
|
|
||||||
<input type="text" name="name" placeholder="z.B. Wohnzimmer-Pi" required title="Eindeutiger Anzeigename für diesen Pi.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Host / IP</span>
|
|
||||||
<input type="text" name="host" placeholder="z.B. 192.168.178.14 oder pi.local" required title="Hostname oder IP im Heimnetzwerk.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">SSH Port</span>
|
|
||||||
<input type="number" name="port" placeholder="22" value="22" title="Standard ist 22.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">SSH Benutzer</span>
|
|
||||||
<input type="text" name="username" placeholder="z.B. pi" required title="Benutzername auf dem Pi.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Auth-Typ</span>
|
|
||||||
<select name="auth_type" title="key = SSH-Key, password = Passwort">
|
|
||||||
<option value="key">key (SSH-Key)</option>
|
|
||||||
<option value="pass">pass (Passwort)</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Key-Pfad (optional)</span>
|
|
||||||
<input type="text" name="key_path" placeholder="/home/app/.ssh/id_rsa" title="Pfad zum Private-Key im Webserver-Container.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Passwort (optional)</span>
|
|
||||||
<input type="password" name="password" placeholder="SSH-Passwort" title="Nur nutzen, wenn Auth-Typ = pass.">
|
|
||||||
</label>
|
|
||||||
<label class="form-field">
|
|
||||||
<span class="muted">Bild-URL (optional)</span>
|
|
||||||
<input type="text" name="image_url" placeholder="https://..." title="Optionales Bild für die Host-Karte.">
|
|
||||||
</label>
|
|
||||||
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
|
||||||
<button class="cta-button" type="submit" data-host-submit>Speichern</button>
|
|
||||||
<button class="nav-link" type="button" data-host-cancel>Zurücksetzen</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card form-card" style="background:var(--panel-2);">
|
<div class="card form-card" style="background:var(--panel-2);">
|
||||||
<strong>Registrierte Hosts</strong>
|
<strong>Registrierte Hosts</strong>
|
||||||
<?php if (!$hosts): ?>
|
<?php if (!$hosts): ?>
|
||||||
@@ -283,3 +240,61 @@ function hostAuthOk(array $host, bool $strictHostKey): bool
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal" data-host-modal aria-hidden="true">
|
||||||
|
<div class="modal-card">
|
||||||
|
<div class="modal-header">
|
||||||
|
<strong data-host-modal-title>Neuer Host</strong>
|
||||||
|
<button class="icon-button" type="button" data-host-close>×</button>
|
||||||
|
</div>
|
||||||
|
<form method="post" class="form-grid" style="margin-top:.75rem;" data-host-form>
|
||||||
|
<input type="hidden" name="id" value="">
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Name</span>
|
||||||
|
<input type="text" name="name" placeholder="z.B. Wohnzimmer-Pi" required title="Eindeutiger Anzeigename für diesen Pi.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Host / IP</span>
|
||||||
|
<input type="text" name="host" placeholder="z.B. 192.168.178.14 oder pi.local" required title="Hostname oder IP im Heimnetzwerk.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">SSH Port</span>
|
||||||
|
<input type="number" name="port" placeholder="22" value="22" title="Standard ist 22.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">SSH Benutzer</span>
|
||||||
|
<input type="text" name="username" placeholder="z.B. pi" required title="Benutzername auf dem Pi.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Auth-Typ</span>
|
||||||
|
<select name="auth_type" title="key = SSH-Key, password = Passwort">
|
||||||
|
<option value="key">key (SSH-Key)</option>
|
||||||
|
<option value="pass">pass (Passwort)</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Key-Pfad (optional)</span>
|
||||||
|
<input type="text" name="key_path" placeholder="/home/app/.ssh/id_rsa" title="Pfad zum Private-Key im Webserver-Container.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Passwort (optional)</span>
|
||||||
|
<input type="password" name="password" placeholder="SSH-Passwort" title="Nur nutzen, wenn Auth-Typ = pass.">
|
||||||
|
</label>
|
||||||
|
<label class="form-field">
|
||||||
|
<span class="muted">Bild-URL (optional)</span>
|
||||||
|
<input type="text" name="image_url" placeholder="https://..." title="Optionales Bild für die Host-Karte.">
|
||||||
|
</label>
|
||||||
|
<div class="host-unsaved" data-host-unsaved style="display:none;">
|
||||||
|
<span class="muted">Änderungen nicht gespeichert.</span>
|
||||||
|
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
||||||
|
<button class="cta-button" type="submit" data-host-submit>Speichern</button>
|
||||||
|
<button class="nav-link" type="button" data-host-discard>Änderungen verwerfen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style="display:flex; gap:10px; flex-wrap:wrap;">
|
||||||
|
<button class="cta-button" type="submit" data-host-submit>Speichern</button>
|
||||||
|
<button class="nav-link" type="button" data-host-cancel>Zurücksetzen</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user