206 Commits

Author SHA1 Message Date
shawn 874e15c01e Add disney_group grouped park filter categories
- New disney_group query param on /api/assets (park, resort,
  office, springs, other)
- Both list and map filter dropdowns show group options above
  individual parks
- Backend uses IN queries for grouped park values
- Map view handles groups via client-side PARK_GROUP_MAP

Closes #69
2026-06-03 00:57:55 -04:00
shawn c7ebef7cc1 docs: update FIELD_MAP — GPS policy: never from seed/MSFS/Cantaloupe 2026-06-02 22:56:20 -04:00
shawn 27cfa6d68e docs: add FIELD_MAP.md — complete field origin reference for all pipelines 2026-06-02 22:51:05 -04:00
shawn b8ea7374e4 feat: local ZXing fallback, Register New Asset button, OCR serial search
Three fixes:
1. Barcode scanner: load ZXing locally with CDN fallback so it works
   even when CDN is unreachable.
2. Scan result: add 'Register' button to create a new asset from a
   scanned machine_id — works for both found and not-found results.
3. OCR serial search: when extracted machine_id isn't found, also try
   searching all numeric strings from OCR text as serial numbers; add
   'Register as New Asset' button on OCR not-found result.
2026-06-02 19:08:58 -04:00
shawn 95a368163a fix: remove duplicate doAutoCheckin call from handleBarcode
showScannedAsset (called on the very next line) already triggers the
auto-checkin. handleBarcode was calling doAutoCheckin immediately before
showScannedAsset, resulting in two check-in records per barcode scan.
2026-06-02 18:50:57 -04:00
shawn 52f801675e fix: replace AbortController with Promise.race to avoid SW hanging fetch bug
Root cause: AbortController signals don't properly propagate through
Service Worker fetch handlers (known Chrome/Safari bug), causing the
fetch to hang indefinitely on mobile browsers instead of timing out.

Fix: replace AbortController-based timeout in api() with Promise.race
against a simple setTimeout. Also:
- Stop all MediaStream tracks explicitly in stopScanning()
- Add 200ms yield point before API call for hardware release
- Bypass SW for API requests entirely (no caching needed for lookups)
- Bump SW cache to v11
- Add outer try/catch safety net in handleBarcode
2026-06-02 18:43:17 -04:00
shawn dd63dc97d5 feat: register service worker in index.html so PWA cache busting works 2026-06-02 18:34:01 -04:00
shawn c8ffab2c06 feat: connect-id suffix matching for OCR + bump SW cache to v10 2026-06-02 18:32:41 -04:00
shawn 3fa76cc74b Fix barcode scan hanging: stop camera before API call + add 15s fetch timeout
- Stop camera scanner (stopScanning) BEFORE making the API call in
  handleBarcode — active camera stream on mobile browsers can throttle
  concurrent fetch requests, causing the lookup to hang indefinitely
- Add 15s AbortController timeout to the api() wrapper so any network
  issue shows 'Request timed out — check your connection' instead of
  hanging forever on 'looking up...'
- Both fixes together ensure the barcode lookup never hangs
2026-06-02 18:24:31 -04:00
shawn 3859d9d555 fix: barcode scanner — hints not applied, per-frame errors killing scanner
Root cause #1: BrowserMultiFormatReader constructor takes 0 args so hints
and timing options (passed as args) were silently ignored. Hints are now
applied as reader.reader.hints, and timing as instance properties.

Root cause #2: ZXing per-frame 'no barcode' exceptions have message=undefined
causing .toLowerCase() to crash in old message-text filter. Switched to
instanceof checks against NotFoundException, ChecksumException, FormatException
which properly distinguishes normal 'no barcode' frames from real errors.

Tested in browser — hint setting and error filtering both verified working.
2026-06-02 18:09:03 -04:00
shawn 2c3ae1de22 feat: add Scan New button to OCR matched-assets result view
When OCR detects serial numbers matching assets in the DB but no
machine_id pattern (XXXXX-XXXXXX) is found, the result now shows:
- 'Asset found via serial/ID match' heading
- Matched assets with name, serial_number, machine_id, matched_on
- View Details button per matched asset
- Scan New + Retake buttons at the bottom

