Commit graph

101 commits

Author SHA1 Message Date
justin
479f3dfc45 add entity upgrade bundle service + deploy completion/IMAP crons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 22:12:11 -05:00
justin
e2313bcc5e add blur detection + Ollama ID validation + corporate check for all carriers
- Client-side: Laplacian variance blur detection in photo quality check
  (very blurry / somewhat blurry / acceptable / good)
- Server-side: async Ollama vision model validates uploaded image is a
  real government ID (minicpm-v:8b), flags non-ID uploads
- Corporate check: sole proprietors now get yellow 'form an LLC' upsell,
  formal entities get annual report/RA reminder
2026-05-30 19:17:31 -05:00
justin
ca7af40ceb update photo ID instructions: suggest QR code first for desktop users 2026-05-30 19:07:13 -05:00
justin
e2c7cc582b increase font sizes in photo ID section for readability (min 14px) 2026-05-30 18:15:22 -05:00
justin
04861ccfe0 Fix QR code: add inline script right at image element for immediate generation
Previous approach relied on the main is:inline script block which
could be blocked by FCC step crashes. New approach: tiny self-contained
script right next to the QR img element, runs immediately, fetches
upload token and generates QR. Falls back to page URL on failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 17:20:04 -05:00
justin
66bd1306e3 Accept TIFF images for photo ID upload (scanner default format)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:28:22 -05:00
justin
d2f9e642d4 Hide QR code on mobile phones, auto-trigger camera on mobile
QR code hidden via CSS on screens < 640px (phones). On mobile,
file input gets capture=environment so tapping the button opens
the camera directly. Tablets still show QR code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:25:52 -05:00
justin
1535acb413 Complete phone-to-desktop photo ID upload pipeline
- API: POST /api/v1/id-upload/token generates upload token
- API: POST /api/v1/id-upload/:token receives base64 image, stores in MinIO
- API: GET /api/v1/id-upload/:token/status returns upload status + thumbnail
- Mobile page: sends image as base64 with upload_token
- Desktop intake: requests token, generates QR with upload URL, polls
  every 3s for phone upload, auto-shows thumbnail when detected
- MinIO storage with presigned URLs for thumbnails
- Compliance order intake_data updated with photo_id_uploaded flag

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:24:35 -05:00
justin
daf6d1f831 Always show QR code for phone photo upload — no click to reveal
QR code displayed inline below the upload button so truckers can
immediately scan with their phone to take a photo of their ID.
Clear instructions: 'Scan with your phone camera to take a photo'

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:17:17 -05:00
justin
73a10d4a0f Simplify photo ID upload: clear instructions for truckers
- 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>
2026-05-30 16:11:49 -05:00
justin
3423b4914a Relabel photo ID buttons: Upload File/Scan + Use Webcam + scanner instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:09:47 -05:00
justin
39fb1c9998 Webcam capture for photo ID via getUserMedia
Desktop users can now use their webcam to photograph their ID:
- Click "Use Camera" → browser requests webcam permission
- Live video preview with orange guide rectangle for ID placement
- Capture button takes high-res JPEG (1280x720)
- Cancel button stops webcam and returns to upload options
- Captured image goes through same quality check flow
- Works on Chrome, Firefox, Edge, Safari (desktop + mobile)
- No libraries needed — native WebRTC API

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:09:02 -05:00
justin
c40dfb552e Auto-save intake data to server after every step
Intake data now persists to DB after each step completion (non-blocking).
If browser crashes, data is recoverable from compliance_orders.intake_data.

Partial saves (_partial: true) only update intake_data without changing
payment_status or marking intake_data_validated. Final submit still
triggers the full validation + worker dispatch flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:01:20 -05:00
justin
beb23d777e Photo ID quality check after upload
Shows uploaded image at larger size with automated quality checks:
- File size (too small = low quality warning)
- File type validation (JPEG, PNG, PDF, HEIC)
- Resolution check (minimum 400x250 for readable text)
- Aspect ratio check (should look like an ID card)

Green checkmark for passing checks, red X for issues.
Yellow warning box for quality problems with specific guidance.
Accept & Continue button to confirm, Retake to re-upload.
After accept, collapses to small preview with "Change ID" option.

Front of ID only (sufficient for FMCSA MCS-150 filing).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:59:27 -05:00
justin
e8769e4d5d Photo ID upload: add QR code for phone + scanner device support
Three upload methods:
- Upload File: standard file picker
- Camera / Scanner: uses capture attribute for camera on mobile
  or TWAIN/WIA scanner devices on desktop
- QR Code: generates QR with current page URL so user can scan
  with phone and take a photo of their ID on mobile

QR generated via api.qrserver.com (no library dependency).
Remove button restores all upload options.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:56:00 -05:00
justin
b8a52303b3 DOT intake: use is:inline script to avoid hoisted bundle crash
FCC step scripts crash on DOT pages due to missing elements. By
using is:inline, DOT intake script runs independently, not in the
hoisted bundle. Stripped TypeScript annotations for plain JS compat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:38:04 -05:00
justin
db96af53bf Guard DOT intake script — skip on non-DOT pages
Wraps entire script in element existence check to prevent running
on FCC pages where other step scripts crash from missing elements.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:35:52 -05:00
justin
989ccaec93 Fix MCS150Step null crash — was breaking ALL DOT intake pages
MCS150Step script compiled into hoisted JS for all order pages.
Non-null assertions on photo ID elements crashed on non-MCS150 pages,
preventing DOTIntakeStep's showRelevantSections from running.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:33:56 -05:00
justin
c98ac9ae54 Fix DOT intake crash: null-safe photo ID element refs
Script crashed on 'Cannot read properties of null' because photo ID
elements are inside a hidden section. All element refs now use
optional chaining instead of non-null assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:31:15 -05:00
justin
d55384975e Fix DOT intake: retry init until wizard found + null-safe PWIntake
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:29:42 -05:00
justin
7fe3311921 Fix DOT intake: wait for DOMContentLoaded before showing sections
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:27:39 -05:00
justin
897554fa4e Fix DOT intake: hide all sections by default, show on load
Sections were visible by default in HTML. Now all hidden, then
showRelevantSections() runs immediately + on pw:step-shown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:26:41 -05:00
justin
ee4ea70beb Fix DOT intake section visibility — read slug from wizard data attribute
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:25:02 -05:00
justin
8ce9e2e118 Unified DOT intake form — one form for all trucking services
Single DOTIntakeStep shows/hides sections based on services ordered:
- Company info + address + signer (always)
- Entity & operations (MCS-150, USDOT, MC Auth, bundles)
- Fleet info (MCS-150, UCR, bundles)
- UCR fleet bracket + base state (UCR)
- Cargo types (MCS-150, bundles)
- D&A program (CDL drivers, DER, consortium)
- BOC-3 docket info (BOC-3)
- Photo ID upload (MCS-150, MC Auth)
- Security/encryption notices

All DOT services now use ["dot-intake", "review"] instead of ["review"].

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 15:23:48 -05:00
justin
b59b266a80 MCS-150 intake: add encryption notice, EIN field, photo ID upload
- Security notice: SSL encryption, encrypted at rest, no third-party sharing
- EIN field added (required for MCS-150 form field 19)
- Photo ID upload with camera capture on mobile
- ID auto-deleted after filing processed
- Preview with remove button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 13:28:43 -05:00
justin
d2b42cd4b8 Add MCS-150 intake form for biennial update orders
Collects all fields needed for FMCSA Form MCS-150:
- Legal name, DBA, DOT#, MC#
- Principal business address
- Entity type, carrier operation, interstate/intrastate
- Fleet info (power units, drivers, annual miles)
- 29 cargo type checkboxes
- Authorized signer name and title

Filed via fax to FMCSA at 202-366-3477 (VitalPBX).
Previously was review-only with no data collection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 11:29:05 -05:00
justin
78ed1db15a Recalculate bundle pricing + bundle auto-uncheck individual items
- DOT Full Compliance Bundle: $499 → $399 (saves $376 vs $775 individual)
- State Compliance Bundle: $599 → $499 (saves $297 vs $796 individual)
- D&A marked non-discountable (passthrough cost to testing provider)
- Order page: selecting a bundle auto-unchecks its individual components
  via data-bundle attribute listing component slugs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 14:49:59 -05:00
justin
f1027694a3 Add interest field to mailing list subscribe (telecom/trucking/formation)
- Footer subscribe modal: new "I'm interested in" dropdown with 3 options
- Hoisted JS: reads interest field, validates selection, passes to API
- Subscribe API: routes to different Listmonk lists by interest
  (telecom→list 3, trucking→list 8, formation→list 9)
- Interest stored as subscriber attribute for campaign segmentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 12:50:27 -05:00
justin
33da00fd89 50-state trucking compliance: services, checker, order page, CA landing
- Migration 079: state_trucking_requirements table seeded for all 51 jurisdictions
  (IRP, IFTA, weight-distance taxes, MCP/CARB, intrastate authority, state DOT)
