Commit Graph

109 Commits

Author SHA1 Message Date
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 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 77ffb0ab2a Remove unused exifr CDN from index.html 2026-05-28 22:04:27 -04:00
shawn f3d176116a Major app overhaul: MSFS data import, lookup-only Find tab, OSRM nav, cleanup 2026-05-28 21:57:58 -04:00
shawn a9f2f88604 ux: Increase map max-height from 60vh to 70vh for better square ratio 2026-05-27 19:51:45 -04:00
shawn 855976e2b6 ux: Map tab enhancements + toast replacements
- Replace 9 native alert()/confirm() calls with showToast()
- Add search bar over map (name, machine_id, category, description)
- Add filter chips with category counts (Bev, Food, Snack, etc.)
- Make map container square (aspect-ratio 1/1)
- Add asset list below map showing filtered results
- Increase asset limit from 1000 to 2000
- Fix missing closing brace in .geofence-panel CSS
- Zero console errors on all tabs
2026-05-27 19:33:43 -04:00
shawn e66bf2638b fix: route planner fetch calls now use api() wrapper with auth token
Root cause: rpLoadTechs() and all other route planner API calls used
raw fetch() instead of the api() wrapper, so no Authorization header
was attached. Auth middleware returned 401, technician list never
loaded, users saw 'Select at least one technician'.

Fixed by replacing all 6 fetch('/api/...') calls in the route planner
with api('/api/...') and removing redundant .json() parsing (api()
returns parsed data directly).

Tested in browser: technician checkboxes populate, Load Today's Route
returns 3 optimized stops with 12.5km route, map renders correctly,
search tab queries return Disney WO results via auth.

Closes: #issue-to-be-created
2026-05-27 19:08:58 -04:00
shawn 3eb09bd861 ux: 5.0 heuristic scorecard — loading states, help modal, keyboard shortcuts, guided empty states, error recovery 2026-05-27 18:57:28 -04:00
shawn b2ceac1594 ux: improve login loading state, toast timing, tab label consistency, enable location filter 2026-05-27 18:35:49 -04:00
shawn da2a23f6da Inline route optimization into canteen server (remove proxy to daily-route on port 8912)
- Added extraction DB config and _get_extraction_db() helper
- Added _status_label, _haversine, _solve_tsp (nearest-neighbor + 2-opt)
- Added GET /api/workorders/search (native)
- Added POST /api/workorders/lookup (native)
- Added GET /api/workorders/today (native, reads bookableresourcebooking)
- Added GET /api/workorders/technicians (native)
- Added POST /api/route/optimize (native TSP solver)
- Removed httpx dependency, _proxy_route, _get_route_client
- Removed all proxy endpoints
2026-05-27 18:18:24 -04:00
shawn 6cb76d8a33 Remove redundant drawer triggers — keep only hamburger ☰
- Removed onclick=openDrawer() from user badge
- Removed '⋯ More' bottom tab (also opened drawer)
- Header hamburger ☰ remains the single drawer trigger
2026-05-25 23:22:17 -04:00
shawn d3e1068e1f Fix bottom tab text clipping — increase horizontal padding 2px→6px 2026-05-25 23:17:07 -04:00
shawn faacf0bce9 Rename 'Add Asset' tab to 'Add / Find Asset' across nav + tabs
- Drawer nav: 'Add Asset' → 'Add / Find Asset'
- Bottom tab bar: 'Add Asset' → 'Add / Find Asset'
- Updated e2e_browser_test.py assertions to match
2026-05-25 23:14:52 -04:00
shawn 34da18530f Remove auto-visit GPS tracking
- Removed startVisitTracking / stopVisitTracking functions
- Removed checkProximityToAssets and haversineM
- Removed logAutoVisit and all visit timer logic
- Removed visitTracker UI div on Map tab
- Removed GPS watchPosition call (was polling at ~1-5s intervals)
- Kept server-side /api/visits endpoints (used by heatmap) and one-shot GPS for scan check-in

Reason: GPS auto-tracker can't distinguish machines above/below each other
on different floors.
2026-05-24 17:41:43 -04:00
shawn f4cbe00f7f feat: scan existing asset redirects to detail view instead of scan result card
When a user scans a barcode that matches an existing asset:
- Redirect directly to the asset detail view (Assets tab)
- Auto-checkin still runs in background if GPS available
- Show a toast notification with the asset name