When machine_id IS found AND matched_assets also has entries, the
matched assets are shown as supplementary info below the main result.

The OCR vision prompt already asked for 'serial numbers' (no change
needed on server side).
2026-06-02 18:05:54 -04:00
shawn dd4744df05 Scan/OCR improvements: Scan New Asset button, barcode scanner fix, serial number OCR search
- Add 'Scan New Asset' button after scanning/OCR'ing an existing asset
  in both barcode and OCR modes (with resetBarcodeScanner/resetOcrScanner)
- Show scan results in-place instead of navigating away from Find Asset tab
- Fix barcode scanner: remove TRY_HARDER hint (too slow on mobile), reduce
  from 13 formats to 7 focused 1D formats, add ZXing library load check
- Extend camera fail timeout from 5s to 10s for slow mobile browsers
- Show matched_assets from OCR serial number cross-reference even when
  no machine_id pattern is detected (previously showed 'No machine ID')
- Display serial/ID DB matches alongside machine_id results when both exist
- Add null-safety to all new reset functions
2026-06-02 18:05:24 -04:00
shawn 1c63ec231f feat: add Scan New Asset buttons to barcode and OCR results
- handleBarcode now shows result in-place via showScannedAsset
  instead of navigating to Assets tab
- showScannedAsset gets a 'Scan New' button that calls
  resetBarcodeScanner()
- resetBarcodeScanner(): clears scan result, restarts barcode camera
- lookupOcrAsset gets a 'Scan New' button that calls
  resetOcrScanner()
- resetOcrScanner(): clears OCR result, restarts OCR camera
- Retake still works for re-capturing the same sticker
2026-06-02 18:03:16 -04:00
shawn e59b0fd6b6 fix: activity log now shows machine_id/name, 164 Disney assets updated with park data, WO date range defaults to week 2026-06-01 23:47:30 -04:00
shawn 1e7576e7d0 chore: bump SW cache to v9 2026-06-01 23:28:45 -04:00
shawn e3295afe73 fix: show GPS coords on map popups + asset list; toast on auto-checkin 2026-06-01 23:28:37 -04:00
shawn 120f52a29a chore: bump service worker cache to v8 to force refresh
Old cached index.html was likely serving stale JS on mobile devices.
v8 purge ensures all clients fetch the latest code on next load.
2026-06-01 23:09:17 -04:00
shawn 0e53da09c7 fix: barcode scan rate 50ms→300ms — decoder was thrashing, missed barcodes
delayBetweenScanAttempts was set to 50ms (20 scans/sec) but with
TRY_HARDER enabled and 13 barcode formats configured, the ZXing decoder
couldn't keep up — frames were skipped and barcodes visible in the
viewfinder were never detected. 300ms gives each frame enough processing
time while still feeling real-time.
2026-06-01 22:59:00 -04:00
shawn 111008a858 fix: ZXing per-frame 'no detection' errors were being treated as camera failures
ZXing's decodeFromVideoDevice fires its callback on every video frame
(~30fps). When no barcode is in view, it passes `(null, err)` where err
is 'No MultiFormat Readers were able to detect the code' — this is
NORMAL, not a hardware failure.

The error branch now filters out 'multiformat' and 'unable to detect'
messages, only calling handleCameraError for real device-level issues
(permission denied, camera not found, stream broken).
2026-06-01 22:56:54 -04:00
shawn 6322786fd2 fix: stop keyboard-loop on mobile camera failure + guard drawer behind login
- handleCameraError() now sets scanningActive=false and resets codeReader
  so ZXing can't fire more callbacks that re-create the manual input and
  re-focus it — mobile keyboard was popping repeatedly
- openDrawer() now checked loginOverlay.hidden before opening, so drawer
  can't appear behind the login screen
