- Yellow instruction box explains 3 methods in plain English (phone photo, computer upload, scanner) - One big orange "Add Photo of Your ID" button - Webcam + QR code kept in code but simplified UI - Accepted formats note Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
661 lines
36 KiB
Text
661 lines
36 KiB
Text
---
|
||
// Unified DOT intake form — shows relevant sections based on services ordered.
|
||
// Handles: MCS-150, UCR, D&A, BOC-3, USDOT, MC Authority, Audit Prep, bundles.
|
||
---
|
||
|
||
<div class="pw-step" data-slug="dot-intake">
|
||
<h2>DOT Filing Information</h2>
|
||
<p class="pw-help">
|
||
Provide your carrier information. We will prepare and submit your filings on your behalf.
|
||
</p>
|
||
<div class="pw-security-notice">
|
||
<svg style="width:16px;height:16px;flex-shrink:0;margin-top:2px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"/></svg>
|
||
<span>All personal information is transmitted over 256-bit SSL encryption and stored encrypted at rest. We never share your data with third parties.</span>
|
||
</div>
|
||
|
||
<div class="pw-form-grid">
|
||
|
||
<!-- ═══ SECTION: Company Info (always shown) ═══ -->
|
||
<h3>Company Information</h3>
|
||
<div class="pw-row">
|
||
<label class="pw-field"><span>Legal Entity Name <em>*</em></span>
|
||
<input type="text" id="dot-legal-name" required placeholder="As registered with FMCSA" /></label>
|
||
</div>
|
||
<div class="pw-row">
|
||
<label class="pw-field"><span>DBA / Trade Name</span>
|
||
<input type="text" id="dot-dba" placeholder="If different from legal name" /></label>
|
||
</div>
|
||
<div class="pw-row-3">
|
||
<label class="pw-field"><span>USDOT Number <em>*</em></span>
|
||
<input type="text" id="dot-dot" required placeholder="e.g. 1234567" /></label>
|
||
<label class="pw-field"><span>MC/MX/FF Number</span>
|
||
<input type="text" id="dot-mc" placeholder="e.g. MC-123456" /></label>
|
||
<label class="pw-field"><span>EIN (Tax ID)</span>
|
||
<input type="text" id="dot-ein" placeholder="XX-XXXXXXX" maxlength="12" /></label>
|
||
</div>
|
||
|
||
<h3>Principal Business Address</h3>
|
||
<div class="pw-row">
|
||
<label class="pw-field"><span>Street Address <em>*</em></span>
|
||
<input type="text" id="dot-street" required placeholder="123 Main St" /></label>
|
||
</div>
|
||
<div class="pw-row-3">
|
||
<label class="pw-field"><span>City <em>*</em></span>
|
||
<input type="text" id="dot-city" required /></label>
|
||
<label class="pw-field"><span>State <em>*</em></span>
|
||
<select id="dot-state" required>
|
||
<option value="">--</option>
|
||
<option value="AL">AL</option><option value="AK">AK</option><option value="AZ">AZ</option><option value="AR">AR</option><option value="CA">CA</option><option value="CO">CO</option><option value="CT">CT</option><option value="DE">DE</option><option value="FL">FL</option><option value="GA">GA</option><option value="HI">HI</option><option value="ID">ID</option><option value="IL">IL</option><option value="IN">IN</option><option value="IA">IA</option><option value="KS">KS</option><option value="KY">KY</option><option value="LA">LA</option><option value="ME">ME</option><option value="MD">MD</option><option value="MA">MA</option><option value="MI">MI</option><option value="MN">MN</option><option value="MS">MS</option><option value="MO">MO</option><option value="MT">MT</option><option value="NE">NE</option><option value="NV">NV</option><option value="NH">NH</option><option value="NJ">NJ</option><option value="NM">NM</option><option value="NY">NY</option><option value="NC">NC</option><option value="ND">ND</option><option value="OH">OH</option><option value="OK">OK</option><option value="OR">OR</option><option value="PA">PA</option><option value="RI">RI</option><option value="SC">SC</option><option value="SD">SD</option><option value="TN">TN</option><option value="TX">TX</option><option value="UT">UT</option><option value="VT">VT</option><option value="VA">VA</option><option value="WA">WA</option><option value="WV">WV</option><option value="WI">WI</option><option value="WY">WY</option><option value="DC">DC</option>
|
||
</select></label>
|
||
<label class="pw-field"><span>ZIP <em>*</em></span>
|
||
<input type="text" id="dot-zip" required maxlength="10" placeholder="12345" /></label>
|
||
</div>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Phone <em>*</em></span>
|
||
<input type="tel" id="dot-phone" required placeholder="(555) 123-4567" /></label>
|
||
<label class="pw-field"><span>Email <em>*</em></span>
|
||
<input type="email" id="dot-email" required placeholder="you@company.com" /></label>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: Entity & Operations (MCS-150, USDOT, MC Auth, bundles) ═══ -->
|
||
<div id="dot-sec-operations" hidden>
|
||
<h3>Entity & Operations</h3>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Entity Type <em>*</em></span>
|
||
<select id="dot-entity-type" required>
|
||
<option value="">Select...</option>
|
||
<option value="sole_proprietorship">Sole Proprietorship</option>
|
||
<option value="partnership">Partnership</option>
|
||
<option value="corporation">Corporation</option>
|
||
<option value="llc">LLC</option>
|
||
<option value="other">Other</option>
|
||
</select></label>
|
||
<label class="pw-field"><span>Carrier Operation <em>*</em></span>
|
||
<select id="dot-carrier-op" required>
|
||
<option value="">Select...</option>
|
||
<option value="authorized_for_hire">Authorized For-Hire</option>
|
||
<option value="exempt_for_hire">Exempt For-Hire</option>
|
||
<option value="private_property">Private (Property)</option>
|
||
<option value="private_passengers">Private (Passengers)</option>
|
||
</select></label>
|
||
</div>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Interstate / Intrastate <em>*</em></span>
|
||
<select id="dot-interstate" required>
|
||
<option value="">Select...</option>
|
||
<option value="interstate">Interstate (across state lines)</option>
|
||
<option value="intrastate_hazmat">Intrastate — Hazmat</option>
|
||
<option value="intrastate_non_hazmat">Intrastate — Non-hazmat</option>
|
||
</select></label>
|
||
<label class="pw-field"><span>Hazmat? <em>*</em></span>
|
||
<select id="dot-hazmat" required>
|
||
<option value="">Select...</option>
|
||
<option value="no">No</option>
|
||
<option value="yes">Yes</option>
|
||
</select></label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: Fleet (MCS-150, UCR, bundles) ═══ -->
|
||
<div id="dot-sec-fleet" hidden>
|
||
<h3>Fleet Information</h3>
|
||
<div class="pw-row-3">
|
||
<label class="pw-field"><span>Power Units (trucks) <em>*</em></span>
|
||
<input type="number" id="dot-power-units" required min="0" placeholder="e.g. 5" /></label>
|
||
<label class="pw-field"><span>Drivers <em>*</em></span>
|
||
<input type="number" id="dot-drivers" required min="0" placeholder="e.g. 6" /></label>
|
||
<label class="pw-field"><span>Annual Miles</span>
|
||
<input type="number" id="dot-miles" min="0" placeholder="e.g. 250000" /></label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: UCR Fleet Bracket (UCR only) ═══ -->
|
||
<div id="dot-sec-ucr" hidden>
|
||
<h3>UCR Registration</h3>
|
||
<p class="pw-field-help">UCR fees are based on the number of qualifying commercial motor vehicles (over 10,001 lbs GVWR, 10+ passengers, or placardable hazmat).</p>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Fleet Size Bracket <em>*</em></span>
|
||
<select id="dot-ucr-bracket">
|
||
<option value="">Select...</option>
|
||
<option value="0-2">0–2 vehicles ($76 gov fee)</option>
|
||
<option value="3-5">3–5 vehicles ($227 gov fee)</option>
|
||
<option value="6-20">6–20 vehicles ($452 gov fee)</option>
|
||
<option value="21-100">21–100 vehicles ($1,576 gov fee)</option>
|
||
<option value="101-1000">101–1,000 vehicles ($7,511 gov fee)</option>
|
||
<option value="1001+">1,001+ vehicles ($73,346 gov fee)</option>
|
||
</select></label>
|
||
<label class="pw-field"><span>Base State <em>*</em></span>
|
||
<input type="text" id="dot-ucr-state" readonly placeholder="From address above" /></label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: Cargo Types (MCS-150, bundles) ═══ -->
|
||
<div id="dot-sec-cargo" hidden>
|
||
<h3>Cargo Types (check all that apply)</h3>
|
||
<div class="pw-cargo-grid">
|
||
<label><input type="checkbox" data-cargo="general" /> General Freight</label>
|
||
<label><input type="checkbox" data-cargo="household" /> Household Goods</label>
|
||
<label><input type="checkbox" data-cargo="metal" /> Metal/Sheets/Coils</label>
|
||
<label><input type="checkbox" data-cargo="motor_vehicles" /> Motor Vehicles</label>
|
||
<label><input type="checkbox" data-cargo="drivetow" /> Drive/Tow Away</label>
|
||
<label><input type="checkbox" data-cargo="logs" /> Logs/Poles/Lumber</label>
|
||
<label><input type="checkbox" data-cargo="building_materials" /> Building Materials</label>
|
||
<label><input type="checkbox" data-cargo="mobile_homes" /> Mobile Homes</label>
|
||
<label><input type="checkbox" data-cargo="machinery" /> Machinery/Large Objects</label>
|
||
<label><input type="checkbox" data-cargo="fresh_produce" /> Fresh Produce</label>
|
||
<label><input type="checkbox" data-cargo="liquids" /> Liquids/Gases</label>
|
||
<label><input type="checkbox" data-cargo="intermodal" /> Intermodal Containers</label>
|
||
<label><input type="checkbox" data-cargo="passengers" /> Passengers</label>
|
||
<label><input type="checkbox" data-cargo="oilfield" /> Oilfield Equipment</label>
|
||
<label><input type="checkbox" data-cargo="livestock" /> Livestock</label>
|
||
<label><input type="checkbox" data-cargo="grain" /> Grain/Feed/Hay</label>
|
||
<label><input type="checkbox" data-cargo="coal" /> Coal/Coke</label>
|
||
<label><input type="checkbox" data-cargo="meat" /> Meat</label>
|
||
<label><input type="checkbox" data-cargo="garbage" /> Garbage/Refuse</label>
|
||
<label><input type="checkbox" data-cargo="chemicals" /> Chemicals</label>
|
||
<label><input type="checkbox" data-cargo="refrigerated" /> Refrigerated Food</label>
|
||
<label><input type="checkbox" data-cargo="beverages" /> Beverages</label>
|
||
<label><input type="checkbox" data-cargo="construction" /> Construction</label>
|
||
<label><input type="checkbox" data-cargo="other" /> Other</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: D&A Program (D&A, bundles) ═══ -->
|
||
<div id="dot-sec-da" hidden>
|
||
<h3>Drug & Alcohol Compliance Program</h3>
|
||
<p class="pw-field-help">Required for all carriers with CDL drivers. We will enroll you in a DOT-compliant consortium and set up your program.</p>
|
||
<div class="pw-row-3">
|
||
<label class="pw-field"><span>CDL Drivers <em>*</em></span>
|
||
<input type="number" id="dot-cdl-drivers" min="0" placeholder="Number of CDL holders" /></label>
|
||
<label class="pw-field"><span>Owner-Operators <em>*</em></span>
|
||
<input type="number" id="dot-owner-ops" min="0" value="0" /></label>
|
||
<label class="pw-field"><span>DER Name</span>
|
||
<input type="text" id="dot-der-name" placeholder="Designated Employer Rep (optional)" /></label>
|
||
</div>
|
||
<div class="pw-row">
|
||
<label class="pw-field"><span>Current D&A Program Provider</span>
|
||
<input type="text" id="dot-current-da" placeholder="Leave blank if none / first time" /></label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: BOC-3 (BOC-3, bundles) ═══ -->
|
||
<div id="dot-sec-boc3" hidden>
|
||
<h3>BOC-3 Process Agent</h3>
|
||
<p class="pw-field-help">We will designate a blanket process agent covering all 48 contiguous states + DC.</p>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Docket Type</span>
|
||
<select id="dot-docket-type">
|
||
<option value="">Select if applicable...</option>
|
||
<option value="MC">MC (Motor Carrier)</option>
|
||
<option value="FF">FF (Freight Forwarder)</option>
|
||
<option value="MX">MX (Mexican Carrier)</option>
|
||
</select></label>
|
||
<label class="pw-field"><span>Docket Number</span>
|
||
<input type="text" id="dot-docket-num" placeholder="e.g. 123456" /></label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: Authorized Signer (always shown) ═══ -->
|
||
<h3>Authorized Signer</h3>
|
||
<div class="pw-row-2">
|
||
<label class="pw-field"><span>Full Name <em>*</em></span>
|
||
<input type="text" id="dot-signer-name" required placeholder="Owner or officer" /></label>
|
||
<label class="pw-field"><span>Title <em>*</em></span>
|
||
<input type="text" id="dot-signer-title" required placeholder="e.g. Owner, President" /></label>
|
||
</div>
|
||
|
||
<!-- ═══ SECTION: Photo ID (MCS-150, MC Auth) ═══ -->
|
||
<div id="dot-sec-photo-id" hidden>
|
||
<h3>Government-Issued Photo ID</h3>
|
||
<p class="pw-field-help">FMCSA requires a copy of your government-issued photo ID (driver's license, passport, or state ID) to verify the signer's identity.</p>
|
||
<div style="background:#fefce8;border:1px solid #fbbf24;border-radius:8px;padding:12px 14px;margin-bottom:12px;font-size:13px;color:#854d0e;line-height:1.6">
|
||
<strong>How to add your ID:</strong><br>
|
||
• <strong>On your phone?</strong> Tap the button below and take a picture of the front of your ID<br>
|
||
• <strong>On a computer?</strong> Take a photo of your ID with your phone, email or text it to yourself, save it, then click the button below to upload it<br>
|
||
• <strong>Have a scanner?</strong> Scan your ID, save the file, then click the button below to upload it
|
||
</div>
|
||
<div class="pw-upload-area">
|
||
<input type="file" id="dot-photo-id" accept="image/*,.pdf" style="display:none" />
|
||
<!-- Preview + quality check -->
|
||
<div id="dot-id-preview" hidden>
|
||
<div style="text-align:center;margin-bottom:12px">
|
||
<img id="dot-id-img" style="max-width:320px;max-height:240px;border-radius:8px;border:2px solid #d1d5db;display:block;margin:0 auto" />
|
||
<p id="dot-id-resolution" style="font-size:11px;color:#94a3b8;margin:6px 0 0"></p>
|
||
</div>
|
||
<div id="dot-id-quality-check" style="background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:14px;margin-bottom:12px">
|
||
<p style="font-size:13px;font-weight:600;color:#1a2744;margin:0 0 10px">Photo ID Quality Check</p>
|
||
<div id="dot-id-checks" style="font-size:12px;color:#374151;line-height:2"></div>
|
||
<div id="dot-id-quality-warn" hidden style="background:#fef3c7;border:1px solid #fbbf24;border-radius:6px;padding:10px;margin-top:10px">
|
||
<p style="font-size:12px;color:#92400e;margin:0;font-weight:600">⚠ Image quality issue detected</p>
|
||
<p id="dot-id-quality-msg" style="font-size:11px;color:#92400e;margin:4px 0 0"></p>
|
||
</div>
|
||
<div id="dot-id-quality-ok" hidden style="background:#f0fdf4;border:1px solid #86efac;border-radius:6px;padding:10px;margin-top:10px">
|
||
<p style="font-size:12px;color:#166534;margin:0">✔ Image looks good — clear and properly sized for submission.</p>
|
||
</div>
|
||
</div>
|
||
<div style="display:flex;gap:8px;justify-content:center">
|
||
<button type="button" id="dot-id-accept" style="padding:8px 24px;background:#059669;color:#fff;border:none;border-radius:6px;font-size:13px;font-weight:600;cursor:pointer">Accept & Continue</button>
|
||
<button type="button" id="dot-id-retake" style="padding:8px 24px;background:#fff;color:#374151;border:1px solid #d1d5db;border-radius:6px;font-size:13px;cursor:pointer">Retake / Re-upload</button>
|
||
</div>
|
||
</div>
|
||
<div id="dot-id-upload-options">
|
||
<button type="button" id="dot-id-btn" style="display:flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:16px 24px;background:#f97316;color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:15px;font-weight:600;box-shadow:0 2px 8px rgba(249,115,22,0.3)">
|
||
<svg style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z"/><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z"/></svg>
|
||
Add Photo of Your ID
|
||
</button>
|
||
<p style="font-size:11px;color:#94a3b8;text-align:center;margin:8px 0 0">Accepted formats: JPEG, PNG, PDF · Max 15MB</p>
|
||
</div>
|
||
</div>
|
||
<div class="pw-security-notice" style="margin-top:8px">
|
||
<svg style="width:14px;height:14px;flex-shrink:0;margin-top:2px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"/></svg>
|
||
<span style="font-size:11px">Your ID is encrypted in transit and at rest. Used only for FMCSA identity verification and automatically deleted after filing.</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="pw-dot-errors" class="pw-err" hidden></div>
|
||
</div>
|
||
|
||
<style>
|
||
.pw-step h2 { margin: 0 0 0.5rem; color: #1a2744; }
|
||
.pw-step h3 { color: #1a2744; margin: 1.25rem 0 0.5rem; font-size: 0.95rem; border-bottom: 1px solid #e2e8f0; padding-bottom: 0.3rem; }
|
||
.pw-help { color: #64748b; font-size: 0.9rem; margin-bottom: 1rem; }
|
||
.pw-form-grid { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 1rem 1.25rem; }
|
||
.pw-row, .pw-row-2, .pw-row-3 { margin-bottom: 0.75rem; }
|
||
.pw-row-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem; }
|
||
.pw-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.75rem; }
|
||
.pw-field { display: flex; flex-direction: column; gap: 0.2rem; }
|
||
.pw-field span { font-size: 0.8rem; font-weight: 600; color: #374151; }
|
||
.pw-field em { color: #dc2626; font-style: normal; }
|
||
.pw-field input, .pw-field select { padding: 0.5rem; border: 1px solid #d1d5db; border-radius: 6px; font-size: 0.85rem; }
|
||
.pw-field input:focus, .pw-field select:focus { outline: none; border-color: #f97316; box-shadow: 0 0 0 2px rgba(249,115,22,0.2); }
|
||
.pw-field-help { font-size: 0.8rem; color: #64748b; margin: 0 0 0.5rem; }
|
||
.pw-cargo-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.3rem 1rem; font-size: 0.85rem; color: #374151; }
|
||
.pw-cargo-grid label { display: flex; align-items: center; gap: 0.4rem; cursor: pointer; }
|
||
.pw-security-notice { display: flex; gap: 8px; align-items: flex-start; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 10px 14px; margin-bottom: 1rem; font-size: 12px; color: #1e40af; line-height: 1.5; }
|
||
.pw-upload-area { border: 2px dashed #d1d5db; border-radius: 8px; padding: 1rem; text-align: center; }
|
||
.pw-err { color: #b91c1c; margin-top: 0.75rem; font-size: 0.9rem; background: #fee2e2; padding: 0.5rem 0.75rem; border-radius: 6px; }
|
||
@media (max-width: 640px) { .pw-row-2, .pw-row-3 { grid-template-columns: 1fr; } .pw-cargo-grid { grid-template-columns: 1fr 1fr; } }
|
||
</style>
|
||
|
||
<script is:inline>
|
||
// Guard: only run on pages that have the DOT intake step
|
||
if (!document.querySelector('[data-slug="dot-intake"], [data-step="dot-intake"]')) {
|
||
// Not a DOT intake page — skip all initialization
|
||
} else {
|
||
|
||
// Determine which sections to show based on services in the batch
|
||
const DOT_SECTIONS = {
|
||
"mcs150-update": ["dot-sec-operations","dot-sec-fleet","dot-sec-cargo","dot-sec-photo-id"],
|
||
"ucr-registration": ["dot-sec-fleet","dot-sec-ucr"],
|
||
"dot-drug-alcohol": ["dot-sec-da"],
|
||
"boc3-filing": ["dot-sec-boc3"],
|
||
"dot-registration": ["dot-sec-operations"],
|
||
"mc-authority": ["dot-sec-operations","dot-sec-photo-id"],
|
||
"dot-audit-prep": ["dot-sec-operations","dot-sec-fleet","dot-sec-da"],
|
||
"dot-full-compliance": ["dot-sec-operations","dot-sec-fleet","dot-sec-cargo","dot-sec-ucr","dot-sec-da","dot-sec-boc3","dot-sec-photo-id"],
|
||
};
|
||
|
||
function showRelevantSections() {
|
||
const PW = (window).PWIntake;
|
||
const state = PW?.get?.() || {};
|
||
// Get service slug from wizard element or state
|
||
const wizardEl = document.querySelector(".pw-wizard[data-service]");
|
||
const pageSlug = wizardEl?.getAttribute("data-service") || "";
|
||
const slugs = state.batch_slugs || [pageSlug || state.service_slug || ""];
|
||
|
||
// Collect all sections to show
|
||
const show = new Set();
|
||
for (const slug of slugs) {
|
||
for (const sec of (DOT_SECTIONS[slug] || [])) {
|
||
show.add(sec);
|
||
}
|
||
}
|
||
// Default: always show operations + fleet if nothing specific
|
||
if (show.size === 0) {
|
||
show.add("dot-sec-operations");
|
||
show.add("dot-sec-fleet");
|
||
}
|
||
|
||
// Show/hide sections
|
||
for (const id of Object.keys(DOT_SECTIONS).flatMap(k => DOT_SECTIONS[k])) {
|
||
const el = document.getElementById(id);
|
||
if (el) el.hidden = !show.has(id);
|
||
}
|
||
|
||
// Auto-fill UCR base state from address
|
||
const stateEl = document.getElementById("dot-state");
|
||
const ucrState = document.getElementById("dot-ucr-state");
|
||
if (stateEl && ucrState) {
|
||
ucrState.value = stateEl.value;
|
||
stateEl.addEventListener("change", () => { ucrState.value = stateEl.value; });
|
||
}
|
||
}
|
||
|
||
// Show sections — retry until wizard element is found
|
||
function initSections() {
|
||
const wizardEl = document.querySelector(".pw-wizard[data-service]");
|
||
if (!wizardEl) { setTimeout(initSections, 100); return; }
|
||
showRelevantSections();
|
||
}
|
||
initSections();
|
||
window.addEventListener("pw:step-shown", (evt) => {
|
||
if (evt.detail.step !== "dot-intake") return;
|
||
showRelevantSections();
|
||
|
||
// Hydrate from existing intake data
|
||
const s = (window).PWIntake.get();
|
||
const d = s.intake_data || {};
|
||
const map = {
|
||
"dot-legal-name": d.legal_name || "", "dot-dba": d.dba_name || "",
|
||
"dot-dot": d.dot_number || "", "dot-mc": d.mc_number || "", "dot-ein": d.ein || "",
|
||
"dot-street": d.address_street || "", "dot-city": d.address_city || "",
|
||
"dot-state": d.address_state || "", "dot-zip": d.address_zip || "",
|
||
"dot-phone": d.phone || "", "dot-email": d.email || s.email || "",
|
||
"dot-entity-type": d.entity_type || "", "dot-carrier-op": d.carrier_operation || "",
|
||
"dot-interstate": d.interstate_intrastate || "", "dot-hazmat": d.hazmat || "",
|
||
"dot-power-units": d.power_units || "", "dot-drivers": d.drivers || "",
|
||
"dot-miles": d.annual_miles || "", "dot-signer-name": d.signer_name || "",
|
||
"dot-signer-title": d.signer_title || "", "dot-cdl-drivers": d.cdl_drivers || "",
|
||
"dot-owner-ops": d.owner_operators || "0", "dot-der-name": d.der_name || "",
|
||
"dot-current-da": d.current_da_provider || "", "dot-ucr-bracket": d.fleet_size_bracket || "",
|
||
};
|
||
for (const [id, val] of Object.entries(map)) {
|
||
const el = document.getElementById(id);
|
||
if (el && val) el.value = val;
|
||
}
|
||
});
|
||
|
||
// Save all data on step-next
|
||
window.addEventListener("pw:step-next", (evt) => {
|
||
const PW = (window).PWIntake;
|
||
if (PW.steps[PW.get().step_index] !== "dot-intake") return;
|
||
const errDiv = document.getElementById("pw-dot-errors");
|
||
errDiv.hidden = true;
|
||
|
||
// Validate required fields (only visible ones)
|
||
const required = ["dot-legal-name","dot-dot","dot-street","dot-city","dot-state","dot-zip","dot-phone","dot-email","dot-signer-name","dot-signer-title"];
|
||
const missing = [];
|
||
for (const id of required) {
|
||
const el = document.getElementById(id);
|
||
if (!el || !el.value.trim()) missing.push(el?.parentElement?.querySelector("span")?.textContent || id);
|
||
}
|
||
// Validate visible-section-specific required fields
|
||
const visibleRequired = {
|
||
"dot-sec-operations": ["dot-entity-type","dot-carrier-op","dot-interstate","dot-hazmat"],
|
||
"dot-sec-fleet": ["dot-power-units","dot-drivers"],
|
||
};
|
||
for (const [secId, fields] of Object.entries(visibleRequired)) {
|
||
const sec = document.getElementById(secId);
|
||
if (sec && !sec.hidden) {
|
||
for (const id of fields) {
|
||
const el = document.getElementById(id);
|
||
if (!el || !el.value.trim()) missing.push(el?.parentElement?.querySelector("span")?.textContent || id);
|
||
}
|
||
}
|
||
}
|
||
if (missing.length) { evt.preventDefault(); errDiv.hidden = false; errDiv.textContent = "Please fill in: " + missing.join(", "); return; }
|
||
|
||
// Collect cargo types
|
||
const cargoTypes = [];
|
||
document.querySelectorAll("[data-cargo]").forEach(function(cb) {
|
||
if (cb.checked) cargoTypes.push(cb.dataset.cargo);
|
||
});
|
||
|
||
const val = (id) => (document.getElementById(id))?.value?.trim() || "";
|
||
const state = PW.get();
|
||
PW.set({ ...state, intake_data: { ...state.intake_data,
|
||
legal_name: val("dot-legal-name"), dba_name: val("dot-dba"),
|
||
dot_number: val("dot-dot"), mc_number: val("dot-mc"), ein: val("dot-ein"),
|
||
address_street: val("dot-street"), address_city: val("dot-city"),
|
||
address_state: val("dot-state"), address_zip: val("dot-zip"),
|
||
phone: val("dot-phone"), email: val("dot-email"),
|
||
entity_type: val("dot-entity-type"), carrier_operation: val("dot-carrier-op"),
|
||
interstate_intrastate: val("dot-interstate"), hazmat: val("dot-hazmat"),
|
||
power_units: val("dot-power-units"), drivers: val("dot-drivers"),
|
||
annual_miles: val("dot-miles"), cargo_types: cargoTypes,
|
||
signer_name: val("dot-signer-name"), signer_title: val("dot-signer-title"),
|
||
fleet_size_bracket: val("dot-ucr-bracket"), base_state: val("dot-ucr-state"),
|
||
cdl_drivers: val("dot-cdl-drivers"), owner_operators: val("dot-owner-ops"),
|
||
der_name: val("dot-der-name"), current_da_provider: val("dot-current-da"),
|
||
docket_type: val("dot-docket-type"), docket_number: val("dot-docket-num"),
|
||
photo_id_uploaded: !!(window).__dotPhotoId,
|
||
}});
|
||
});
|
||
|
||
// Photo ID upload (elements may not exist if section is hidden)
|
||
const idBtn = document.getElementById("dot-id-btn");
|
||
const idInput = document.getElementById("dot-photo-id");
|
||
const idPreview = document.getElementById("dot-id-preview");
|
||
const idImg = document.getElementById("dot-id-img");
|
||
const idRemove = document.getElementById("dot-id-remove");
|
||
const idQrBtn = document.getElementById("dot-id-qr-btn");
|
||
const idQrContainer = document.getElementById("dot-id-qr-container");
|
||
const idUploadOpts = document.getElementById("dot-id-upload-options");
|
||
|
||
// Upload button — opens file picker
|
||
idBtn?.addEventListener("click", function() { if (idInput) idInput.click(); });
|
||
|
||
// Webcam camera button
|
||
var idCamBtn = document.getElementById("dot-id-cam-btn");
|
||
var idWebcam = document.getElementById("dot-id-webcam");
|
||
var idVideo = document.getElementById("dot-id-video");
|
||
var idCaptureBtn = document.getElementById("dot-id-capture");
|
||
var idCamCancel = document.getElementById("dot-id-cam-cancel");
|
||
var idCanvas = document.getElementById("dot-id-canvas");
|
||
var webcamStream = null;
|
||
|
||
idCamBtn?.addEventListener("click", function() {
|
||
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||
alert("Camera not available in this browser. Please use Upload File instead.");
|
||
return;
|
||
}
|
||
navigator.mediaDevices.getUserMedia({ video: { width: { ideal: 1280 }, height: { ideal: 720 }, facingMode: "environment" } })
|
||
.then(function(stream) {
|
||
webcamStream = stream;
|
||
idVideo.srcObject = stream;
|
||
if (idWebcam) idWebcam.hidden = false;
|
||
if (idUploadOpts) {
|
||
// Hide upload options but keep webcam visible
|
||
var buttons = idUploadOpts.querySelector("div");
|
||
if (buttons) buttons.style.display = "none";
|
||
}
|
||
})
|
||
.catch(function(err) {
|
||
alert("Could not access camera: " + err.message + ". Please use Upload File instead.");
|
||
});
|
||
});
|
||
|
||
idCaptureBtn?.addEventListener("click", function() {
|
||
if (!idVideo || !idCanvas) return;
|
||
idCanvas.width = idVideo.videoWidth;
|
||
idCanvas.height = idVideo.videoHeight;
|
||
var ctx = idCanvas.getContext("2d");
|
||
ctx.drawImage(idVideo, 0, 0);
|
||
// Stop webcam
|
||
if (webcamStream) {
|
||
webcamStream.getTracks().forEach(function(t) { t.stop(); });
|
||
webcamStream = null;
|
||
}
|
||
if (idWebcam) idWebcam.hidden = true;
|
||
// Convert to blob and handle as file
|
||
idCanvas.toBlob(function(blob) {
|
||
var file = new File([blob], "photo-id-capture.jpg", { type: "image/jpeg" });
|
||
handleIdFile(file);
|
||
}, "image/jpeg", 0.92);
|
||
});
|
||
|
||
idCamCancel?.addEventListener("click", function() {
|
||
if (webcamStream) {
|
||
webcamStream.getTracks().forEach(function(t) { t.stop(); });
|
||
webcamStream = null;
|
||
}
|
||
if (idWebcam) idWebcam.hidden = true;
|
||
if (idUploadOpts) {
|
||
var buttons = idUploadOpts.querySelector("div");
|
||
if (buttons) buttons.style.display = "";
|
||
}
|
||
});
|
||
|
||
// QR code button — show QR with current page URL for phone upload
|
||
idQrBtn?.addEventListener("click", function() {
|
||
if (!idQrContainer) return;
|
||
var showing = !idQrContainer.hidden;
|
||
idQrContainer.hidden = showing;
|
||
if (!showing) {
|
||
// Generate QR code using canvas
|
||
var canvas = document.getElementById("dot-id-qr-canvas");
|
||
if (canvas && !canvas.dataset.rendered) {
|
||
var url = window.location.href;
|
||
// Use a simple QR code API (Google Charts - no library needed)
|
||
var img = new Image();
|
||
img.crossOrigin = "anonymous";
|
||
img.onload = function() {
|
||
canvas.width = 200;
|
||
canvas.height = 200;
|
||
var ctx = canvas.getContext("2d");
|
||
ctx.drawImage(img, 0, 0, 200, 200);
|
||
canvas.dataset.rendered = "1";
|
||
};
|
||
img.src = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" + encodeURIComponent(url);
|
||
}
|
||
}
|
||
});
|
||
|
||
var idAcceptBtn = document.getElementById("dot-id-accept");
|
||
var idRetakeBtn = document.getElementById("dot-id-retake");
|
||
var idQualityWarn = document.getElementById("dot-id-quality-warn");
|
||
var idQualityOk = document.getElementById("dot-id-quality-ok");
|
||
var idQualityMsg = document.getElementById("dot-id-quality-msg");
|
||
var idChecksDiv = document.getElementById("dot-id-checks");
|
||
var idResolution = document.getElementById("dot-id-resolution");
|
||
var idAccepted = false;
|
||
|
||
function runQualityCheck(file, imgEl) {
|
||
var checks = [];
|
||
var warnings = [];
|
||
|
||
// File size check
|
||
var sizeMB = (file.size / 1024 / 1024).toFixed(1);
|
||
if (file.size < 50000) {
|
||
checks.push({ok: false, text: "File size: " + sizeMB + "MB — too small, may be low quality"});
|
||
warnings.push("Image file is very small (" + sizeMB + "MB). Please use a higher resolution.");
|
||
} else if (file.size > 15000000) {
|
||
checks.push({ok: false, text: "File size: " + sizeMB + "MB — very large"});
|
||
warnings.push("File is over 15MB. Consider a smaller image.");
|
||
} else {
|
||
checks.push({ok: true, text: "File size: " + sizeMB + "MB"});
|
||
}
|
||
|
||
// File type check
|
||
var validTypes = ["image/jpeg","image/png","image/heic","image/heif","image/webp","application/pdf"];
|
||
if (validTypes.indexOf(file.type) >= 0 || file.type.startsWith("image/")) {
|
||
checks.push({ok: true, text: "File type: " + file.type.split("/")[1].toUpperCase()});
|
||
} else {
|
||
checks.push({ok: false, text: "File type: " + file.type + " — not a recognized image format"});
|
||
warnings.push("Please upload a JPEG, PNG, or PDF image of your ID.");
|
||
}
|
||
|
||
// Resolution check (for images)
|
||
if (imgEl && imgEl.naturalWidth) {
|
||
var w = imgEl.naturalWidth;
|
||
var h = imgEl.naturalHeight;
|
||
if (idResolution) idResolution.textContent = w + " × " + h + " pixels";
|
||
if (w < 400 || h < 250) {
|
||
checks.push({ok: false, text: "Resolution: " + w + "×" + h + " — too low, text may be unreadable"});
|
||
warnings.push("Image resolution is too low. FMCSA may not accept an unreadable ID.");
|
||
} else if (w < 800 || h < 500) {
|
||
checks.push({ok: true, text: "Resolution: " + w + "×" + h + " — acceptable"});
|
||
} else {
|
||
checks.push({ok: true, text: "Resolution: " + w + "×" + h + " — good"});
|
||
}
|
||
|
||
// Aspect ratio check (ID cards are roughly 1.6:1)
|
||
var ratio = Math.max(w,h) / Math.min(w,h);
|
||
if (ratio > 0.8 && ratio < 2.5) {
|
||
checks.push({ok: true, text: "Aspect ratio: looks like an ID card"});
|
||
} else {
|
||
checks.push({ok: false, text: "Aspect ratio: " + ratio.toFixed(1) + ":1 — doesn't look like a standard ID"});
|
||
warnings.push("This doesn't appear to be a photo of an ID card. Please upload a clear photo of the front of your government-issued ID.");
|
||
}
|
||
}
|
||
|
||
// Render checks
|
||
if (idChecksDiv) {
|
||
idChecksDiv.innerHTML = checks.map(function(c) {
|
||
return '<div style="display:flex;align-items:center;gap:6px">' +
|
||
(c.ok ? '<span style="color:#059669">✔</span>' : '<span style="color:#dc2626">✘</span>') +
|
||
'<span>' + c.text + '</span></div>';
|
||
}).join("");
|
||
}
|
||
|
||
if (warnings.length > 0) {
|
||
if (idQualityWarn) { idQualityWarn.hidden = false; idQualityMsg.textContent = warnings.join(" "); }
|
||
if (idQualityOk) idQualityOk.hidden = true;
|
||
} else {
|
||
if (idQualityWarn) idQualityWarn.hidden = true;
|
||
if (idQualityOk) idQualityOk.hidden = false;
|
||
}
|
||
}
|
||
|
||
function handleIdFile(file) {
|
||
if (!file) return;
|
||
window.__dotPhotoId = file;
|
||
idAccepted = false;
|
||
if (file.type.startsWith("image/") && idImg) {
|
||
var reader = new FileReader();
|
||
reader.onload = function(e) {
|
||
idImg.src = e.target?.result;
|
||
// Wait for image to load to check dimensions
|
||
idImg.onload = function() { runQualityCheck(file, idImg); };
|
||
};
|
||
reader.readAsDataURL(file);
|
||
} else {
|
||
// PDF or other — can't preview but accept
|
||
if (idResolution) idResolution.textContent = "PDF document";
|
||
runQualityCheck(file, null);
|
||
}
|
||
if (idPreview) idPreview.hidden = false;
|
||
if (idUploadOpts) idUploadOpts.style.display = "none";
|
||
}
|
||
|
||
// Accept button
|
||
idAcceptBtn?.addEventListener("click", function() {
|
||
idAccepted = true;
|
||
if (idPreview) {
|
||
// Collapse to small preview
|
||
var qualCheck = document.getElementById("dot-id-quality-check");
|
||
if (qualCheck) qualCheck.hidden = true;
|
||
idAcceptBtn.hidden = true;
|
||
idRetakeBtn.style.fontSize = "11px";
|
||
idRetakeBtn.textContent = "Change ID";
|
||
if (idImg) { idImg.style.maxWidth = "150px"; idImg.style.maxHeight = "100px"; }
|
||
}
|
||
});
|
||
|
||
// Retake button
|
||
idRetakeBtn?.addEventListener("click", function() {
|
||
window.__dotPhotoId = null;
|
||
idAccepted = false;
|
||
if (idInput) idInput.value = "";
|
||
// Stop webcam if running
|
||
if (webcamStream) { webcamStream.getTracks().forEach(function(t) { t.stop(); }); webcamStream = null; }
|
||
if (idWebcam) idWebcam.hidden = true;
|
||
if (idPreview) idPreview.hidden = true;
|
||
if (idUploadOpts) idUploadOpts.style.display = "";
|
||
// Reset quality check UI
|
||
var qualCheck = document.getElementById("dot-id-quality-check");
|
||
if (qualCheck) qualCheck.hidden = false;
|
||
if (idAcceptBtn) idAcceptBtn.hidden = false;
|
||
if (idRetakeBtn) { idRetakeBtn.style.fontSize = "13px"; idRetakeBtn.textContent = "Retake / Re-upload"; }
|
||
if (idImg) { idImg.style.maxWidth = "320px"; idImg.style.maxHeight = "240px"; }
|
||
});
|
||
|
||
idInput?.addEventListener("change", function() {
|
||
handleIdFile(idInput.files?.[0]);
|
||
});
|
||
|
||
// (webcam capture handled above via getUserMedia)
|
||
|
||
} // end guard
|
||
</script>
|