When scanning a barcode not in database:
- Switch back to Add Asset tab and show the create form
- Deferred status update survives async startScanning

No backend changes needed.

Closes #39
2026-05-24 17:17:13 -04:00
shawn b896a5680d feat: enlarge asset list cards — bigger icons, padding, prominent floor/building/room display 2026-05-23 20:31:48 -04:00
shawn a5708623cc feat: show address, floor, building, room in asset list items
- Add .ai-address and .ai-location CSS classes
- Increase .asset-item padding to 14px, align-items to flex-start
- Reduce .ai-name font to 14px, allow wrapping
- Render address line (📍 icon) when present
- Render floor · building · room location line when any field present
- Keep existing icon and meta line unchanged
2026-05-23 19:46:25 -04:00
shawn 13bf61d856 feat: extract floor/building/room/trailer from asset names
Adds scripts/extract_asset_location_data.py which:
- Extracts floor, building_number, room, trailer_number, building_name from names
- Detects and removes duplicated name segments
- Updates both assets (1848) and locations (364) tables
- Dry-run mode by default, --apply to write changes
2026-05-23 19:41:55 -04:00
shawn a305766d65 fix: show '— No report yet' for assets without dex_report_date 2026-05-23 18:46:58 -04:00
shawn 6016f6a5e6 feat: add No DEX (5d) filter toggle to asset list filter panel
- Add .filter-toggle.warn CSS styles (amber border/text with active bg)
- Add Data Health row with toggle button in filter panel HTML
- Add noDexFilterActive state variable and toggleNoDexFilter() function
- Wire no_dex_days=5 into API URL builder when toggle active
- Integrate into getActiveFilterCount, renderActiveFilterTags, removeFilter, clearAllFilters
2026-05-23 17:41:04 -04:00
shawn c9c6317900 fix: larger park/resort labels (13px bold), allow 2-line wrap, center-aligned 2026-05-23 00:41:35 -04:00
shawn 0be50e7957 fix: revert chips to emoji, Snack pins use 🍿 fallback (img→emoji), bigger markers 28px 2026-05-23 00:37:10 -04:00
shawn 1bb03eb964 feat: SVG icons for map toolbar chips — flame/crosshair/outline, 13px, with label spans 2026-05-23 00:34:54 -04:00
shawn 384a0b842b fix: simple colored circle pins, no transforms — 22px dot with emoji and white border 2026-05-23 00:33:53 -04:00
shawn 71de462040 fix: teardrop pin markers (smaller, 24px) with category emoji on white dot 2026-05-23 00:31:46 -04:00
shawn 26311e7fe6 feat: text labels on Leaflet.draw toolbar — Area/Box/Circle/Edit/Delete instead of bare icons; revert chip changes 2026-05-23 00:29:56 -04:00
shawn 0401e45ea4 feat: category-based icon pins on map — 🥤🍔🔧 etc instead of blue dots 2026-05-23 00:27:54 -04:00
shawn 796914b089 feat: clearer map control icons with SVG + label spans, replace emoji chips 2026-05-23 00:25:43 -04:00
shawn 4ac930f2bf feat: 15 named Disney resort outlines with individual labels on map 2026-05-23 00:18:47 -04:00
shawn 0f2ead4b5b feat: bolder park outline styling — thicker lines (5px), full opacity, stronger fill, saturated colors 2026-05-23 00:15:08 -04:00
shawn 39b8669b21 Fix park outline coordinates + visibility
- Magic Kingdom: accurate 60-point polygon from OSM way 297428432
- All parks: adjusted boundaries for better alignment
- Increased stroke weight (3px) and opacity for visibility
- Higher fill opacity (0.15)
2026-05-23 00:04:27 -04:00
shawn 5380bf7256 Add Disney park outlines to map
- Polygons for Magic Kingdom, Epcot, Hollywood Studios, Animal Kingdom, Disney Springs
- Resort area outline (dashed) for Pop Century / Art of Animation
- Color-coded to match existing park badge colors
- Labels with park icons at polygon centers
- Toggle chip (🏰 Parks) in map controls to show/hide
- Outlines visible by default on map load
2026-05-22 23:53:09 -04:00
shawn a0e6f627f4 Revert "Add clear GPS endpoint + frontend button"
This reverts commit ca77a29d7d.
2026-05-22 23:32:51 -04:00
shawn ca77a29d7d Add clear GPS endpoint + frontend button
- DELETE /api/assets/{id}/gps sets lat/lng to NULL
- Detail view shows 'Clear GPS' button when asset has GPS coords
- Confirmation modal before clearing
- Tests: 204 success + 404 not found
2026-05-22 23:30:07 -04:00
shawn d91c1f0861 fix: normalize GF Bev/GF Food category casing throughout
- import_machines.py: add GF Bev/GF Food/Bev to category_map
- static/index.html: CATEGORY_MAP keys Gf bev→GF Bev, Gf food→GF Food
- DB: migrated 236 Gf bev→GF Bev, 173 Gf food→GF Food
2026-05-22 19:15:57 -04:00
shawn db6859a0f5 fix: Add Cache-Control: no-cache to static files to prevent stale HTML/image caching 2026-05-22 18:44:36 -04:00
shawn 71a0c9936a chore: Update chips icon to transparent-background PNG with 'REAL POTATO CRISPS' 2026-05-22 18:42:39 -04:00
shawn 8c828000bb feat: Swap snack icon from popcorn 🍿 to bag of chips image
Replaced the Snack category emoji with an actual chips illustration
served from static/icons/chips.png. The icon renders in the asset
list category badges as a 28x28 object-fit:contain image within
the 42x42 circular badge.
2026-05-22 18:40:26 -04:00
shawn e8a918fc7b feat: add is_disney column for reliable Disney/Non-Disney filtering
- Added is_disney INTEGER DEFAULT 0 column to assets table
- Backfilled 996 Disney assets based on D- customer prefix
- Server: is_disney in AssetCreate/AssetUpdate models, INSERT/UPDATE
- Server: disney_filter query param now uses is_disney column (no JOIN needed)
- import_cantaloupe.py: sets is_disney=1 when customer starts with D-
- import_machines.py: sets is_disney=1 when customer starts with D-
- disney_classify.py: sets is_disney=1 when classifying
- Frontend: new Location Type dropdown with Disney/Non-Disney toggle
  (replaces the __disney__/__non_disney__ park dropdown pseudo-options)