2026-06-01 22:46:38 -04:00
shawn 0f0aa1021a Fix UX-015: ZXing async callback silently swallows camera errors - add handleCameraError(), 5s cameraFailTimer, error branch in decodeFromVideoDevice callback 2026-06-01 22:11:12 -04:00
shawn d092abd91a UX fixes: admin link gating, GPS pulse timeout, ? shortcut overlay, offline queue, native confirm→modal 2026-06-01 22:01:23 -04:00
shawn a7b1887274 chore: Remove stale bak files from index 2026-06-01 21:23:07 -04:00
shawn b020665088 fix: Default to OCR mode on initial page load
- Fixed HTML initial state: OCR toggle button has 'active', OCR panel has 'active', barcode does not
- switchTab calls setAddAssetMode() which syncs visual panels AND starts the right camera
- Previously the JS variable was 'ocr' but HTML hardcoded barcode as active — visual mismatch
2026-06-01 21:22:25 -04:00
shawn 5ae9763f9a chore: Ignore .bak files, remove from tracking 2026-06-01 19:17:14 -04:00
shawn 50ba02e79e fix: Make work order detail modal scrollable
- Added max-height: 85vh to modal container
- Made modal-body scrollable (overflow-y: auto, flex shrink)
- Title and action buttons stay fixed while body scrolls
- Fixes 'too tall and not scrollable' when WO details have many items
2026-06-01 19:17:10 -04:00
shawn 4e4632ab3d chore: Remove accidentally committed DB backups 2026-06-01 19:12:24 -04:00
shawn 86975e2e2b feat: Default to OCR scanner, auto-start camera, hide viewport after capture
- Default add-asset mode changed from barcode to OCR
- Auto-start OCR camera when switching to OCR mode or Add Asset tab
- Camera viewport hidden entirely after photo capture (no placeholder showing)
- Retake button added to all OCR result states (found, not-found, error)
- 'Retake' resets UI and re-starts camera for a new photo
2026-06-01 19:12:01 -04:00
shawn 03cf4b9281 feat: Route tab overhaul + work order filters + Next Stop + Google Maps
- Added work order filter bar to Route tab matching the Work Orders tab UX:
  status, priority, work type dropdowns + date range selector
- Filter params passed to /api/workorders/today endpoint (backend updated
  with new query params: status, priority, work_type, date_range)
- Added Google Maps Navigate button on every route stop
- Added Mark Done & Next Stop button — tracks current stop index,
  marks completed stops with green check, advances to next with
  auto-prompt to open Google Maps navigation
- Animated stop progress: done stops dimmed with green ✓, current
  stop highlighted with accent border
- Updated /api/workorders/today to support date_range (week/month/all)
  and additional filter WHERE clauses
2026-06-01 13:10:37 -04:00
shawn 2d232941da Fix single checkin GET to include username via JOIN 2026-06-01 00:27:29 -04:00
shawn e325981b17 Fix check-in user tracking
- Backend: create_checkin now auto-resolves user_id from auth token
  when not provided in the request body
- Backend: list_checkins now JOINs users table to return username
  alongside each check-in
- Frontend: all 4 check-in functions now pass user_id from
  AppState.currentUser
- Frontend: check-in history shows 👤 username next to each entry
- Also made work order count clickable: shows WO detail modal
- Added work_orders list to MSFS asset response

Closes #62
2026-05-31 23:54:13 -04:00
shawn 07539b683e feat: add script to import machines from Cantaloupe Excel export
Adds scripts/import_from_excel.py that reads Cantaloupe .xlsx exports
and imports missing machines into the canteen-asset-tracker DB.
- Dry-run by default, --write to commit, --prod for production DB
- Maps all 62 spreadsheet columns to assets table schema
- Stores full raw row as JSON in seed_data table
- Backs up DB before import
2026-05-31 11:09:27 -04:00
shawn 8713169e84 feat: add My GPS button to asset detail GPS editor
Adds a '📍 My GPS' button to the GPS Map Editor card in the asset
detail screen that:
- Grabs device GPS via navigator.geolocation.getCurrentPosition
- Places map marker at current device position
- Auto-unlocks the GPS editor if locked, shows Save button
- Displays accuracy in status text
- Provides clear error messages for denied/timed-out/unavailable

