Public Access
Visitor sign in kiosk: multi-site, badge printing, WWCC expiry warnings, admin accounts with 2FA
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
<!doctype html>
|
||||
<html lang="en-AU">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Admin — visitor sign in</title>
|
||||
<link rel="stylesheet" href="/css/admin.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ------------------------------------------------------------ login -->
|
||||
<div id="login" class="login" hidden>
|
||||
<div class="login-card">
|
||||
<h1 id="login-heading">Visitor admin</h1>
|
||||
|
||||
<form class="login-step" id="step-password">
|
||||
<label>
|
||||
<span>Email address</span>
|
||||
<input type="email" id="login-email" autocomplete="username" autocapitalize="none">
|
||||
</label>
|
||||
<label>
|
||||
<span>Password</span>
|
||||
<input type="password" id="login-password" autocomplete="current-password">
|
||||
</label>
|
||||
<p class="hint" id="domain-rule" hidden></p>
|
||||
<button type="submit">Continue</button>
|
||||
</form>
|
||||
|
||||
<form class="login-step" id="step-2fa" hidden>
|
||||
<p class="hint" id="twofa-intro"></p>
|
||||
<div id="twofa-setup" hidden>
|
||||
<img id="twofa-qr" alt="Two factor setup QR code" width="200" height="200">
|
||||
<p class="hint">Can't scan it? Enter this key by hand:<br><code id="twofa-secret"></code></p>
|
||||
</div>
|
||||
<label>
|
||||
<span id="twofa-label">6 digit code</span>
|
||||
<input id="twofa-code" inputmode="numeric" autocomplete="one-time-code" maxlength="12">
|
||||
</label>
|
||||
<button type="submit">Verify</button>
|
||||
<button type="button" class="link-quiet" id="twofa-cancel">Start again</button>
|
||||
</form>
|
||||
|
||||
<div class="login-step" id="step-recovery" hidden>
|
||||
<p class="hint">Save these recovery codes somewhere safe. Each one works once, if you ever
|
||||
lose the phone with your authenticator app on it.</p>
|
||||
<ul class="recovery" id="recovery-list"></ul>
|
||||
<button type="button" id="recovery-done">I've saved them</button>
|
||||
</div>
|
||||
|
||||
<form class="login-step" id="step-newpassword" hidden>
|
||||
<p class="hint">Set a password only you know before you continue.</p>
|
||||
<label>
|
||||
<span>Current password</span>
|
||||
<input type="password" id="pw-current" autocomplete="current-password">
|
||||
</label>
|
||||
<label>
|
||||
<span>New password</span>
|
||||
<input type="password" id="pw-new" autocomplete="new-password">
|
||||
</label>
|
||||
<label>
|
||||
<span>New password again</span>
|
||||
<input type="password" id="pw-again" autocomplete="new-password">
|
||||
</label>
|
||||
<button type="submit">Save and continue</button>
|
||||
</form>
|
||||
|
||||
<div class="login-step" id="step-setup" hidden>
|
||||
<p class="hint" id="setup-message"></p>
|
||||
</div>
|
||||
|
||||
<p class="err" id="login-error" hidden></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ---------------------------------------------------------- console -->
|
||||
<div id="console" hidden>
|
||||
<header class="topbar">
|
||||
<strong id="site-name">Visitor admin</strong>
|
||||
<nav>
|
||||
<button class="tab on" data-tab="onsite">On site</button>
|
||||
<button class="tab" data-tab="log">Visit log</button>
|
||||
<button class="tab" data-tab="recurring">Recurring visitors <span class="badge-count" id="alert-count" hidden></span></button>
|
||||
<button class="tab" data-tab="hosts">People to visit</button>
|
||||
<button class="tab" data-tab="sites">Sites</button>
|
||||
<button class="tab owner-only" data-tab="admins" hidden>Admins</button>
|
||||
<button class="tab" data-tab="system">System</button>
|
||||
</nav>
|
||||
<label class="site-switch" id="site-switch" hidden>
|
||||
<span>Site</span>
|
||||
<select id="site-filter"></select>
|
||||
</label>
|
||||
<button id="logout" class="link">Sign out</button>
|
||||
</header>
|
||||
|
||||
<p class="banner" id="expiry-banner" hidden></p>
|
||||
|
||||
<main>
|
||||
<!-- ------------------------------------------------------ on site -->
|
||||
<section class="panel on" id="panel-onsite">
|
||||
<div class="panel-head">
|
||||
<h2>Currently on site</h2>
|
||||
<button class="ghost" id="refresh-onsite">Refresh</button>
|
||||
</div>
|
||||
<p class="stat" id="onsite-count">—</p>
|
||||
<div id="onsite-table"></div>
|
||||
</section>
|
||||
|
||||
<!-- -------------------------------------------------------- log -->
|
||||
<section class="panel" id="panel-log">
|
||||
<div class="panel-head">
|
||||
<h2>Visit log</h2>
|
||||
<a class="ghost" id="csv-link" href="/admin/api/visits.csv">Download CSV</a>
|
||||
</div>
|
||||
<div class="filters">
|
||||
<label><span>From</span><input type="date" id="log-from"></label>
|
||||
<label><span>To</span><input type="date" id="log-to"></label>
|
||||
<label class="grow"><span>Search name, host or contact</span><input id="log-q"></label>
|
||||
<button class="ghost" id="log-search">Apply</button>
|
||||
</div>
|
||||
<div id="log-table"></div>
|
||||
</section>
|
||||
|
||||
<!-- ------------------------------------------------- recurring -->
|
||||
<section class="panel" id="panel-recurring">
|
||||
<div class="panel-head">
|
||||
<h2>Recurring visitors</h2>
|
||||
<button class="primary" id="new-recurring">Add a recurring visitor</button>
|
||||
</div>
|
||||
<div id="expiry-summary"></div>
|
||||
<div id="recurring-table"></div>
|
||||
</section>
|
||||
|
||||
<!-- ----------------------------------------------------- hosts -->
|
||||
<section class="panel" id="panel-hosts">
|
||||
<div class="panel-head">
|
||||
<h2>People a visitor can ask for</h2>
|
||||
<button class="primary" id="new-host">Add a person</button>
|
||||
</div>
|
||||
<p class="hint" id="hosts-scope"></p>
|
||||
<details class="import">
|
||||
<summary>Import from CSV</summary>
|
||||
<p class="hint">
|
||||
Paste the file contents below, or choose a .csv file. Recognised column headings are
|
||||
<code>name</code>, <code>email</code> and <code>area</code> (or department / team).
|
||||
A single column of names works too. The import applies to the site selected above.
|
||||
</p>
|
||||
<input type="file" id="host-file" accept=".csv,text/csv">
|
||||
<textarea id="host-csv" rows="6" placeholder="name,email,area Jess Rogerson,jess@example.com,Front office"></textarea>
|
||||
<label class="inline"><input type="checkbox" id="host-replace"> Deactivate anyone at this site who is not in the file</label>
|
||||
<button class="primary" id="do-host-import">Import</button>
|
||||
</details>
|
||||
<div id="hosts-table"></div>
|
||||
</section>
|
||||
|
||||
<!-- ----------------------------------------------------- sites -->
|
||||
<section class="panel" id="panel-sites">
|
||||
<div class="panel-head">
|
||||
<h2>Sites</h2>
|
||||
<button class="primary owner-only" id="new-site" hidden>Add a site</button>
|
||||
</div>
|
||||
<p class="hint">Each site has its own name, its own list of people to visit, and its own
|
||||
badge settings. Point a kiosk at one with
|
||||
<code>/?site=<em>slug</em></code>, or let staff pick on first use.</p>
|
||||
<div id="sites-list"></div>
|
||||
</section>
|
||||
|
||||
<!-- ---------------------------------------------------- admins -->
|
||||
<section class="panel" id="panel-admins">
|
||||
<div class="panel-head">
|
||||
<h2>Admin accounts</h2>
|
||||
<button class="primary" id="new-admin">Invite an admin</button>
|
||||
</div>
|
||||
<p class="hint" id="admins-note"></p>
|
||||
<div id="admins-table"></div>
|
||||
</section>
|
||||
|
||||
<!-- ---------------------------------------------------- system -->
|
||||
<section class="panel" id="panel-system">
|
||||
<h2>System</h2>
|
||||
<div id="system-body"></div>
|
||||
<h2 class="section-gap">Your account</h2>
|
||||
<div id="account-body"></div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<dialog id="modal">
|
||||
<form method="dialog" id="modal-form">
|
||||
<h3 id="modal-title"></h3>
|
||||
<div id="modal-body"></div>
|
||||
<div class="modal-actions">
|
||||
<button value="cancel" class="ghost" id="modal-cancel">Cancel</button>
|
||||
<button value="save" class="primary" id="modal-save">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<p class="toast" id="toast" role="status" hidden></p>
|
||||
|
||||
<footer class="foot">Created by: Jess Rogerson (yelling commands at Claude.AI)</footer>
|
||||
|
||||
<script src="/js/admin.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user