2026-05-22 18:34:13 -04:00
shawn 60dfd3434a feat: add Disney/Non-Disney quick filter to dropdown panel
Backend:
- disney_filter query param on /api/assets (values: 'disney' | 'non_disney')
- Detects Disney by checking disney_park IS NOT NULL OR customer name LIKE 'D-%'
- Catches ESPN, Wide World of Sports, all Disney resorts, etc.
- Adds LEFT JOIN to customers table when disney_filter is active

Frontend:
- '🏰 Disney only' and '🏢 Non-Disney' options in Disney Park dropdown
- Active filter tag shows which is selected
- Works together with other dropdown filters
2026-05-22 18:25:54 -04:00
shawn 11628898ff chore: remove orphaned renderFilterPills function 2026-05-22 18:21:32 -04:00
shawn c19295f1a2 fix: remove inline filter pills, keep only Filters button + dropdown panel 2026-05-22 18:21:07 -04:00
shawn e0412acbe3 feat: add Category, Make, Disney Park to filter panel dropdowns with backend endpoints 2026-05-22 18:19:32 -04:00
shawn d98a791c96 feat: improved asset filter UX - collapsible filter panel, customer/location cascade, status dropdown, assigned_to filter, active filter tags, result count
Backend:
- GET /api/customers — lists all customers for filter dropdown
- GET /api/locations?customer_id=X — cascading locations per customer
- GET /api/users — lists users for assigned_to filter

Frontend:
- Collapsible 'Filters' panel with Customer→Location cascade, Status, Assigned To
- Active filter tags showing what's currently filtering, removable individually
- 'Clear all filters' button to reset all at once
- Result count display ('X assets found')
- Better search placeholder showing searchable fields
- Filter count badge on the toggle button
2026-05-22 18:15:35 -04:00
shawn dec6a90409 Add proper category icons for all 10 types, remove stale CSS 2026-05-22 18:02:41 -04:00
shawn 7394d44caf Load all assets at once (no pagination), add server-side disney_park filter 2026-05-22 17:59:33 -04:00
shawn 95b5bd26eb Add pagination (Prev/Next with counter) and X-Total-Count header 2026-05-22 17:57:24 -04:00
shawn cce5cf2b5f Add Disney park badges, filter pills, editable classification, and service entrances to asset detail view 2026-05-22 17:53:32 -04:00