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).
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
<!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">✗</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">✗</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">✓</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>
|
||||
Reference in New Issue
Block a user