URL.createObjectURL on a PDF creates a blob URL that cannot render in
an img tag, showing broken image alt text. Skip preview creation for
PDF files so the review modal displays without a thumbnail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 5s cloud timeout was too tight for the initial WIF authentication
which requires 3 HTTP round-trips (STS, IAM credentials, resource
manager). First call took 5.5s and was discarded, falling back to slow
CPU-based PaddleOCR. Increased to 10s to accommodate cold-start auth.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pdf2image requires poppler-utils which is not installed in the OCR
container. PyMuPDF is already in requirements.txt and can render PDF
pages to PNG at 300 DPI natively without extra system dependencies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The backend SUPPORTED_IMAGE_TYPES set excluded application/pdf, returning
415 before the request ever reached the OCR microservice. Added PDF to
the allowed types in both controller and service validation layers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The receipt extractor only accepted image MIME types, rejecting PDFs at
the OCR layer. Added application/pdf to supported types and PDF-to-image
conversion (first page at 300 DPI) before OCR preprocessing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ReceiptCameraButton with "Add Receipt" button that opens
AddReceiptDialog. Upload path feeds handleCaptureImage, camera path
calls startCapture. Tier gating preserved.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
URL-to-screen sync on mount and screen-to-URL sync via replaceState
enable direct URL navigation, page refresh, and bookmarks on mobile.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create shared getVehicleLabel/getVehicleSubtitle in core/utils with
VehicleLike interface. Replace all direct year/make/model concatenation
across 17 consumer files to prevent null values in vehicle names.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: Add authenticated endpoints for pending association CRUD
(GET/POST/DELETE /api/email-ingestion/pending). Service methods for
resolving (creates fuel/maintenance record) and dismissing associations.
Frontend: New email-ingestion feature with types, API client, hooks,
PendingAssociationBanner (dashboard), PendingAssociationList, and
ResolveAssociationDialog. Mobile-first responsive with 44px touch
targets and full-screen dialogs on small screens.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract all notification logic from EmailIngestionService into
dedicated EmailIngestionNotificationHandler class
- Add notification_logs entries for every email sent (success/failure)
- Add in-app user_notifications for all error scenarios (no vehicles,
no attachments, OCR failure, processing failure)
- Update email templates with enhanced variables: merchantName,
totalAmount, date, guidance
- Update pending vehicle notification title to 'Vehicle Selection Required'
- Add sample variables for receipt templates in test email flow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New ReceiptClassifier module with keyword-based classification for
fuel vs maintenance receipts from email text and OCR raw text
- Classifier-first pipeline: classify from email subject/body keywords
before falling back to OCR-based classification
- Fuel keywords: gas, fuel, gallons, octane, pump, diesel, unleaded,
shell, chevron, exxon, bp
- Maintenance keywords: oil change, brake, alignment, tire, rotation,
inspection, labor, parts, service, repair, transmission, coolant
- Confident classification (>= 2 keyword matches) routes to specific
OCR endpoint; unclassified falls back to both endpoints + rawText
classification + field-count heuristic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add tier-gated "Scan Receipt" button to MaintenanceRecordForm
- Wire useMaintenanceReceiptOcr hook with CameraCapture and ReviewModal
- Auto-populate form fields from accepted OCR results via setValue
- Upload receipt as document and pass receiptDocumentId on record create
- Show receipt thumbnail + "View Receipt" button in edit dialog
- Add receipt indicator chip on records list rows
- Add receiptDocumentId and receiptDocument to frontend types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add useMaintenanceReceiptOcr hook mirroring fuel receipt OCR pattern,
MaintenanceReceiptReviewModal with confidence indicators and inline editing,
and maintenance-receipt.types.ts for extraction field types. Includes
category/subtype suggestion via keyword matching from service descriptions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add receipt_document_id FK on maintenance_records, update types/repo/service
to support receipt linking on create and return document metadata on GET.
Add OCR proxy endpoint POST /api/ocr/extract/maintenance-receipt with
tier gating (maintenance.receiptScan) through full chain: routes -> controller
-> service -> client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New MaintenanceReceiptExtractor: Gemini-primary extraction with regex
cross-validation for dates, amounts, and odometer readings
- New maintenance_receipt_validation.py: cross-validation patterns for
structured field confidence adjustment
- New POST /extract/maintenance-receipt endpoint reusing
ReceiptExtractionResponse model
- Per-field confidence scores (0.0-1.0) with Gemini base 0.85,
boosted/reduced by regex agreement
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EmailNotificationToggle used a custom button-based toggle that rendered
as a circle. Replaced with MUI Switch component to match the pill-style
toggles used on the SettingsPage throughout the app.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maintenance page called useMaintenanceRecords() without a vehicleId,
causing the schedules query (enabled: !!vehicleId) to never execute.
Added vehicle selector to both desktop and mobile pages, auto-selects
first vehicle, and passes selectedVehicleId to the hook. Also fixed
stale query invalidation keys in delete handlers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Handle poll errors including 410 Gone in useManualExtraction hook
- Add specific progress stage messages (Preparing/Processing/Mapping/Complete)
- Enforce 44px minimum touch targets on all interactive elements
- Add tests for inline editing, mobile fullscreen, and desktop modal layouts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add filename .pdf extension fallback and %PDF magic bytes validation to
extractManual controller. Update getJobStatus to return 410 Gone for
expired jobs. Add 16 unit tests covering all acceptance criteria.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>