fix: move inline JavaScript to external file to comply with CSP
This commit is contained in:
parent
406ff2f9c2
commit
a0ca704ac1
4 changed files with 97 additions and 64 deletions
79
static/app.js
Normal file
79
static/app.js
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Open allocation modal
|
||||
document.querySelectorAll('.allocate-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
openAllocateModal(btn);
|
||||
});
|
||||
});
|
||||
|
||||
// Open release modal from history table
|
||||
document.querySelectorAll('.release-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
const id = btn.dataset.id;
|
||||
const modal = document.getElementById('release-modal-' + id);
|
||||
if (modal) {
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close modal via Cancel button
|
||||
document.querySelectorAll('.cancel-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
const modalId = btn.dataset.modal;
|
||||
const modal = document.getElementById(modalId);
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Close modal by clicking on the overlay background
|
||||
document.querySelectorAll('.modal-overlay').forEach(function (overlay) {
|
||||
overlay.addEventListener('click', function (event) {
|
||||
if (event.target === overlay) {
|
||||
overlay.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Prevent clicks inside the modal from closing it
|
||||
document.querySelectorAll('.modal').forEach(function (modal) {
|
||||
modal.addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function openAllocateModal(btn) {
|
||||
const slug = btn.dataset.slug;
|
||||
const name = btn.dataset.name;
|
||||
const select = document.getElementById('prefix-' + slug);
|
||||
const prefixLength = select ? select.value : '';
|
||||
|
||||
document.getElementById('modal-pool-slug').value = slug;
|
||||
document.getElementById('modal-prefix-length').value = prefixLength;
|
||||
document.getElementById('modal-pool-name').textContent = name;
|
||||
document.getElementById('modal-prefix-display').textContent = '/' + prefixLength;
|
||||
|
||||
try {
|
||||
const resp = await fetch('/partials/pools/' + slug + '/preview?prefix_length=' + prefixLength);
|
||||
const html = await resp.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
const cidr = doc.getElementById('preview-cidr')?.textContent || '–';
|
||||
const addressCount = doc.getElementById('preview-address-count')?.textContent || '–';
|
||||
const usableHosts = doc.getElementById('preview-usable-hosts')?.textContent || '–';
|
||||
const warning = doc.getElementById('preview-warning')?.textContent || '';
|
||||
|
||||
document.getElementById('modal-cidr').textContent = cidr;
|
||||
document.getElementById('modal-address-count').textContent = addressCount;
|
||||
document.getElementById('modal-usable-hosts').textContent = usableHosts;
|
||||
document.getElementById('modal-warning').textContent = warning;
|
||||
} catch (e) {
|
||||
document.getElementById('modal-warning').textContent = 'Could not load preview.';
|
||||
}
|
||||
|
||||
document.getElementById('allocate-modal').style.display = 'flex';
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@
|
|||
</div>
|
||||
|
||||
{% if allocation.status == "active" || allocation.status == "reserved" %}
|
||||
<button class="btn btn-danger" onclick="document.getElementById('release-modal').style.display='flex'">Release subnet</button>
|
||||
<button class="btn btn-danger release-btn" data-id="{{ allocation.id }}">Release subnet</button>
|
||||
{% endif %}
|
||||
|
||||
<a href="/" class="btn btn-secondary">Back</a>
|
||||
|
|
@ -108,8 +108,8 @@
|
|||
</section>
|
||||
{% endif %}
|
||||
|
||||
<div id="release-modal" class="modal-overlay" style="display:none;" onclick="if (event.target === this) this.style.display='none';">
|
||||
<div class="modal" onclick="event.stopPropagation();">
|
||||
<div id="release-modal-{{ allocation.id }}" class="modal-overlay" style="display:none;">
|
||||
<div class="modal">
|
||||
<h2 class="modal-title">Release subnet {{ allocation.cidr }}</h2>
|
||||
<form method="post" action="/allocations/{{ allocation.id }}/release" class="modal-body" id="release-form">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
|
|
@ -117,10 +117,12 @@
|
|||
<label for="release-note">Release note *</label>
|
||||
<textarea id="release-note" name="release_note" rows="3" required minlength="3" maxlength="500"></textarea>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('release-modal').style.display='none'">Cancel</button>
|
||||
<button type="button" class="btn btn-secondary cancel-btn" data-modal="release-modal-{{ allocation.id }}">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Release</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/static/app.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<td data-label="Actions">
|
||||
<a href="/allocations/{{ a.id }}">Detail</a>
|
||||
{% if a.status == "active" || a.status == "reserved" %}
|
||||
| <button type="button" class="btn-link" onclick="document.getElementById('release-modal-{{ a.id }}').style.display='flex'">Release</button>
|
||||
| <button type="button" class="btn-link release-btn" data-id="{{ a.id }}">Release</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -38,8 +38,8 @@
|
|||
|
||||
{% for a in allocations %}
|
||||
{% if a.status == "active" || a.status == "reserved" %}
|
||||
<div id="release-modal-{{ a.id }}" class="modal-overlay" style="display:none;" onclick="if (event.target === this) this.style.display='none';">
|
||||
<div class="modal" onclick="event.stopPropagation();">
|
||||
<div id="release-modal-{{ a.id }}" class="modal-overlay" style="display:none;">
|
||||
<div class="modal">
|
||||
<h2 class="modal-title">Release subnet {{ a.cidr }}</h2>
|
||||
<form method="post" action="/allocations/{{ a.id }}/release" class="modal-body" id="release-form-{{ a.id }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
<label for="release-note-{{ a.id }}">Release note *</label>
|
||||
<textarea id="release-note-{{ a.id }}" name="release_note" rows="3" required minlength="3" maxlength="500"></textarea>
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('release-modal-{{ a.id }}').style.display='none'">Cancel</button>
|
||||
<button type="button" class="btn btn-secondary cancel-btn" data-modal="release-modal-{{ a.id }}">Cancel</button>
|
||||
<button type="submit" class="btn btn-danger">Release</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
hx-indicator="#indicator-{{ pool.slug }}"
|
||||
>
|
||||
<label for="prefix-{{ pool.slug }}">Subnet size</label>
|
||||
<select name="prefix_length" id="prefix-{{ pool.slug }}" onchange="updateModalPrefix('{{ pool.slug }}', this.value)">
|
||||
<select name="prefix_length" id="prefix-{{ pool.slug }}">
|
||||
{% for pl in pool.allowed_prefixes %}
|
||||
<option value="{{ pl }}" {% if pl == "24" %}selected{% endif %}>/{{ pl }}</option>
|
||||
{% endfor %}
|
||||
|
|
@ -37,9 +37,10 @@
|
|||
</form>
|
||||
|
||||
<button
|
||||
class="btn"
|
||||
class="btn allocate-btn"
|
||||
style="margin-top: 1rem;"
|
||||
onclick="openAllocateModal('{{ pool.slug }}')"
|
||||
data-slug="{{ pool.slug }}"
|
||||
data-name="{{ pool.name }}"
|
||||
>
|
||||
[ Allocate next free subnet ]
|
||||
</button>
|
||||
|
|
@ -85,8 +86,8 @@
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<div id="allocate-modal" class="modal-overlay" style="display:none;" onclick="if (event.target === this) closeAllocateModal();">
|
||||
<div class="modal" onclick="event.stopPropagation();">
|
||||
<div id="allocate-modal" class="modal-overlay" style="display:none;">
|
||||
<div class="modal">
|
||||
<h2 class="modal-title">Confirm subnet allocation</h2>
|
||||
<form method="post" action="/allocations" class="modal-body" id="allocate-form">
|
||||
<input type="hidden" name="pool_slug" id="modal-pool-slug">
|
||||
|
|
@ -125,61 +126,12 @@
|
|||
<input type="text" id="ticket" name="ticket" maxlength="100">
|
||||
|
||||
<div class="modal-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeAllocateModal()">Cancel</button>
|
||||
<button type="button" class="btn btn-secondary cancel-btn" data-modal="allocate-modal">Cancel</button>
|
||||
<button type="submit" class="btn">Confirm allocation</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const poolData = {
|
||||
{% for pool in pools %}
|
||||
"{{ pool.slug }}": {
|
||||
name: "{{ pool.name }}",
|
||||
allowed_prefixes: {{ pool.allowed_prefixes_json|safe }}
|
||||
},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
function updateModalPrefix(slug, prefixLength) {
|
||||
// preview is updated via HTMX automatically
|
||||
}
|
||||
|
||||
async function openAllocateModal(slug) {
|
||||
const select = document.getElementById('prefix-' + slug);
|
||||
const prefixLength = select.value;
|
||||
const modal = document.getElementById('allocate-modal');
|
||||
|
||||
document.getElementById('modal-pool-slug').value = slug;
|
||||
document.getElementById('modal-prefix-length').value = prefixLength;
|
||||
document.getElementById('modal-pool-name').textContent = poolData[slug].name;
|
||||
document.getElementById('modal-prefix-display').textContent = '/' + prefixLength;
|
||||
|
||||
try {
|
||||
const resp = await fetch('/partials/pools/' + slug + '/preview?prefix_length=' + prefixLength);
|
||||
const html = await resp.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
|
||||
const cidr = doc.getElementById('preview-cidr')?.textContent || '–';
|
||||
const addressCount = doc.getElementById('preview-address-count')?.textContent || '–';
|
||||
const usableHosts = doc.getElementById('preview-usable-hosts')?.textContent || '–';
|
||||
const warning = doc.getElementById('preview-warning')?.textContent || '';
|
||||
|
||||
document.getElementById('modal-cidr').textContent = cidr;
|
||||
document.getElementById('modal-address-count').textContent = addressCount;
|
||||
document.getElementById('modal-usable-hosts').textContent = usableHosts;
|
||||
document.getElementById('modal-warning').textContent = warning;
|
||||
} catch (e) {
|
||||
document.getElementById('modal-warning').textContent = 'Could not load preview.';
|
||||
}
|
||||
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeAllocateModal() {
|
||||
document.getElementById('allocate-modal').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
<script src="/static/app.js"></script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue