Commit graph

89 commits

Author SHA1 Message Date
justin
ad3d189b2b post-completion flow: survey, referral program, review ask
- Migration 081: referral_codes, referral_uses, exit_surveys tables
- API: POST /api/v1/survey, POST /api/v1/referral/check, GET /api/v1/referral/:email
- Worker: completion_emails.py — sends completion + 24h follow-up (survey + referral)
- Survey page: /survey/?order=X&rating=N — star rating, feedback, Google review ask
- Referral: REF-FIRSTNAME codes, $25 credit per referred order, no limit
- Low ratings (1-3 stars) trigger Telegram alert for admin follow-up
- Cron: every 15 minutes
2026-05-30 21:22:14 -05:00
justin
08e80e11f9 add POST /api/v1/dot/name-check: parallel SOS + FMCSA name availability check
- Checks state SOS for entity name availability (via workers adapter)
- Checks FMCSA census for exact + fuzzy name matches
- Returns: sos_available, fmcsa_in_use, fmcsa_matches, combined 'available' flag
- 20s timeout on SOS lookup, FMCSA query is instant (local DB)
2026-05-30 21:02:32 -05:00
justin
58aa2cf78e map all FMCSA status codes to services: OOS→ETA ($499), Inactive→reactivation, Revoked/Suspended→reinstatement
- OOS (Out of Service): RED, urgent messaging, maps to Emergency Temporary Authority $499
- Inactive: YELLOW, reactivation $149
- Not Authorized: YELLOW, new USDOT + MC authority
- Revoked: RED, new authority application
- Suspended: YELLOW, compliance resolution + reinstatement
- Cancelled: YELLOW, new registration
- Each status has specific actionable messaging with PW CTA
2026-05-30 20:48:47 -05:00
justin
b106a88e90 add USDOT reactivation service ($149) + map inactive status to reactivation CTA
- New service: usdot-reactivation — filed via ask.fmcsa.dot.gov (sub-cat 302)
- Inactive carriers see 'PW can handle reactivation, no Login.gov needed'
- Compliance checker maps inactive operating status to reactivation service
2026-05-30 20:40:56 -05:00
justin
2b8e2cf7ea fix: inactive carriers show yellow warning instead of green, even if FMCSA says 'allowed' 2026-05-30 20:39:28 -05:00
justin
18fb45dba0 fix operating status: translate FMCSA status codes to human-readable labels (A=Active, I=Inactive, etc) 2026-05-30 20:38:22 -05:00
justin
a6c7d2aef8 increase SOS lookup timeout to 20s for slower state portals 2026-05-30 19:23:41 -05:00
justin
e8a98b1130 corporate check: live SOS lookup via state adapters for all 51 states
- Formal entities: queries workers /entity-status endpoint for real-time
  Secretary of State status (ACTIVE/dissolved/revoked/delinquent)
