Files
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

346 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Submit a Claim - Debt Recovery Experts</title>
<meta name="description" content="Submit a commercial claim for recovery review. Debt Recovery Experts helps businesses recover what they are owed through professional, compliant collection services.">
<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">Commercial Claim Recovery</span>
</span>
</a>
<div class="dre-header-actions">
<a href="login.html">Client Login</a>
</div>
</div>
</header>
<main class="dre-main dre-narrow">
<div class="dre-intro">
<h1>Submit a Claim</h1>
<p>Tell us about the outstanding balance you would like our team to review. Submission
takes about three minutes. There is no obligation, and a member of our team will follow
up after reviewing your claim.</p>
</div>
<div id="intake-alert" class="dre-alert dre-alert-error" role="alert"></div>
<!-- ============================== FORM STATE ============================== -->
<form id="intake-form" class="dre-card" novalidate>
<div class="dre-form-section">
<h3>Your Information</h3>
<div class="dre-row">
<div class="dre-field">
<label for="company_name">Company Name <span class="dre-required">*</span></label>
<input type="text" id="company_name" name="company_name" maxlength="200" required autocomplete="organization">
<span class="dre-field-error" data-error-for="company_name"></span>
</div>
<div class="dre-field">
<label for="contact_name">Your Full Name <span class="dre-required">*</span></label>
<input type="text" id="contact_name" name="contact_name" maxlength="200" required autocomplete="name">
<span class="dre-field-error" data-error-for="contact_name"></span>
</div>
</div>
<div class="dre-row">
<div class="dre-field">
<label for="email">Email Address <span class="dre-required">*</span></label>
<input type="email" id="email" name="email" maxlength="254" required autocomplete="email">
<span class="dre-hint">We will send your secure client-portal login link here.</span>
<span class="dre-field-error" data-error-for="email"></span>
</div>
<div class="dre-field">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone" maxlength="50" autocomplete="tel">
<span class="dre-field-error" data-error-for="phone"></span>
</div>
</div>
</div>
<div class="dre-form-section">
<h3>Debtor Information</h3>
<p class="dre-small" style="margin-bottom:1rem;">The business or individual that owes the balance.</p>
<div class="dre-field">
<label for="debtor_name">Debtor Name <span class="dre-required">*</span></label>
<input type="text" id="debtor_name" name="debtor_name" maxlength="200" required>
<span class="dre-field-error" data-error-for="debtor_name"></span>
</div>
<div class="dre-field">
<label for="debtor_business_type">Debtor Entity Type <span class="dre-required">*</span></label>
<select id="debtor_business_type" name="debtor_business_type" required>
<option value="INDIVIDUAL">Individual</option>
<option value="SOLE_PROPRIETORSHIP">Sole Proprietorship</option>
<option value="LLC" selected>LLC</option>
<option value="CORPORATION">Corporation</option>
<option value="PARTNERSHIP">Partnership</option>
<option value="OTHER">Other</option>
</select>
<span class="dre-field-error" data-error-for="debtor_business_type"></span>
</div>
<div class="dre-row">
<div class="dre-field">
<label for="debtor_contact_email">Debtor Email</label>
<input type="email" id="debtor_contact_email" name="debtor_contact_email" maxlength="254">
<span class="dre-field-error" data-error-for="debtor_contact_email"></span>
</div>
<div class="dre-field">
<label for="debtor_contact_phone">Debtor Phone</label>
<input type="tel" id="debtor_contact_phone" name="debtor_contact_phone" maxlength="50">
<span class="dre-field-error" data-error-for="debtor_contact_phone"></span>
</div>
</div>
<div class="dre-field">
<label for="debtor_physical_address">Debtor Address</label>
<input type="text" id="debtor_physical_address" name="debtor_physical_address" maxlength="500" placeholder="Street, City, State, ZIP">
<span class="dre-field-error" data-error-for="debtor_physical_address"></span>
</div>
</div>
<div class="dre-form-section">
<h3>Claim Details</h3>
<div class="dre-row">
<div class="dre-field">
<label for="amount">Amount Owed (USD) <span class="dre-required">*</span></label>
<input type="number" id="amount" name="amount" min="0.01" max="1000000" step="0.01" required placeholder="15000.00">
<span class="dre-hint">Maximum $1,000,000 per claim.</span>
<span class="dre-field-error" data-error-for="amount"></span>
</div>
<div class="dre-field">
<label for="invoice_date">Invoice / Debt Date</label>
<input type="date" id="invoice_date" name="invoice_date">
<span class="dre-field-error" data-error-for="invoice_date"></span>
</div>
</div>
<div class="dre-field">
<label for="client_reference">Your Invoice / PO Number</label>
<input type="text" id="client_reference" name="client_reference" maxlength="200" placeholder="INV-2048">
<span class="dre-field-error" data-error-for="client_reference"></span>
</div>
<div class="dre-field">
<label for="description">Description of the Debt</label>
<textarea id="description" name="description" maxlength="5000" placeholder="What is the debt for? (service, goods, contract, etc.)"></textarea>
<span class="dre-field-error" data-error-for="description"></span>
</div>
<div class="dre-field" style="background:#fdf3e7; border:1px solid #f2ddb8; border-radius:8px; padding:0.85rem 1rem;">
<span class="dre-small" style="color:#92600a;">
Important: Do not enter Social Security numbers, full bank account numbers, or card
numbers anywhere on this form. Submissions containing this information will be rejected.
</span>
</div>
</div>
<div class="dre-form-section">
<div class="dre-checkbox-field">
<input type="checkbox" id="tos_accepted" name="tos_accepted" required>
<label for="tos_accepted">
I confirm the information provided is accurate and I authorize Debt Recovery Experts
to review and pursue recovery of this claim on my behalf. I have read and agree to the
Terms of Service. <span class="dre-required">*</span>
</label>
</div>
<span class="dre-field-error" data-error-for="tos_accepted"></span>
</div>
<button type="submit" id="intake-submit" class="dre-btn dre-btn-primary dre-btn-block">
Submit Claim
</button>
</form>
<!-- ============================== SUCCESS STATE ============================== -->
<div id="intake-success" class="dre-card dre-state dre-hidden">
<div class="dre-state-icon ok">&#10003;</div>
<h2>Claim Received</h2>
<p id="intake-success-message">Our team will review and contact you shortly.</p>
<dl class="dre-kv">
<dt>Claim Number</dt>
<dd id="intake-claim-number">--</dd>
<dt>Client Number</dt>
<dd id="intake-client-number">--</dd>
</dl>
<p class="dre-mt">
<strong>Check your email.</strong> We will send a secure login link to access your client
portal, where you can track claim status, upload documents, and message our team.
</p>
<a href="login.html" class="dre-btn dre-btn-secondary dre-mt">Go to Client Login</a>
</div>
</main>
<footer class="dre-footer">
Debt Recovery Experts operates in compliance with the FDCPA and applicable state debt collection
laws. This form is for submitting claims for review only.
</footer>
<script src="js/dre-api.js"></script>
<script>
(function () {
"use strict";
var form = document.getElementById("intake-form");
var alertEl = document.getElementById("intake-alert");
var submitBtn = document.getElementById("intake-submit");
var successEl = document.getElementById("intake-success");
function clearFieldErrors() {
var errs = form.querySelectorAll(".dre-field-error");
for (var i = 0; i < errs.length; i++) {
errs[i].classList.remove("is-visible");
errs[i].textContent = "";
}
var inputs = form.querySelectorAll("[aria-invalid]");
for (var j = 0; j < inputs.length; j++) {
inputs[j].removeAttribute("aria-invalid");
}
}
function showFieldError(fieldKey, message) {
var el = form.querySelector('[data-error-for="' + fieldKey + '"]');
if (el) {
el.textContent = message;
el.classList.add("is-visible");
}
}
// Map a backend validation error location like "client.email" or
// "debtor.name" or "claim.amount_cents" to a local field key.
function mapLocToField(loc) {
var map = {
"client.company_name": "company_name",
"client.contact_name": "contact_name",
"client.email": "email",
"client.phone": "phone",
"debtor.name": "debtor_name",
"debtor.business_type": "debtor_business_type",
"debtor.contact_email": "debtor_contact_email",
"debtor.contact_phone": "debtor_contact_phone",
"debtor.physical_address": "debtor_physical_address",
"claim.amount_cents": "amount",
"claim.description": "description",
"claim.client_reference": "client_reference",
"claim.invoice_date": "invoice_date",
"tos_accepted": "tos_accepted",
};
return map[loc] || null;
}
function distributeValidationMessage(message) {
// Backend joins multiple errors with "; ", each like "loc: msg"
var parts = message.split("; ");
var matched = false;
parts.forEach(function (part) {
var idx = part.indexOf(": ");
if (idx === -1) return;
var loc = part.slice(0, idx);
var msg = part.slice(idx + 2);
var field = mapLocToField(loc);
if (field) {
showFieldError(field, msg);
var input = form.querySelector('[name="' + field + '"]');
if (input) input.setAttribute("aria-invalid", "true");
matched = true;
}
});
return matched;
}
form.addEventListener("submit", function (e) {
e.preventDefault();
DRE.hideAlert(alertEl);
clearFieldErrors();
var amountRaw = document.getElementById("amount").value;
var amountCents = Math.round(parseFloat(amountRaw || "0") * 100);
var payload = {
client: {
company_name: document.getElementById("company_name").value.trim(),
contact_name: document.getElementById("contact_name").value.trim(),
email: document.getElementById("email").value.trim(),
phone: document.getElementById("phone").value.trim() || null,
},
debtor: {
name: document.getElementById("debtor_name").value.trim(),
business_type: document.getElementById("debtor_business_type").value,
contact_email: document.getElementById("debtor_contact_email").value.trim() || null,
contact_phone: document.getElementById("debtor_contact_phone").value.trim() || null,
physical_address: document.getElementById("debtor_physical_address").value.trim() || null,
},
claim: {
amount_cents: amountCents,
description: document.getElementById("description").value.trim() || null,
client_reference: document.getElementById("client_reference").value.trim() || null,
invoice_date: document.getElementById("invoice_date").value || null,
},
tos_accepted: document.getElementById("tos_accepted").checked,
};
if (!payload.tos_accepted) {
showFieldError("tos_accepted", "You must accept the Terms of Service to submit a claim.");
return;
}
if (!amountCents || amountCents <= 0) {
showFieldError("amount", "Enter a valid amount greater than $0.");
return;
}
DRE.setLoading(submitBtn, true, "Submitting...");
DRE.apiFetch("/api/intake", { method: "POST", body: payload }).then(function (result) {
DRE.setLoading(submitBtn, false, null, "Submit Claim");
if (result.ok) {
var data = result.data;
document.getElementById("intake-claim-number").textContent = data.claim_number || "--";
document.getElementById("intake-client-number").textContent = data.client_number || "--";
document.getElementById("intake-success-message").textContent =
data.message || "Our team will review and contact you shortly.";
form.classList.add("dre-hidden");
successEl.classList.remove("dre-hidden");
successEl.scrollIntoView({ behavior: "smooth", block: "start" });
return;
}
var code = DRE.getErrorCode(result);
var message = DRE.getErrorMessage(result, "We could not submit your claim. Please try again.");
if (code === "validation_error") {
var matched = distributeValidationMessage(message);
if (!matched) {
DRE.showAlert(alertEl, message);
} else {
DRE.showAlert(alertEl, "Please correct the highlighted fields below.");
}
} else if (code === "rate_limited") {
DRE.showAlert(alertEl, "Too many submissions from this connection. Please try again in a few minutes.");
} else {
DRE.showAlert(alertEl, message);
}
alertEl.scrollIntoView({ behavior: "smooth", block: "start" });
});
});
})();
</script>
</body>
</html>