Commit Graph

191 Commits

Author SHA1 Message Date
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
shawn b80ed4b483 feat: Ollama vision OCR via Windows PC for label photo matching
- Add persistent SSH tunnel (systemd service) to Windows PC Ollama
- Add _HAS_OLLAMA check at server startup (qwen2.5vl:3b)
- Replace Tesseract-only OCR with Ollama-first strategy
- Ollama (qwen2.5vl:3b) handles dark/complex labels Tesseract can't read
- Keurig serial 2500.0100.0025534 now correctly matched as Asset #5144
- Add ocr_source field to /api/ocr response ('ollama' or 'tesseract')
- Fix match_label_photo.py argparse bug (image_paths -> images)
- Update match_label_photo.py CLI to read vision key from Hermes config
2026-05-29 08:59:53 -04:00
shawn 8022c77b70 feat: add normalized matching for label photos → asset DB lookup
- normalize_identifier() strips dots/dashes/prefixes, keeps alphanumeric
- find_asset_by_normalized_id() searches serial_number, connect_id,
  equipment_id, barcode with normalized comparison
- /api/ocr now returns matched_assets in addition to legacy machine_id
- New /api/match-text endpoint for client-side text matching
- scripts/match_label_photo.py CLI tool for OCR + DB matching
- Vision model fixed (mimo-v2-omni at opencode.ai, was using
  truncated placeholder key)
2026-05-29 08:37:06 -04:00
shawn e10e226743 Add serial_number to barcode search
- /api/assets/search now also matches serial_number column
  so scanning a sticker with just the serial number works
2026-05-29 07:59:37 -04:00
shawn 8c8f8a261c Add 2D barcode scanning + connect_id search
- Add QR_CODE, DATA_MATRIX, PDF_417, AZTEC to barcode scanner hints
  so serial number stickers with 2D barcodes can be scanned
- Update /api/assets/search to also match connect_id so full
  Connect IDs (e.g. VM-05912-0000068454) work when scanned
- Bump SW cache v5→v6, footer v3.2→v3.3
2026-05-29 07:55:44 -04:00
shawn 2bdcfe0bae feat: branch fallback for assets without account linkage
- 1,606 more assets now get company from branch/territory name
  (e.g. 'Canteen Corp/Orlando, FL') when the connect_id exists
  in extraction DB but has no linked account name
- Coverage: 7,349/7,488 (98.1%) have company + customer_name
- Only 139 remain empty (4 prefixes not in branch cache)
- Updated AGENTS.md with data population docs
2026-05-29 07:51:02 -04:00
shawn 4f9c0d0b75 feat: populate company/place/customer_name from MSFS extraction DB
- New script: scripts/populate_asset_company_place.py maps connect_id →
  account name (company/customer_name) + city (place) + address via
  extraction DB (msdyn_customerasset + account join)
- 5,743/7,488 assets now have company + customer_name populated
- 5,647/7,488 assets now have place (city) populated
- Fix: SELECT COALESCE(c.name, a.customer_name) so stored customer_name
  isn't overridden to NULL when customer_id FK is missing
- Bump SW v4→v5, version v3.1→v3.2 to force cache refresh
2026-05-29 07:45:01 -04:00
shawn 5180d811a8 fix: add place/building/floor fields to main detail view
The detail view's Asset Details card was missing location fields
(place, building_name, building_number, floor, room, trailer_number)
that were visible in the list cards. Added them alongside Model
so building/floor info appears in the main card rather than only
in the collapsed Directions & Access section below.
2026-05-29 00:25:10 -04:00
shawn f1eb453a19 fix: keep GUEST/CAST visible in asset names
- clean_customer_name() no longer strips GUEST/CAST suffixes
- Re-ran migration: 967 asset names updated with GUEST/CAST preserved
- Format: '95301 - Animal Kingdom CAST / address / building'
  vs previous: '95301 - Animal Kingdom / address / building'
2026-05-29 00:13:08 -04:00
shawn 9ee8de8578 fix: add/find tab now lookup-only, nav fix, route techs fix
- Add/Find tab: removed check-in card and add-asset form, now pure lookup
- OCR: stop camera after capture, added GPS capture at photo time
- OCR: increased timeout from 8s to 15s to reduce 500 errors
- Nav tab: added GPS prompt + asset search when navigating directly
- Route: include all 26 techs from bookableresource table (not just Shawn)
2026-05-29 00:10:30 -04:00
shawn 1db6475f83 feat: asset naming, location extraction, and location display
- New asset naming: {MID} - {Customer} / {Address} / {Building details}
- Extracted 660 building_names from customer data (Disney resorts, hotels)
- 1,704 assets now have location data (building_name, floor, etc.)
- Location section in detail view now shows building, floor, trailer, room
- Added has_gps filter to API for map loading
- Added location_source column to track GPS origin
2026-05-28 23:58:56 -04:00
shawn ae3b114ebc feat: import Seed (mycantaloupe.com) data — 1,655 assets enriched
- Added import_seed.py — reads Machine List(8).xlsx, matches by Asset ID → machine_id
- 51 new columns on assets table (route, barcode, sales, contact info, etc.)
- seed_data table with full 62-field JSON per asset for detail enrichment
- Seed data card in frontend asset detail view
- GET /api/assets/{id} now returns result.seed and result.seed_imported_at
- GPS columns from Seed ignored per request
- 1,654 matched, 199 unmatched (Seed-only assets not in MSFS)

Refs #47
2026-05-28 23:28:08 -04:00
shawn 908c67a26c docs: update msfs-data-import.md after clean-slate reset
- Cleared all MSFS-derived (address-geocoded) GPS
- Only real field GPS (28 assets from photo EXIF) remains
- Documented backup locations and clean-slate process

Refs #46
2026-05-28 23:01:17 -04:00
shawn a8158566ac feat: OCR auto-saves GPS to asset + MSFS enrichment in detail view
OCR endpoint (/api/ocr) now auto-saves EXIF GPS from the photo
to the asset's latitude/longitude fields when they are blank,
so the first photo scan at a machine permanently records its location.

GET /api/assets/{id} now includes an 'msfs' block enriched from
the merged Dynamics 365 Field Service data (merged-assets.json)
loaded at server start (1,834 matched assets). Fields shown:
equipment_id, connect_id, compass_asset_number, manufacturer,
model_text, dex_model, install_date, software_version, etc.

Frontend updates:
- New 'Field Service Data' card in asset detail view when MSFS
  data is available (manufacturer, Connect ID, install date, etc.)
- '📍 GPS saved to asset' confirmation shown after OCR auto-save
- AppState.ocrGpsSaved flag to track the auto-save event
2026-05-28 22:28:34 -04:00
shawn 3a8f89432a fix: stray closing brace broke entire JS script + remove login gate for guest mode 2026-05-28 22:16:43 -04:00
shawn b07c30da5e docs: MSFS data import pipeline — extraction → merge → assets.db 2026-05-28 22:07:14 -04:00