- Green if active, red if not active, yellow if not found or lookup failed
- Sole proprietors: yellow 'form an LLC' upsell
- 12s timeout so compliance check doesn't hang on slow state portals
2026-05-30 19:21:06 -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
e40f359693 fix photo upload: add synchronous /jobs/presign and /jobs/minio-upload endpoints to workers 2026-05-30 19:13:51 -05:00
justin
592b9875da fix TS build: use rawDot not dotNumber in pending filing query 2026-05-30 19:03:15 -05:00
justin
ffc5a16b5c add corporate compliance check (#15) to DOT checker + annual report/RA/reinstatement services
- Check 15: detects LLC/Inc/Corp/LTD/LP in entity name, shows yellow warning
  about annual reports, franchise tax, and registered agent requirements
- New services: annual-report-filing (49), registered-agent (9/yr),
  entity-reinstatement (99)
- Upsell opportunity: 1.31M formal entities in FMCSA database
2026-05-30 19:02:37 -05:00
justin
1f1113d63c add fax filing pipeline: VitalPBX sender, attestation cover page with digital signature, compliance checker pending filing override
- filing_attestation.py: generates cover page attesting PW submitted document
  to recipient with date/time stamp, contact info, and digital signature
- fax_sender.py: sends PDFs via VitalPBX API, polls for delivery, generates
  attested copy for customer records
- dot-lookup.ts: if DOT has pending MCS-150 order, show green 'UPDATE SUBMITTED'
  instead of red 'OVERDUE' in compliance checker
- requirements.txt: add pyhanko + cryptography for PDF digital signatures
2026-05-30 18:32:01 -05:00
justin
e0fc4810d1 show proper error on phone when photo ID upload fails to MinIO 2026-05-30 18:13:18 -05:00
justin
7ef509c247 fix photo ID upload: use workers for MinIO storage + public presigned URLs
- id-upload.ts: replace broken direct minio import with workers presign/upload
- job_server.py: add minio-upload handler for API to store files via workers
- rewrite presigned URLs from internal minio:9000 to public minio.performancewest.net
- fixes: thumbnail not showing after phone upload, base64 fallback storage
2026-05-30 18:12:06 -05:00
justin
f60c5229ab Fix mobile photo upload: resize large camera images + increase body limit
Mobile cameras produce 8-12MB photos. Now:
- Canvas-based resize to max 2000x1500 before upload
- JPEG compression at 0.7-0.85 quality
- Express body limit increased to 5MB for id-upload route
- Falls back to raw upload for small images and PDFs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 17:24:16 -05:00
justin
6b874ea72b Add ts-nocheck to id-upload.ts (minio optional dep)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:42:41 -05:00
justin
0ba8730487 Add Telegram notifications for tickets, quotes, and insurance leads
Tickets: 📩 for support, 🏥 for insurance leads, 💰 for quotes
Quotes: 💰 with name, email, company, service, details
All fire-and-forget to Telegram bot — non-blocking.

Previously these only went to ERPNext with no real-time alert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 16:35:11 -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
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
6f3ad1b686 Customer dashboard: order tracking + portal login
- New page: /portal/dashboard/ — customer can view all orders
- Auth: cookie-based login, shows auth modal if not logged in
- Orders grouped by batch, filtered by DOT/FCC tabs
- Shows service name, amount, discount, status badge, payment method
- Portal API: /api/v1/portal/me now returns compliance_orders

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 14:20:58 -05:00
justin
e82aa0b8c2 Fix PayPal capture for compliance orders + MCS-150 form generator
PayPal capture was defaulting to canada_crtc_orders table for all
non-formation orders. Now properly routes compliance_batch orders
to compliance_orders table with batch_id lookup. Also infers
order type from ID prefix (CB-=batch, CO-=compliance, FO-=formation).

MCS-150 form generator: produces DOCX with fax cover sheet + filled
MCS-150 form for faxing to FMCSA at 202-366-3477.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-30 11:34:16 -05:00
justin
c47c52e9e8 Remove external action_url links from compliance checker
Don't send users to FMCSA portal or state agency sites — keep them
on our site to order services through us. Removed all action_url
from API responses and "Fix this" / "Learn more" links from frontend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 16:16:44 -05:00
justin
da00817174 Show DOT# or FRN in Telegram order notifications
Reads dot_number or frn from intake_data and includes in the
notification. DOT orders show DOT#, FCC orders show FRN.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 15:55:41 -05:00
justin
9579d74d7d Fix Telegram notification: show actual paid amount after discount
Was showing service_fee_cents ($69) instead of actual charge ($35.54).
Now subtracts discount_cents and adds surcharge_cents. Also shows
discount line in notification when a promo code was used.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 15:51:34 -05:00
justin
4e7493b088 Mark MC Authority as non-discountable ($300 FMCSA gov fee)
Non-discountable services: BOC-3 ($25 vendor), D&A (~$100 provider),
MC Authority ($300 gov fee). All other DOT services are pure labor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 15:02:31 -05:00
justin
79fb4722f1 Mark BOC-3 as non-discountable (passthrough cost to Process Agent LLC)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 15:00:46 -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
4aff121c0b Fix interstate detection: use carrier_operation code A, not text match
FMCSA census carrier_operation is single-letter: A=Interstate,
B=Intrastate Hazmat, C=Intrastate Non-Hazmat. Previous code searched
for "interstate" in text which never matched. Now 22,089 interstate
carriers will be properly flagged for IRP/IFTA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 14:01:18 -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
3d4c72f259 Insurance referral on order page + fix MCS-150 date display
- Order page: insurance referral checkbox (pre-checked) shown when
  ?ins=1 from checker or carrier has insurance gap. Flag stored
  in intake_data.insurance_referral_requested.
- Checker CTA passes &ins=1 when insurance issues found.
- MCS-150: use mcs150Outdated=N from FMCSA API to show green even
  without exact date. Fixes "Filing date not available" for carriers
  not in local census.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-29 00:12:28 -05:00
justin
a471f26bb9 Fix 4 bugs from trucking code review
1. Insurance "on file" check: undefined !== null was true, falsely
   showing green. Changed to !!field && field !== "0".
2. Insurance lead ticket: filtered for c.id === "insurance" but
   actual IDs are insurance_bipd/cargo/bond. Fixed to match prefix.
3. Bundle pricing: was $499 for $376 of services (MORE than
   individual). Now includes Safety Audit Prep ($399), making
   individual total $775 and bundle saves $276.
4. Order page submit button: inline styles for visibility
   (bg-orange-500 not in Astro CSS).

UCR $46 gov fee confirmed correct for 2026 (fees stayed flat).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 23:55:19 -05:00
justin
54d3ec6fb2 Fix: add type assertion for FMCSA API response
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 23:51:27 -05:00
justin
e8f453e14b Fix: separate insurance checks by type + inline styles for visibility
API: Split "Insurance Filing" into separate checks:
- Liability Insurance (BIPD) — BMC-91/91X
- Cargo Insurance — household goods
- Broker Bond / Trust Fund — BMC-84/85 ($75K minimum)
Each has its own clear label and specific remediation detail.

Frontend: Convert CTA box, insurance lead capture, and "looking good"
box from Tailwind classes to inline styles (Tailwind classes not in
Astro compiled CSS for static public/ files).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 23:47:29 -05:00
justin
068739cb20 Add FMCSA API fallback for name search when local DB is empty
Tries local fmcsa_carriers table first (ILIKE partial match).
If no results, falls back to FMCSA QCMobile API name search.
Ensures name search works even before full 2M census is loaded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 23:37:45 -05:00
justin
dddfd53cd3 Update DOT service pricing per competitive research
MCS-150: $69, BOC-3: $89, UCR: $69 + gov fee ($46 min),
New USDOT: $89, MC Authority: $349 + $300 gov fee,
D&A Program: $149/yr, Audit Prep: $399, Full Bundle: $499.

Positioned below market on entry-level services (MCS-150 $69 vs
$99-200 competitors) to win first orders and upsell.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 22:57:43 -05:00
justin
8149996107 Add 8 DOT/FMCSA services to catalog and handler registry
Service catalog (compliance-orders.ts):
- mcs150-update: $79 (MCS-150 biennial update)
- boc3-filing: $149 (BOC-3 process agent)
- ucr-registration: $79 + $59 gov fee (UCR annual)
- dot-registration: $149 (new USDOT number)
- mc-authority: $499 + $300 gov fee (operating authority)
- dot-drug-alcohol: $199 (D&A compliance program)
- dot-audit-prep: $399 (new entrant safety audit prep)
- dot-full-compliance: $499 (bundle)

Handler registry (__init__.py):
- MCS150UpdateHandler for admin-assisted filings
- BOC3FilingHandler for process agent designations
- Other DOT services use MCS150 handler pattern (admin todo)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 22:43:57 -05:00
justin
b0f518b1c4 Fix MCS-150 date display format (ISO date only)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 21:13:50 -05:00
justin
c40d85b343 Fix: named import for pool from db module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 21:13:05 -05:00
justin
46069b07a0 Add DOT/FMCSA compliance checker API
GET /api/v1/dot/lookup?dot=XXXXXX — live compliance check combining
local census data + FMCSA QCMobile API. Checks:
- Operating status (allowed to operate Y/N)
- MCS-150 biennial update (overdue detection)
- Insurance filing (BIPD, cargo, bond)
- Safety rating (S/C/U)
- Operating authority status
- Out-of-service rates vs national average
- Crash record

GET /api/v1/dot/search?name=Acme — name search against local census

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 21:11:46 -05:00
justin
dfee4fc6c0 Add FMCSA motor carrier census table and Socrata data downloader
New vertical: FMCSA/DOT motor carrier compliance services.
- Migration 078: fmcsa_carriers table with 31 fields (DOT#, name,
  email, phone, address, fleet size, MCS-150 date, carrier type)
- Downloader: Socrata API ingest for 2M+ carriers with upsert
- Data source: data.transportation.gov (free, public)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-28 21:05:46 -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
de50971fb3 Add intake form links to compliance order confirmation email
The confirmation email now includes a prominent blue box with
direct links to the intake form for each ordered service. Subject
changed to "Action Required" to prompt the customer to complete it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:26:33 -05:00
justin
5cb360acba Fix: add allowed_emails to DiscountCode TypeScript interface
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:19:12 -05:00
justin
6ed5105ae5 Send intake email for $0 free orders
Free orders bypass Stripe, so the Stripe webhook never fires and the
intake/confirmation email never gets sent. Now trigger
sendComplianceIntakeEmail directly in the $0 bypass flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:18:19 -05:00
justin
b81e102d39 Validate allowed_emails on discount code lookup
The /api/v1/discount/:code endpoint now checks allowed_emails when
an email is provided. If the email isn't in the allowed list, returns
valid:false so the frontend doesn't show a fake discount. The promo
field is cleared and unlocked if validation fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:08:51 -05:00
justin
7bb08f3493 Handle already-paid orders gracefully in checkout
When create-session is called for an order that's already paid (e.g.
free order with page refresh, duplicate submit, or browser retry),
return a success redirect instead of 404. Prevents confusing
"Payment Not Confirmed" errors on the success page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 01:07:27 -05:00
justin
b91e76e44c Fix: FCC errors now fall through to local DB fallback
Was returning 502 early instead of throwing to trigger the catch block
where the local fcc_499_filers fallback lives.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:42:43 -05:00
justin
bbfa6393fa Fall back to local DB when FCC name search is blocked
FCC/Akamai is blocking our server IP (403). Name search now falls back
to querying the local fcc_499_filers table (20K+ records) when the
live FCC search fails.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-22 00:41:47 -05:00