Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 15e7eb5bd1 | |||
| e082671f53 | |||
| ef28805042 |
@@ -1,4 +1,8 @@
|
||||
// mcias.js — HTMX event wiring for the MCIAS web UI.
|
||||
// mcias.js — HTMX event wiring and CSP-safe UI helpers for the MCIAS web UI.
|
||||
//
|
||||
// All interactive behavior that would otherwise require inline onclick/onchange
|
||||
// handlers is wired here via data-* attributes so that script-src 'self'
|
||||
// (without 'unsafe-inline') is sufficient.
|
||||
|
||||
// Show server error responses in the global #htmx-error-banner.
|
||||
//
|
||||
@@ -24,3 +28,60 @@ document.body.addEventListener('htmx:afterSwap', function () {
|
||||
banner.innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
// --- Toggle-form buttons ---
|
||||
//
|
||||
// Usage: <button data-toggle-form="create-form"
|
||||
// data-label-show="Add Item" data-label-hide="Cancel">
|
||||
function toggleForm(btn) {
|
||||
var id = btn.getAttribute('data-toggle-form');
|
||||
var el = document.getElementById(id);
|
||||
if (!el) { return; }
|
||||
var show = el.hidden || el.style.display === 'none';
|
||||
el.hidden = false;
|
||||
el.style.display = show ? '' : 'none';
|
||||
var labelShow = btn.getAttribute('data-label-show') || 'Create';
|
||||
var labelHide = btn.getAttribute('data-label-hide') || 'Cancel';
|
||||
btn.textContent = show ? labelHide : labelShow;
|
||||
}
|
||||
|
||||
// --- Clickable rows ---
|
||||
//
|
||||
// Usage: <tr class="clickable-row" data-href="/audit/123">
|
||||
function handleClickableRow(row) {
|
||||
var href = row.getAttribute('data-href');
|
||||
if (href) { window.location = href; }
|
||||
}
|
||||
|
||||
// --- Policy form tab switching ---
|
||||
//
|
||||
// Usage: <button data-tab="form"> / <button data-tab="json">
|
||||
function showTab(tab) {
|
||||
var formMode = document.getElementById('pf-form-mode');
|
||||
var jsonMode = document.getElementById('pf-json-mode');
|
||||
var formBtn = document.getElementById('tab-form');
|
||||
var jsonBtn = document.getElementById('tab-json');
|
||||
if (!formMode || !jsonMode) { return; }
|
||||
formMode.style.display = tab === 'form' ? '' : 'none';
|
||||
jsonMode.style.display = tab === 'json' ? '' : 'none';
|
||||
if (formBtn) { formBtn.className = tab === 'form' ? 'btn btn-sm btn-secondary' : 'btn btn-sm'; }
|
||||
if (jsonBtn) { jsonBtn.className = tab === 'json' ? 'btn btn-sm btn-secondary' : 'btn btn-sm'; }
|
||||
}
|
||||
|
||||
// --- Auto-wire on DOMContentLoaded ---
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Toggle-form buttons.
|
||||
document.querySelectorAll('[data-toggle-form]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () { toggleForm(btn); });
|
||||
});
|
||||
|
||||
// Clickable table rows.
|
||||
document.querySelectorAll('[data-href]').forEach(function (row) {
|
||||
row.addEventListener('click', function () { handleClickableRow(row); });
|
||||
});
|
||||
|
||||
// Tab buttons.
|
||||
document.querySelectorAll('[data-tab]').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () { showTab(btn.getAttribute('data-tab')); });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,3 +34,8 @@ th { background: #e9ecef; }
|
||||
input, select { padding: 0.4rem; border: 1px solid #ced4da; border-radius: 4px; }
|
||||
.clickable-row { cursor: pointer; }
|
||||
.clickable-row:hover { background: #e9ecef; }
|
||||
.flex-wrap { flex-wrap: wrap; }
|
||||
.flex-1 { flex: 1; min-width: 200px; }
|
||||
.flex-2 { flex: 2; min-width: 300px; }
|
||||
.text-center { text-align: center; }
|
||||
.p-2 { padding: 2rem; }
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
<p class="text-muted text-small">Manage user and service accounts</p>
|
||||
</div>
|
||||
<button class="btn btn-primary"
|
||||
onclick="var f=document.getElementById('create-form');f.style.display=f.style.display==='none'?'block':'none'">
|
||||
data-toggle-form="create-form"
|
||||
data-label-show="+ New Account"
|
||||
data-label-hide="Cancel">
|
||||
+ New Account
|
||||
</button>
|
||||
</div>
|
||||
<div id="create-form" class="card mt-2" style="display:none">
|
||||
<div id="create-form" class="card mt-2" hidden>
|
||||
<h2 style="font-size:1rem;font-weight:600;margin-bottom:1rem">Create Account</h2>
|
||||
<form hx-post="/accounts" hx-target="#accounts-tbody" hx-swap="afterbegin">
|
||||
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">
|
||||
@@ -36,7 +38,9 @@
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary" type="submit">Create</button>
|
||||
<button class="btn btn-secondary" type="button"
|
||||
onclick="document.getElementById('create-form').style.display='none'">Cancel</button>
|
||||
data-toggle-form="create-form"
|
||||
data-label-show="Cancel"
|
||||
data-label-hide="Cancel">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{define "audit_rows"}}
|
||||
{{range .Events}}
|
||||
<tr class="clickable-row" onclick="window.location='/audit/{{.ID}}'">
|
||||
<tr class="clickable-row" data-href="/audit/{{.ID}}">
|
||||
<td class="text-small text-muted"><a href="/audit/{{.ID}}">{{formatTime .EventTime}}</a></td>
|
||||
<td><code style="font-size:.8rem">{{.EventType}}</code></td>
|
||||
<td class="text-small text-muted">{{if .ActorUsername}}{{.ActorUsername}}{{else}}—{{end}}</td>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{{define "policy_form"}}
|
||||
<div style="margin-bottom:.75rem;border-bottom:1px solid var(--border-color);padding-bottom:.5rem;display:flex;gap:.5rem">
|
||||
<button type="button" id="tab-form" class="btn btn-sm btn-secondary"
|
||||
onclick="showTab('form')" style="font-size:.8rem">Form</button>
|
||||
data-tab="form">Form</button>
|
||||
<button type="button" id="tab-json" class="btn btn-sm"
|
||||
onclick="showTab('json')" style="font-size:.8rem;opacity:.6">JSON</button>
|
||||
data-tab="json">JSON</button>
|
||||
</div>
|
||||
<form hx-post="/policies" hx-target="#policies-tbody" hx-swap="afterbegin">
|
||||
<div id="pf-form-mode">
|
||||
@@ -121,20 +121,4 @@
|
||||
|
||||
<button class="btn btn-sm btn-secondary" type="submit">Create Rule</button>
|
||||
</form>
|
||||
<script>
|
||||
(function() {
|
||||
var active = 'form';
|
||||
window.showTab = function(tab) {
|
||||
active = tab;
|
||||
document.getElementById('pf-form-mode').style.display = tab === 'form' ? '' : 'none';
|
||||
document.getElementById('pf-json-mode').style.display = tab === 'json' ? '' : 'none';
|
||||
document.getElementById('tab-form').style.opacity = tab === 'form' ? '1' : '.6';
|
||||
document.getElementById('tab-json').style.opacity = tab === 'json' ? '1' : '.6';
|
||||
var formBtn = document.getElementById('tab-form');
|
||||
var jsonBtn = document.getElementById('tab-json');
|
||||
formBtn.className = tab === 'form' ? 'btn btn-sm btn-secondary' : 'btn btn-sm';
|
||||
jsonBtn.className = tab === 'json' ? 'btn btn-sm btn-secondary' : 'btn btn-sm';
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
@@ -82,7 +82,9 @@
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.5rem">
|
||||
<h2 style="font-size:1rem;font-weight:600;margin:0">New Credentials</h2>
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
onclick="var f=document.getElementById('pgcreds-create-form');f.hidden=!f.hidden;this.textContent=f.hidden?'Add Credentials':'Cancel'">Add Credentials</button>
|
||||
data-toggle-form="pgcreds-create-form"
|
||||
data-label-show="Add Credentials"
|
||||
data-label-hide="Cancel">Add Credentials</button>
|
||||
</div>
|
||||
<div id="pgcreds-create-form" hidden>
|
||||
<p class="text-muted text-small" style="margin-bottom:1rem;margin-top:.5rem">
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
<h1>Policy Rules</h1>
|
||||
<p class="text-muted text-small">{{len .Rules}} operator rules (built-in defaults not shown)</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="document.getElementById('create-form').style.display='block';this.style.display='none'">
|
||||
<button class="btn btn-primary"
|
||||
data-toggle-form="create-form"
|
||||
data-label-show="Add Rule"
|
||||
data-label-hide="Cancel">
|
||||
Add Rule
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="create-form" class="card mt-2" style="display:none">
|
||||
<div id="create-form" class="card mt-2" hidden>
|
||||
<h2 style="font-size:1rem;font-weight:600;margin-bottom:1rem">Create Policy Rule</h2>
|
||||
{{template "policy_form" .}}
|
||||
</div>
|
||||
|
||||
@@ -6,22 +6,25 @@
|
||||
<h2>SSO Clients</h2>
|
||||
<p class="text-muted text-small">Registered applications that use MCIAS for single sign-on.</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="var f=document.getElementById('create-form');f.hidden=!f.hidden;this.textContent=f.hidden?'Add Client':'Cancel'">Add Client</button>
|
||||
<button class="btn btn-primary"
|
||||
data-toggle-form="create-form"
|
||||
data-label-show="Add Client"
|
||||
data-label-hide="Cancel">Add Client</button>
|
||||
</div>
|
||||
|
||||
<div id="create-form" class="card mt-2" hidden>
|
||||
<div class="card-title">Register SSO Client</div>
|
||||
<form hx-post="/sso-clients" hx-target="#sso-clients-tbody" hx-swap="afterbegin">
|
||||
<div class="d-flex gap-1" style="flex-wrap:wrap">
|
||||
<div class="form-group" style="flex:1;min-width:200px">
|
||||
<div class="d-flex gap-1 flex-wrap">
|
||||
<div class="form-group flex-1">
|
||||
<label class="form-label">Client ID / Service Name</label>
|
||||
<input class="form-control" type="text" name="client_id" required placeholder="e.g. mcr">
|
||||
</div>
|
||||
<div class="form-group" style="flex:2;min-width:300px">
|
||||
<div class="form-group flex-2">
|
||||
<label class="form-label">Redirect URI</label>
|
||||
<input class="form-control" type="url" name="redirect_uri" required placeholder="https://service.example.com/sso/callback">
|
||||
</div>
|
||||
<div class="form-group" style="flex:1;min-width:200px">
|
||||
<div class="form-group flex-1">
|
||||
<label class="form-label">Tags <span class="text-muted text-small">(comma-separated)</span></label>
|
||||
<input class="form-control" type="text" name="tags" placeholder="env:prod,tier:web">
|
||||
</div>
|
||||
@@ -49,6 +52,6 @@
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{if not .Clients}}<p class="text-muted" style="text-align:center;padding:2rem">No SSO clients registered.</p>{{end}}
|
||||
{{if eq (len .Clients) 0}}<p class="text-muted text-center p-2">No SSO clients registered.</p>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user