- Migration 080: carrier_operating_states tracking table
- 13 new state trucking services in catalog ($99-$599)
- StateTruckingHandler with state-specific admin todos
- DOT compliance checker: 7 new state-level checks (IRP, IFTA, weight tax,
  MCP/CARB, emissions, intrastate authority, state DOT number)
- New API endpoint: GET /api/v1/dot/state-requirements
- DOT order page: state compliance service cards with auto-preselect
- California trucking landing page (MCP + CARB + IRP + IFTA)
- Fix: DOT checker nav missing Trucking/DOT section
- Fix: All 8 DOT intake pages missing style block (dangling text)
- Fix: DOT confirmation email now says "Order Confirmed" not "Action Required"
- Fix: MCS150/BOC3/StateTrucking handlers missing async process() method
- Fix: StateTruckingHandler connection leak + slug resolution

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 12:46:33 -05:00
justin
c80f3a408a Fix: generic review step text (was FCC/USAC specific)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 00:49:18 -05:00
justin
e67df3c4c3 DOT intake: review-only (no telecom entity step), email verifier,
updated flagger excluding 4+ year stale carriers

- Intake manifest: DOT services use ["review"] only, skipping the
  telecom entity step with FRN/USAC fields
- Flagger: excludes 4+ year overdue carriers from campaign (spam
  trap risk). 18,277 safe targets from 100K records.
- Email verifier: self-hosted MX + SMTP verification tool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 00:29:28 -05:00
justin
aad45dc827 Add DOT service intake pages and manifest entries
8 new Astro intake pages for DOT services:
mcs150-update, boc3-filing, ucr-registration, dot-registration,
mc-authority, dot-drug-alcohol, dot-audit-prep, dot-full-compliance.

All use entity + review wizard steps (admin-assisted services).
Added to INTAKE_MANIFEST and SERVICE_META with correct pricing.

Fixes 404 on /order/mcs150-update?order=CO-xxx from confirmation emails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 00:02:47 -05:00
justin
69b70ee96d Add Trucking/DOT to site-wide nav and footer
Nav dropdown: Trucking/DOT section with DOT Compliance Services,
MCS-150/BOC-3/UCR link, and DOT Compliance Check [FREE] badge.
Added to both desktop mega-dropdown and mobile menu.

Footer: DOT/Trucking in services list, DOT Compliance Check in
free tools list.

This updates all Astro-compiled pages site-wide (nav partial).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 23:12:27 -05:00
justin
d4c4ae003e Fix 6 bugs from code review
Critical:
- Single-order discount used wrong column names (discount_pct/discount_flat_cents
  → discount_type/discount_value). Discounts were silently $0.
- Single-order discount skipped allowed_emails and expires_at checks
- Free orders now set paid_at = NOW()

High:
- Discount usage now tracked in discount_usage table + current_uses incremented
- Flat discount only replaces bundle when flat >= bundle (was always replacing)

Minor:
- Removed unused CDR profile fetch in EntityStep

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 02:16:38 -05:00
justin
4125b0f09f Auto-fill entity from FRN in order intake_data, not just URL param
When intake loads from ?order=CO-xxx, the FRN is in the order's
intake_data, not the URL. Now checks state.intake_data.frn and
state.entity.frn as fallback sources for auto-fill.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:53:44 -05:00
justin
03c72a2525 Pre-fill intake form from order data via ?order=CO-xxx
When the intake page is loaded with ?order=CO-xxx (from the
confirmation email), fetch the order and pre-fill customer name,
email, and FRN from the order record. Previously only worked
with JWT token-based links.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:51:12 -05:00
justin
2316168072 Clarify email field is not shared with FCC
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:49:53 -05:00
justin
d62a858098 Clarify USAC Filer ID: 6 digits starting with 8, with example and link
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:48:47 -05:00
justin
0cb6e20021 Clarify intake form labels: name, email, carrier entity
- "Email address (yours)" → "Your email address" with helper text
- "Your name" → "Your first and last name" with placeholder
- "Carrier legal name" → "Carrier entity legal name" with examples

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:47:52 -05:00
justin
fdbed5c097 Pre-fill email from URL on checkout and compliance checker
Email passes through the full funnel: campaign email (?email=) →
compliance checker → order page. Reduces friction for campaign
recipients who would otherwise have to type their email manually.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:49:28 -05:00
justin
868b0eeca5 Pass promo code through compliance checker to order page
- Compliance checker reads ?code= from URL, stores it, passes it
  through to the order page CTA link
