Files
dre/frontend/verify.html
T
root 7a62b0b340 Backend Aug 22-25: AI analysis, welcome-packet templating, LetterStream, DocuSeal, staff RBAC + tier gate
- analysis.py: deterministic claim scorer + /analyze /approve /letter /advance-tier endpoints (auto-runs on intake)
- packet.py + packet_fields.json: welcome-packet templating engine (6 onboarding docs, field catalog)
- letterstream.py + letters.py: certified-mail send pipeline + letter lifecycle (webhook verified)
- docuseal.py: DocuSeal signing integration
- staff.py/models.py/schema.sql/auth.py: approval actor from staff key, tier gate (APPROVED+ACTIVE+onboarding docs), onboarding_docs table
- frontend/: dependency-free static portal (intake, magic-link login/verify, dashboard)
- landing-mockups/: 4 design-stance mockups + favicons
- legal/: aup/privacy/sms-terms/terms HTML
- docs/: letter-queue scope, letterstream API contract, 6 welcome-packet templates
- review-dre-landing-2026-08-21.md: 3-variant landing feedback sprint
- compliance/DRE_Compliance_Manual.md: updated

Source synced from deployed /opt/dre-portal/app/ (was 4 days ahead of git).
2026-08-26 02:26:33 -04:00

121 lines
4.0 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verifying Login - Debt Recovery Experts</title>
<link rel="stylesheet" href="css/dre.css">
</head>
<body>
<header class="dre-header">
<div class="dre-header-inner">
<a class="dre-brand" href="index.html">
<span class="dre-mark">DRE</span>
<span>
Debt Recovery Experts
<span class="dre-brand-sub">Client Portal</span>
</span>
</a>
</div>
</header>
<main class="dre-main dre-narrow">
<!-- ============================== CHECKING STATE ============================== -->
<div id="verify-checking" class="dre-card dre-state">
<div class="dre-spinner dre-spinner-dark" style="width:32px;height:32px;border-width:3px;margin:0 auto 1rem;"></div>
<h2>Verifying Your Login Link...</h2>
<p>Please wait a moment.</p>
</div>
<!-- ============================== ERROR STATE ============================== -->
<div id="verify-error" class="dre-card dre-state dre-hidden">
<div class="dre-state-icon err">&#10007;</div>
<h2>Login Link Invalid or Expired</h2>
<p id="verify-error-message">
This login link is invalid, has expired, or has already been used. Login links are valid
for 15 minutes and can only be used once.
</p>
<a href="login.html" class="dre-btn dre-btn-primary dre-mt">Request a New Login Link</a>
</div>
<!-- ============================== NO TOKEN STATE ============================== -->
<div id="verify-no-token" class="dre-card dre-state dre-hidden">
<div class="dre-state-icon err">&#10007;</div>
<h2>Missing Login Link</h2>
<p>No login token was found in this link. Please use the link from your email, or request a
new one below.</p>
<a href="login.html" class="dre-btn dre-btn-primary dre-mt">Go to Login</a>
</div>
<!-- ============================== SUCCESS STATE ============================== -->
<div id="verify-success" class="dre-card dre-state dre-hidden">
<div class="dre-state-icon ok">&#10003;</div>
<h2>Login Successful</h2>
<p>Redirecting you to your dashboard...</p>
</div>
</main>
<footer class="dre-footer">
Debt Recovery Experts Client Portal
</footer>
<script src="js/dre-api.js"></script>
<script>
(function () {
"use strict";
var checkingEl = document.getElementById("verify-checking");
var errorEl = document.getElementById("verify-error");
var noTokenEl = document.getElementById("verify-no-token");
var successEl = document.getElementById("verify-success");
function showOnly(el) {
[checkingEl, errorEl, noTokenEl, successEl].forEach(function (e) {
e.classList.add("dre-hidden");
});
el.classList.remove("dre-hidden");
}
var params = new URLSearchParams(window.location.search);
var token = params.get("token");
// Strip the token from the URL immediately so it does not linger in
// browser history / can't be re-shared accidentally, per spec.
if (token && window.history && window.history.replaceState) {
window.history.replaceState({}, document.title, window.location.pathname);
}
if (!token) {
showOnly(noTokenEl);
} else {
DRE.apiFetch("/api/auth/verify", { method: "POST", body: { token: token } }).then(function (result) {
if (result.ok && result.data && result.data.session_token) {
DRE.setSessionToken(result.data.session_token);
try {
if (result.data.client) {
localStorage.setItem("dre_client_summary", JSON.stringify(result.data.client));
}
} catch (e) {
/* ignore storage errors */
}
showOnly(successEl);
setTimeout(function () {
window.location.href = "dashboard.html";
}, 800);
return;
}
var message = DRE.getErrorMessage(result, "This login link is invalid, has expired, or has already been used.");
document.getElementById("verify-error-message").textContent = message;
showOnly(errorEl);
});
}
})();
</script>
</body>
</html>