Includes enableHighAccuracy: true, 15s timeout, 60s cache.
2026-05-31 09:53:23 -04:00
shawn 2a92c4e71b Swap all UI icons to game-icons SVGs via sprite
- Created SVG sprite with 49 game-icons symbols (58KB)
- Added .gi CSS class for icon sizing/fill
- Replaced 77 emoji in static HTML with <use href='#gi-{name}'>
- Skip <option> elements (can't render SVG there)
- Added JS gi() helper for 33 dynamic icon assignments
- Replaced 📍🔒🔓📷📋↕️⚠ in JS-driven content
- Zero network requests, zero console errors
- Pairs with existing CATEGORY_MAP game-icons SVGs
2026-05-31 02:40:52 -04:00
shawn cc4f3b7dcf Swap all category icons to game-icons
All 8 categories now use Game Icons from game-icons.net:
- Bev:     game-icons:soda-bottle (glass soda bottle)
- Snack:   game-icons:chips-bag (crinkly chip bag)
- Food:    game-icons:hamburger (stacked burger)
- Equip:   game-icons:wrench (adjustable wrench)
- Appl:    game-icons:cooler (ice chest)
- Markets: game-icons:shop (storefront with awning)
- Coffee:  game-icons:coffee-cup (paper cup with steam)
- Other:   game-icons:cargo-crate (industrial crate)

Zero external deps — inline SVGs from Iconify CDN
2026-05-31 02:26:01 -04:00
shawn dc11ac75d2 Swap Bev and Snack icons to game-icons
- Bev: hugeicons:soda-can → game-icons:soda-bottle
- Snack: mdi:chips → game-icons:chips-bag
- Other categories unchanged
2026-05-31 02:24:21 -04:00
shawn b444009593 Replace emoji icons with Iconify SVG icons
- Bev: hugeicons:soda-can (soda can with pull tab, outline style)
- Snack: mdi:chips (bag of chips)
- Food: mdi:hamburger
- Equipment: mdi:wrench
- Appliances: mdi:fridge-outline
- Markets: mdi:store
- Coffee: mdi:coffee
- Other: mdi:package-variant
- Removed stale <img guard in map sidebar rendering
- Added SVG sizing CSS for asset list icons
- Zero external deps — inline SVGs from Iconify CDN
2026-05-31 02:15:49 -04:00
shawn 18743ffdde Add GPS Map Editor to asset detail view
Add a GPS map editor card at the top of asset details, allowing users to
manually set an asset's GPS coordinates via an interactive Leaflet map.

- GPS card with Leaflet map and draggable marker
- 🔒 Lock/unlock toggle (locked by default when GPS exists)
- 💾 Save GPS button (visible only when unlocked)
- Saves via PUT /api/assets/{id} with lat/lng
- Click on map or drag marker to set position
- Fallback to Orlando coords (28.5383, -81.3792) when no GPS
- Auto-refresh detail view after save

Closes #61
2026-05-31 01:47:54 -04:00
shawn 064fc43fe3 fix: remove all address-geocoded GPS, keep only original check-ins
All GPS from MSFS account addresses and Cantaloupe seed data removed.
Only 5 authentic app check-in GPS entries remain.
Reverts the bulk restore from earlier commit (gps restore was address-based).
2026-05-31 01:45:53 -04:00
shawn 616769e805 feat: restore GPS data from pre-MSFS backup + MSFS account data
- 221 GPS entries restored via serial_number match from pre-MSFS backup
- 5,730 GPS entries restored via MSFS account address data
- Coverage improved from 1.6% (124) to 80.4% (6,075) of 7,560 assets
- Remaining 1,485 have no GPS in MSFS account records
2026-05-31 01:40:03 -04:00
shawn 55a21e981f Recategorize: Markets (🏪) replaces Kiosk; 106 coffee machines Bev→Coffee
- Renamed Kiosk category → Markets (includes 365 Retail Markets,
  Intuitivo, Panoptyc, Smart Market, Micro-Market-Ave C)
- Moved 106 coffee brands from Bev to Coffee: Nespresso (36),
  Curtis (23), Krea (14), EVERSYS (12), Bloomfield (6),
  Segafredo (4), Bravilor (4), Simonelli (2), Starbucks (2),
  Marco (2), Cafection Digi-cup (1)
- Updated CATEGORY_MAP
2026-05-31 01:09:40 -04:00
shawn fff2b6a52a feat: add Coffee category () — move 63 De Jong Duke / Impressions vending machines from Equipment
- New Coffee category with amber (#a16207) branding
- 63 coffee vending machines recategorized from Equipment
- CATEGORY_MAP updated in main app
2026-05-31 00:52:02 -04:00
shawn 955e489977 Add Kiosk category, re-categorize 207 assets, fix .gitignore
- New Kiosk category (🏪, purple) for 365 Retail Markets, Panoptyc, Intuitivo,
  Smart Market, Micro-Market — 1,100 assets moved from Equipment/Other
- CRANE (114) → Equipment (was wrongly 'Other')
- Vendo, Royal, DN, DIXIE NARCO (52) → Bev (beverage vending machines)
- USI (2) → Snack
- Cafection Digi-cup, Echostream, Purelogix, Avalon (4) → Bev
- CoolServ, Cold Snap, KoolTek (3) → Appliances (cooling/refrigeration)
- De Jong Duke, AC, Impressions, Lektro-Vend (11) → Equipment
- Remaining 'Other' reduced from 221 to 14 genuine unknowns
- Zero unmapped categories — Category Health clean
2026-05-31 00:40:21 -04:00
shawn f1a3bb9f00 feat: revised category icons + unknown type detection
- Cleaned CATEGORY_MAP: removed dead entries (Furniture, GF Bev,
  Snack/bev, Snack/food), fixed duplicate 'Food' key, proper
  distinct icons for all 6 real categories
- Added categoryInfo() helper: 🥤/🍿/🍔/🔧/🔌/📦 for known cats,
   (red) for unmapped categories, 📦 for null
- Updated all render sites (list, map pins, map list, detail view,
  scan result, OCR result) to use categoryInfo()
- Added category icons to detail view and scan/OCR result headers
- Synced categories DB table: added Equipment/Appliances with icons,
  removed unused Unknown
- Updated seed data in both server.py and admin_server.py to match
  real asset categories
- Added category_health to /api/stats with unmapped category detection
- Added Category Health card to admin dashboard (shown only when
  unmapped categories exist)
- Updated CSS backgrounds for all real categories + .cat-unmapped
2026-05-31 00:28:03 -04:00
shawn 5c2e15e28b ux: persist help overlay dismissed state via localStorage 2026-05-30 21:20:50 -04:00
shawn 474a618f79 UX fixes: fix Map tab crash, remove Edit/Delete buttons, update Help dialog and menu labels 2026-05-30 20:56:28 -04:00
shawn 9ae0f271ea chore: remove accidentally committed gps backup file 2026-05-29 23:17:53 -04:00
shawn 17b870e4cc feat: wire vision/OCR results back into asset records
- Auto-save photo_path to matched assets when photo uploaded via /api/ocr
- Auto-extract and save serial_number from OCR text to blank-serial matched assets
- Add _extract_serial_from_text helper for serial number pattern matching
- Create scripts/backfill_vision_photos.py for batch-processing unlinked photos
- Add docs/OCR_PIPELINE.md documenting the full OCR/vision pipeline
- Fix test DB schema: add connect_id, equipment_id, barcode, customer_name
- Fix OCR test assertions to match actual endpoint behavior
2026-05-29 10:03:51 -04:00
shawn 99cef94153 Cross-reference photos against assets DB
- Scanned all 15 photos in uploads/photos/ via vision + Tesseract OCR
- Matched keurig_label.jpg → Asset #5144 (machine_id 636671)
- Updated Asset #5144 photo_path in assets.db
- Created scripts/crossref_photos.py for reusable batch matching
- Reported unmatched IDs for coca_cola_label.jpg and telemetry_device.jpg
- Generated crossref_report.md with full findings
2026-05-29 09:56:49 -04:00
shawn e2db719fd7 docs: update OCR description to reflect Ollama vision primary 2026-05-29 09:00:27 -04:00