- Allows email campaigns to link to checker with coupon pre-applied

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-21 15:16:27 -05:00
justin
5a07335b2f Fix: remove escaped backticks that broke compliance checker JS
The lead capture template literal used \` (escaped backtick) which
passed through Astro's compiler literally, creating an invalid JS
token that prevented the entire script from executing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 16:18:30 -05:00
justin
397e006321 Fix: remove remaining TypeScript assertions from inline script
as HTMLInputElement and as HTMLElement leaked into compiled JS,
causing SyntaxError in the lead capture handler block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 16:06:33 -05:00
justin
b9f27e939a Fix: remove TypeScript annotation from inline script (broke compliance checker)
Record<string, string> type annotation leaked into compiled JS output,
causing SyntaxError that silently broke the entire script block.
runCheck() and all event handlers stopped working.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-20 16:02:42 -05:00
justin
54bdb9d480 Add sticky CTA bar, penalty warnings, lead capture to compliance checker
- Sticky bar fixed to bottom: "N issues found — your filing needs to be
  corrected" with "See How to Fix This" + "Call Us" buttons
- Per-filing penalty warnings on red/yellow cards: RMD removal from network,
  CPNI $239K/violation, 499-A Red Light blocks all FCC apps, CALEA $10K/day,
  CORES can't legally operate
- CTA copy changed: "Your filing is inaccurate" urgency box + "Fix My Filing"
  button with "no commitment until you pay" reassurance
- Lead capture form: "Get your compliance report emailed" for people not
  ready to buy — creates ticket + tracks lead-capture event in Umami

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-13 12:29:47 -05:00
justin
0ef07e25b4 Remove prices from compliance checker service selection
Hide individual service prices and total/discount row from the
compliance checker results. Users see service names + checkboxes
only. Prices revealed on the order page after clicking Get Started.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-09 11:05:24 -05:00
justin
c127cdd908 Improve compliance checker UX + add search logging
- Loading message: shows estimated time (30-90 seconds) + rotating status
  updates (RMD, CPNI, USAC, BDC, STIR/SHAKEN, compiling report)
- Timeout increased to 90s (was 60s)
- Error messages: "try again in a few minutes" (not "moments" or "check internet")
- New compliance_check_log table: logs every FCC lookup with FRN, entity,
  IP, user agent, referrer, issue count, severity, response time
- Enables conversion funnel analysis and follow-up

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-06 22:37:29 -05:00
justin
bd5193e45f Add Umami custom event tracking across all key pages
- Created /js/pw-analytics.js with conversion funnel events
- Added to Base.astro layout (all Astro pages) + 6 static HTML pages
- Events tracked: compliance-check-start, compliance-check-complete,
  order-cta-click, checkout-page-view, checkout-start, esign-opened,
  esign-submitted, campaign-click (UTM attribution), contact-form-submit
- Server-side payment-complete event from checkout webhook via Umami API
- Auto-tracks any element with data-track="event-name" attribute

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-05 05:10:13 -05:00
justin
7d783bdb39 Reprice services: à la carte ~$2,000 vs FCC Carrier Reg bundle $1,299
Individual services repriced so buying separately costs ~$2K, making
the $1,299 FCC Carrier Registration a clear 35% savings:

- CORES/FRN: $99 → $149
- Form 499 Initial: $299 → $349
- D.C. Registered Agent: $99 → $149/yr
- RMD: $219 → $249 (+ $100 FCC fee)
- CPNI: $149 → $199
- CALEA SSI: $299 → $799 (includes consulting work)
- BDC Broadband: $199 → $249
- BDC Voice: $149 → $199
- BDC Both: $299 → $349

Retired New Carrier Bundle ($1,799) — redirected to FCC Carrier
Registration ($1,299) which includes more services and the wizard.

Updated prices in: API catalog, batch order page, compliance checker CTA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 06:23:19 -05:00
justin
16b5c2da0b Fix BDC pricing in compliance checker CTA
- BDC both (broadband+voice): $299 (was $349, now matches bdc-filing API price)
- BDC broadband-only: $199 (uses bdc_broadband slug)
- BDC voice-only: $149 (uses bdc_voice slug — was showing $199)
- Added _bdcVariant tracking so CTA uses correct slug per user answer
- Reset variant on undo

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-04 06:17:06 -05:00