249 lines
9.4 KiB
HTML
249 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Dashboard - NOTXCS</title>
|
|
<link rel="stylesheet" href="/css/style.css">
|
|
</head>
|
|
<body>
|
|
<div class="app-shell">
|
|
<aside class="sidebar">
|
|
<h2>NOTXCS</h2>
|
|
<div class="user-info">Signed in as <strong id="username-display"></strong> <span id="role-badge" class="badge type-badge"></span></div>
|
|
<a id="admin-link" class="hidden" href="/admin" style="display:block;margin-bottom:12px;color:var(--accent);font-size:13px;">Manage users</a>
|
|
<button class="secondary" id="logout-btn" style="margin-bottom: 16px;">Log out</button>
|
|
|
|
<ul class="place-list" id="place-list"></ul>
|
|
|
|
<div class="card" style="margin-top: 16px;">
|
|
<h3 style="margin-top:0;font-size:14px;">Add place</h3>
|
|
<form id="add-place-form">
|
|
<label for="place-name">Name <span style="color:var(--muted);font-weight:normal;">(optional)</span></label>
|
|
<input type="text" id="place-name">
|
|
<div id="add-place-custom-fields" class="hidden">
|
|
<label for="place-id">Place ID <span style="color:var(--muted);font-weight:normal;">(leave blank to auto-generate)</span></label>
|
|
<input type="text" id="place-id">
|
|
<label for="place-api-key">API Key <span style="color:var(--muted);font-weight:normal;">(leave blank to auto-generate)</span></label>
|
|
<input type="text" id="place-api-key">
|
|
</div>
|
|
<button type="submit" class="primary">Add place</button>
|
|
</form>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main">
|
|
<div id="no-place" class="empty-state">
|
|
Select or create a place from the sidebar to manage its readers.
|
|
</div>
|
|
|
|
<div id="place-view" class="hidden">
|
|
<div class="main-header">
|
|
<h1 id="place-title"></h1>
|
|
<button class="danger" id="delete-place-btn">Delete place</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Name</h3>
|
|
<form id="set-place-name-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="place-name-custom">Place name <span style="color:var(--muted);font-weight:normal;">(leave blank to show the place ID instead)</span></label>
|
|
<input type="text" id="place-name-custom">
|
|
</div>
|
|
<button type="submit" class="primary">Save</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>API Key</h3>
|
|
<div class="inline-form">
|
|
<div class="field-group">
|
|
<label for="place-id-display">Place ID</label>
|
|
<input type="text" id="place-id-display" readonly>
|
|
</div>
|
|
<div class="field-group">
|
|
<label for="place-api-key-display">Current key</label>
|
|
<input type="password" id="place-api-key-display" readonly>
|
|
</div>
|
|
<button type="button" class="secondary" id="toggle-api-key-btn">Show</button>
|
|
<button type="button" class="secondary" id="copy-api-key-btn">Copy</button>
|
|
<button type="button" class="secondary" id="regenerate-api-key-btn">Regenerate</button>
|
|
</div>
|
|
<form id="set-api-key-form" class="inline-form hidden" style="margin-top:12px;">
|
|
<div class="field-group">
|
|
<label for="place-api-key-custom">Set custom key <span style="color:var(--muted);font-weight:normal;">(leave blank to auto-generate)</span></label>
|
|
<input type="text" id="place-api-key-custom">
|
|
</div>
|
|
<button type="submit" class="primary">Save</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Add reader</h3>
|
|
<form id="add-reader-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="reader-id">Reader ID</label>
|
|
<input type="text" id="reader-id" required>
|
|
</div>
|
|
<div class="field-group">
|
|
<label for="reader-name">Name</label>
|
|
<input type="text" id="reader-name" required>
|
|
</div>
|
|
<button type="submit" class="primary">Add reader</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Readers</h3>
|
|
<div id="readers-empty" class="empty-state hidden">No readers yet for this place.</div>
|
|
<div class="readers-grid" id="readers-grid"></div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Add access group</h3>
|
|
<p style="margin-top:-8px;color:var(--muted);font-size:13px;">Access groups (e.g. "Staff") bundle multiple users/cards/rank rules so they can be assigned to multiple readers at once.</p>
|
|
<form id="add-access-group-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="access-group-name">Name</label>
|
|
<input type="text" id="access-group-name" required>
|
|
</div>
|
|
<button type="submit" class="primary">Add group</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Access groups</h3>
|
|
<div id="access-groups-empty" class="empty-state hidden">No access groups yet for this place.</div>
|
|
<ul class="acl-list" id="access-groups-list"></ul>
|
|
</div>
|
|
|
|
<div class="card" id="shared-access-card">
|
|
<h3>Shared access</h3>
|
|
<p style="margin-top:-8px;color:var(--muted);font-size:13px;">Grant other users access to manage this place's settings, readers and access groups.</p>
|
|
<form id="add-shared-access-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="shared-access-username">Username</label>
|
|
<input type="text" id="shared-access-username" required>
|
|
</div>
|
|
<button type="submit" class="primary">Grant access</button>
|
|
</form>
|
|
<div id="shared-access-empty" class="empty-state hidden">No other users have been granted access to this place.</div>
|
|
<ul class="acl-list" id="shared-access-list"></ul>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<div id="acl-modal" class="modal-overlay hidden">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h2>Allowed persons & credentials — <span id="acl-reader-name"></span></h2>
|
|
<button class="secondary" id="acl-close-btn">Close</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Add entry</h3>
|
|
<form id="add-acl-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="acl-type">Type</label>
|
|
<select id="acl-type">
|
|
<option value="0">User ID</option>
|
|
<option value="1">Card number</option>
|
|
<option value="2">Group exact rank</option>
|
|
<option value="3">Group minimum rank</option>
|
|
<option value="4">Allow all</option>
|
|
<option value="5">Access group</option>
|
|
</select>
|
|
</div>
|
|
<div class="field-group" id="acl-data-group">
|
|
<label for="acl-data">User ID</label>
|
|
<input type="text" id="acl-data">
|
|
</div>
|
|
<div class="field-group hidden" id="acl-group-id-group">
|
|
<label for="acl-group-id">Group ID</label>
|
|
<input type="text" id="acl-group-id">
|
|
</div>
|
|
<div class="field-group hidden" id="acl-group-rank-group">
|
|
<label for="acl-group-rank">Rank</label>
|
|
<input type="number" id="acl-group-rank" min="0">
|
|
</div>
|
|
<div class="field-group hidden" id="acl-access-group-group">
|
|
<label for="acl-access-group">Access group</label>
|
|
<select id="acl-access-group"></select>
|
|
</div>
|
|
<button type="submit" class="primary">Add entry</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Entries</h3>
|
|
<div id="acl-empty" class="empty-state hidden">No entries yet. Access will be denied for everyone until an entry is added.</div>
|
|
<ul class="acl-list" id="acl-list"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="logs-modal" class="modal-overlay hidden">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h2>Scan logs — <span id="logs-reader-name"></span></h2>
|
|
<button class="secondary" id="logs-close-btn">Close</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Recent scans</h3>
|
|
<div id="logs-empty" class="empty-state hidden">No scans have been recorded for this reader yet.</div>
|
|
<ul class="acl-list" id="logs-list"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="group-modal" class="modal-overlay hidden">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h2>Access group members — <span id="group-name"></span></h2>
|
|
<button class="secondary" id="group-close-btn">Close</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Add member</h3>
|
|
<form id="add-group-member-form" class="inline-form">
|
|
<div class="field-group">
|
|
<label for="group-member-type">Type</label>
|
|
<select id="group-member-type">
|
|
<option value="0">User ID</option>
|
|
<option value="1">Card number</option>
|
|
<option value="2">Group exact rank</option>
|
|
<option value="3">Group minimum rank</option>
|
|
<option value="4">Allow all</option>
|
|
</select>
|
|
</div>
|
|
<div class="field-group" id="group-member-data-group">
|
|
<label for="group-member-data">User ID</label>
|
|
<input type="text" id="group-member-data">
|
|
</div>
|
|
<div class="field-group hidden" id="group-member-group-id-group">
|
|
<label for="group-member-group-id">Group ID</label>
|
|
<input type="text" id="group-member-group-id">
|
|
</div>
|
|
<div class="field-group hidden" id="group-member-group-rank-group">
|
|
<label for="group-member-group-rank">Rank</label>
|
|
<input type="number" id="group-member-group-rank" min="0">
|
|
</div>
|
|
<button type="submit" class="primary">Add member</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>Members</h3>
|
|
<div id="group-members-empty" class="empty-state hidden">No members yet. This group grants no access until members are added.</div>
|
|
<ul class="acl-list" id="group-members-list"></ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/js/dashboard.js"></script>
|
|
</body>
|
|
</html>
|
|
|