3c12ebba16
Native confirm() dialogs block browser automation entirely (discovered via a frozen tab while browser-testing the app.messages/app.modal adoption), and native alert()/confirm() are visually inconsistent with the rest of the UI. Replaced every call site with app.messages.action/confirm/toast: - directory.ejs: rotateSecret/deleteResource confirms and all inline save/add/remove-group/edge error alerts now target #resourceModal's actionMessage (or, for deleteResource — called from the outer table row, not the modal — the page's own card). - impersonate_modal.ejs, onboarding.ejs: no local .actionMessage target exists on these pages, so their alerts became page-wide toasts. - executive.ejs: two alerts in sendNotification's validation now use the existing $compose target; saveTos's alert now reuses the function's own msgEl inline-message element instead of introducing a second mechanism. - users.ejs, profile.ejs, proxy's profile.ejs: toggleActive's alert (no row context available at the call site) became a toast; revokeInvite/revokeToken/rotateToken use the row/card element already in scope. - app.js: removed app.user.remove and app.oauthClient.remove, which contained native confirm() guards and had zero callers anywhere in the app — dead code, deleted rather than converted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
188 lines
5.6 KiB
Plaintext
188 lines
5.6 KiB
Plaintext
<%- include('top') %>
|
|
|
|
<script type="text/javascript">
|
|
app.auth.forceLogin();
|
|
|
|
var currentUser;
|
|
|
|
async function loadOnboarding() {
|
|
currentUser = await app.auth.asyncUser;
|
|
|
|
var needs = currentUser.onboardingNeeds || [];
|
|
|
|
if (!needs.length) {
|
|
location.replace('/');
|
|
return;
|
|
}
|
|
|
|
if (needs.includes('tos')) {
|
|
document.getElementById('section-tos').style.display = '';
|
|
}
|
|
if (needs.includes('dob')) {
|
|
document.getElementById('section-dob').style.display = '';
|
|
}
|
|
if (needs.includes('password')) {
|
|
document.getElementById('section-password').style.display = '';
|
|
}
|
|
}
|
|
|
|
function checkAllDone() {
|
|
var sections = document.querySelectorAll('.onboarding-section');
|
|
var anyVisible = false;
|
|
sections.forEach(function(el) {
|
|
if (el.style.display !== 'none') anyVisible = true;
|
|
});
|
|
if (!anyVisible) location.replace('/');
|
|
}
|
|
|
|
async function acceptTos() {
|
|
var checkbox = document.getElementById('tosCheckbox');
|
|
if (!checkbox.checked) {
|
|
app.messages.toast('Please read and check the box to accept the Terms of Service.', 'danger');
|
|
return;
|
|
}
|
|
try {
|
|
var resp = await fetch('/api/user/accept-tos', {
|
|
method: 'POST',
|
|
headers: { 'auth-token': localStorage.getItem('APIToken') }
|
|
});
|
|
if (!resp.ok) throw new Error('Failed');
|
|
document.getElementById('section-tos').style.display = 'none';
|
|
checkAllDone();
|
|
} catch(e) {
|
|
app.messages.toast('Could not save TOS acceptance. Please try again.', 'danger');
|
|
}
|
|
}
|
|
|
|
async function saveDob() {
|
|
var dob = document.getElementById('dobInput').value;
|
|
if (!dob) {
|
|
app.messages.toast('Please enter your date of birth.', 'danger');
|
|
return;
|
|
}
|
|
try {
|
|
var resp = await fetch('/api/user/' + currentUser.uid, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'auth-token': localStorage.getItem('APIToken'),
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ dateOfBirth: dob })
|
|
});
|
|
if (!resp.ok) throw new Error('Failed');
|
|
document.getElementById('section-dob').style.display = 'none';
|
|
checkAllDone();
|
|
} catch(e) {
|
|
app.messages.toast('Could not save date of birth. Please try again.', 'danger');
|
|
}
|
|
}
|
|
|
|
async function changePassword() {
|
|
var pw = document.getElementById('pwInput').value;
|
|
var pw2 = document.getElementById('pwInput2').value;
|
|
if (!pw || pw.length < 5) {
|
|
app.messages.toast('Password must be at least 5 characters.', 'danger');
|
|
return;
|
|
}
|
|
if (pw !== pw2) {
|
|
app.messages.toast('Passwords do not match.', 'danger');
|
|
return;
|
|
}
|
|
try {
|
|
var resp = await fetch('/api/user/password', {
|
|
method: 'PUT',
|
|
headers: {
|
|
'auth-token': localStorage.getItem('APIToken'),
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ userPassword: pw, password: pw2 })
|
|
});
|
|
if (!resp.ok) throw new Error('Failed');
|
|
document.getElementById('section-password').style.display = 'none';
|
|
checkAllDone();
|
|
} catch(e) {
|
|
app.messages.toast('Could not change password. Please try again.', 'danger');
|
|
}
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
loadOnboarding();
|
|
});
|
|
</script>
|
|
|
|
<div class="row justify-content-center" style="display:none">
|
|
<div class="col-md-8">
|
|
|
|
<div class="card shadow-lg mb-4 mt-2">
|
|
<div class="card-header shadow">
|
|
<i class="fa-solid fa-circle-user"></i> Account Setup
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted mb-0">Please complete the steps below before continuing.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- TOS -->
|
|
<div id="section-tos" class="onboarding-section card shadow-lg mb-4" style="display:none">
|
|
<div class="card-header shadow">
|
|
<i class="fa-solid fa-file-contract"></i> Terms of Service
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="border rounded p-3 mb-3" style="max-height:300px;overflow-y:auto">
|
|
<%- tosHtml %>
|
|
</div>
|
|
<div class="form-check mb-3">
|
|
<input class="form-check-input" type="checkbox" id="tosCheckbox">
|
|
<label class="form-check-label" for="tosCheckbox">
|
|
I have read and agree to the Terms of Service
|
|
</label>
|
|
</div>
|
|
<button class="btn btn-primary shadow" onclick="acceptTos()">
|
|
<i class="fa-solid fa-check"></i> Accept
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DOB -->
|
|
<div id="section-dob" class="onboarding-section card shadow-lg mb-4" style="display:none">
|
|
<div class="card-header shadow">
|
|
<i class="fa-solid fa-calendar"></i> Date of Birth
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">Your date of birth is required for account verification.</p>
|
|
<div class="mb-3">
|
|
<label class="form-label">Date of Birth</label>
|
|
<input type="date" class="form-control shadow" id="dobInput" />
|
|
</div>
|
|
<button class="btn btn-primary shadow" onclick="saveDob()">
|
|
<i class="fa-solid fa-check"></i> Save
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Password -->
|
|
<div id="section-password" class="onboarding-section card shadow-lg mb-4" style="display:none">
|
|
<div class="card-header shadow">
|
|
<i class="fa-solid fa-key"></i> Change Password
|
|
</div>
|
|
<div class="card-body">
|
|
<p class="text-muted">You must set a new password before continuing.</p>
|
|
<div class="mb-3">
|
|
<label class="form-label">New Password</label>
|
|
<input type="password" class="form-control shadow" id="pwInput" placeholder="At least 5 characters" />
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Confirm Password</label>
|
|
<input type="password" class="form-control shadow" id="pwInput2" placeholder="Retype password" />
|
|
</div>
|
|
<button class="btn btn-primary shadow" onclick="changePassword()">
|
|
<i class="fa-solid fa-check"></i> Change Password
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<%- include('bottom') %>
|