206 Commits

Author SHA1 Message Date
shawn 58f8b67b28 auth: full session-based auth with remember-me support
- Seed user: shawn / Brett85!@ (SHA-256)
- Sessions table with expires_at column
- login endpoint supports remember_me flag (30d vs 1d expiry)
- logout endpoint invalidates server-side token
- auth middleware + /api/auth/me check session expiry
- Frontend sends remember_me in login request body
- Frontend calls /api/auth/logout on client logout
- Migration function for existing databases
2026-05-21 20:17:21 -04:00
shawn 921a40a560 Fix: call initAuth() on DOMContentLoaded so login overlay shows on page load. Also includes v3 admin separation changes that weren't committed. 2026-05-21 20:07:52 -04:00
shawn b5381fde69 t_4a71eab5: Strip admin UI from main index.html
- Removed empty comment blocks for removed admin tabs (Customers, Dashboard, Activity, Reports, Settings)
- Removed geofence Create/Edit/Delete UI from map tab (chip, color picker, geofence panel)
- Removed leaflet-draw dependency (no longer needed)
- Removed CustState and showCustView (admin-only)
- Removed all geofence JS functions (toggleGeofenceDraw, saveDrawnGeofence, loadGeofences, renderGeofenceList, editGeofence, deleteGeofence, assignGeofenceUsers)
- Removed loadTechnicianOptions() and its /api/users call
- Fixed loadManualLocations() to not call removed /api/locations endpoint
- Removed initMap orphan call and drawingGeofence cleanup in switchTab
- Removed unused geofence CSS styles and color-picker-row styles
- Kept Admin Panel link in drawer footer pointing to admin app
2026-05-21 17:56:13 -04:00
shawn ba6921eade t_4a71eab5: Strip admin UI from main index.html
- Replaced loadSettingsCache() with hardcoded seed data (categories, makes, keys, badges)
- Removed tabActivity lifecycle hook (activity log moved to admin app)
- Updated version to v3.0 + Admin Panel link in drawer footer
- Updated PROJECT.md with v3 project structure (3 repos, admin server docs)
- Removed backup files (.bak, .orig)
- Main index.html reduced from 7645 to 4228 lines
2026-05-21 17:47:22 -04:00
shawn 0824075048 t_68f4f6a2: Strip admin API routes from main server.py 2026-05-21 17:27:06 -04:00
shawn 9a1a5d9caa docs: add admin.canteen.ourpad.casa proxy config to PROJECT.md
NPMPlus proxy ID 20, cert ID 21, backend 192.168.0.127:8901
Cloudflare DNS A record grey cloud, Let's Encrypt SSL

Closes gitea issue #26
2026-05-21 17:18:21 -04:00
shawn 7cf566ad6d t_2f17b1b4: Add EXIF GPS extraction to upload/photo and OCR endpoints
- _extract_gps_from_bytes helper reads GPSInfo IFD via PIL get_ifd()
- Converts DMS to decimal degrees, returns {lat, lng} or None
- /api/upload/photo: reads bytes first, extracts GPS before saving
- /api/ocr: extracts GPS from raw bytes before OCR processing
- exif_gps returned alongside path/machine_id in both endpoints
- Photos without EXIF GPS omit the exif_gps key entirely
- Raw file bytes preserved (no re-compression, no EXIF stripping)
2026-05-21 17:17:38 -04:00
shawn 5d2d169885 fix: rename photoPicker to cameraInput/galleryInput broke JS IIFE
Two places referenced old photoPicker element ID that was removed:
1. connectFromGallery() — switched to window._connectMode flag + galleryInput.click()
2. Connect mode hook IIFE — now hooks both cameraInput and galleryInput via
   shared hookPicker() helper using window._connectMode instead of DOM attr

This was crashing the entire inline script (TypeError on null) ~line 4040,
preventing initAuth()/checkAuthGate() from ever running — no login screen.
2026-05-21 16:16:53 -04:00
shawn e16f2ec90f gallery: split Take Photo vs Pick from Gallery inputs
- Two dedicated <input> elements: cameraInput (capture=environment) for
  direct camera, galleryInput (no capture) for gallery picker
- Both wired to handlePickedPhoto() via a shared wireInput helper
- refreshGPS() fires on button click (before picker opens) for a fresh
  high-accuracy GPS fix — no maximumAge, so it pulls current location
  instead of relying on cached values from page-load initGPS()
- Workaround for browser EXIF GPS stripping: camera captures may preserve
  EXIF GPS, gallery picks fall through to device geolocation API
2026-05-21 15:29:03 -04:00
shawn 09d83da4e2 server OCR: accept any file extension, detect format from bytes
- Removed extension whitelist — no more 'unsupported format' errors
- Falls back to .jpg temp file for unknown/no extension
- PIL detects actual image format from file bytes (handles DNG as TIFF)
- Android browsers often strip DNG extensions when picking from gallery
- Tested: works with .dng, .jpg, no extension, and .bogus
2026-05-21 15:05:39 -04:00
shawn 28f1a33a2d gallery: increase server OCR timeout to 20s
- 6s was too short for 13MB DNG upload on mobile
- Bumped to 20s to handle large RAW photos
- Added 'Uploading to server OCR...' status message during upload
- Server OCR handles DNG correctly (tested via curl), so the
  timeout was the root cause of gallery upload failures
2026-05-21 14:55:13 -04:00
shawn 0cf5dddf83 DNG/RAW photo support for gallery upload
- Server: accept .dng in OCR endpoint (PIL opens as TIFF)
- Server: bump OCR size limit to 20MB (DNG files can be large)
- Client: gallery upload tries server OCR first (with 6s timeout),
  then falls back to client-side Tesseract.js — matches 📸 capture flow
- DNG photos now work with 'Pick from Gallery'
2026-05-21 14:51:57 -04:00
shawn 97699a95ea gallery upload: better OCR error messages
- Catches 'unknown error' from Tesseract.js (unsupported
  image formats like RAW/DNG) and shows helpful guidance:
  'Use the 📸 live camera instead'
- Falls through to actual error message for other failures
- Better null-safety on error object
2026-05-21 14:47:34 -04:00
shawn 78c3c19ce4 gallery upload: add FileReader + img error handlers
- reader.onerror: shows error message on file read failure
- img.onerror: detects unsupported image formats (HEIC, RAW)
- Shows clear feedback: 'unsupported format' or 'format not supported'
- Helps diagnose gallery upload issues
2026-05-21 14:44:02 -04:00
shawn 6403699486 fix: login lag + 📸 overlay visibility + status text
- Login lag: camera/GPS/assets no longer start before login.
  Only initAuth() + initOfflineSupport() run on pageload.
  Camera/GPS/assets start after successful login (or auto-login
  from stored session).
- 📸 capture overlay: was defined in HTML but never shown.
  Now shown when camera starts, hidden when scanning stops.
- Status text: updated from 'Point camera at a barcode' to
  'Point camera at a barcode, or tap 📸 for text stickers'
2026-05-21 14:16:24 -04:00
shawn d55cc4aa44 gallery upload: GPS fallback + auto-lookup
- GPS: when EXIF stripped by mobile browser, falls back to
  navigator.geolocation.getCurrentPosition() for device location
- OCR result now populates scanMachineId hidden field so Create works
- createFromScanPhoto also checks AppState.gpsLat as fallback
- Gallery OCR now auto-lookups via handleBarcode() — routes to confirm
  card (existing asset) or new asset form (not found)
- Manual entry button also calls handleBarcode() after prompt
2026-05-21 14:09:14 -04:00
shawn 7331b4e1aa barcode scan: add confirmation step before auto check-in
- Existing assets now show a confirm card with machine ID + ✓ Confirm & Check In / ✏️ Edit buttons
- Confirm triggers auto check-in then shows the result card
- Edit opens inline machine ID editor with Re-lookup
- Cancel button returns to original confirmation
- New asset flow unchanged (form already editable)
2026-05-21 14:03:59 -04:00
shawn 04544603ea Fix extra closing brace causing JS parse error 2026-05-21 12:26:07 -04:00
shawn 07a0212b6b Clean up orphaned functions causing JS errors 2026-05-21 12:23:29 -04:00
shawn 8480a6d364 Fix mode structure: Scan (barcode+OCR+gallery) + Manual only
- Removed separate OCR and Connect mode toggles — merged into Scan
- Renamed Barcode mode to Scan with OCR fallback + gallery upload
- Added captureScanOcr() — tap to capture frame for OCR when barcode fails
- handleBarcode() now compares barcode vs OCR result, picks valid ID
- Gallery photo picker integrated into Scan mode (OCR + EXIF GPS)
- Removed old photoPreviewCard and connect-mode HTML sections
- Removed orphaned functions referencing removed elements
2026-05-21 12:20:14 -04:00
shawn 37ba54b750 T5: Offline queue + background sync — fix missing offline banner, add API GET caching to SW
- Added missing #offlineBanner HTML element with queueCountBadge
  (showOfflineBanner/hideOfflineBanner were referencing it but it didn't exist)
- Enhanced SW: API GET calls now use network-first with cache fallback
  (POST/PUT/DELETE still pass through — main thread handles offline queuing)
- Bumped SW cache from canteen-v1 to canteen-v2
2026-05-21 12:01:05 -04:00
shawn 622996624a T4: Connect Label upload workflow — unified photo + OCR + GPS
- Added 'Connect' mode toggle on Add Asset tab with Camera/Gallery options
- processConnectLabelPhoto(file): parallel OCR + GPS extraction
- Shows results card with editable machine_id, lat/lng, map preview
- POST /api/connect-label endpoint with photo upload + GPS + reverse geocode
- Connect Label section in Assets list (localStorage-backed recent scans)
- Handles OCR failure (manual entry) and missing EXIF GPS gracefully
2026-05-21 11:50:09 -04:00
shawn f895bf99de T2: Photo gallery picker with preview 2026-05-21 11:20:20 -04:00
shawn 5cb8e8aa73 T1: Tesseract.js client-side OCR fallback 2026-05-21 11:08:16 -04:00
shawn c8973337c8 Clean up accidental db-shm/db-wal commit, add to gitignore 2026-05-21 07:45:35 -04:00
shawn 14687e7ba6 Fix Nav tab infinite recursion (pre-UX-sweep bug)
The reverted code had two bugs in the tab switching logic:
1. closeNavigate() called switchTab() -> infinite recursion since
   switchTab() calls closeNavigate() when leaving tabNavigate
2. currentTab was updated AFTER closeNavigate(), so the recursive
   call hit the same condition over and over

Fix: move currentTab assignment before cleanup, use a wasNavigating
flag to track the old tab state, and make closeNavigate() only reset
nav UI (caller handles tab transition).
2026-05-21 07:45:16 -04:00
shawn 62016bae83 Revert all UX sweep changes (T5-T11, 16 commits)
This reverts all commits from 828fd5c..bc8cc99, undoing the entire
canteen-ux-sweep board changes: Dashboard Fixes, Map Improvements,
Modal System, Navigate fixes, Empty States, Search & Filter, Reports,
Navigate Route-Finding UI, T6 cleanup, Page Transitions, Skeleton Loading.

Restores codebase to pre-UX-sweep state at d844429.
2026-05-21 07:36:58 -04:00
shawn bc8cc99280 T11a: Skeleton loading fade-out fixes
- Remove duplicate @keyframes shimmer (CSS conflict resolution)
- Remove dead .skeleton/.shimmer-stat/.shimmer-bar CSS classes
- Add hideSkeletons() fade-out to renderAssetList, renderCustList, renderActivity
- Dashboard and reports already had restore*Content() fade-out
2026-05-21 00:05:17 -04:00
shawn b398bc1469 T11b: Page transitions + micro-animations polish
- Remove duplicate FAB HTML element (was two #fabAdd buttons)
- Remove duplicate FAB CSS block (keep single definition with pulse class)
- Add .fab:hover to remaining FAB CSS block
- Convert all fab.style.display usage to hidden-fab CSS class for smooth transitions
- Add direction-aware tab transitions: forward slides from right, reverse (back) slides from left
- Fix FAB pulse persistence: restart animation on click instead of removing permanently
- Move FAB init code after FAB HTML element so hidden-fab applies on initial load
- Remove redundant style.display FAB visibility in switchTab (now handled by updateFabVisibility)

Tab transition animations already existed (slide+fade with cubic-bezier easing).
Button press/hover scale effects and .btn-pressed haptic class already existed.
Success toast slide-up already existed via translateY transition.
Consistent easing curve (cubic-bezier 0.4,0,0.2,1) already applied across transitions.
prefers-reduced-motion already respected (animation-duration: 0.01ms at line 1696).
2026-05-21 00:03:45 -04:00
shawn 111a412c3f T6 cleanup: remove scope-creep FAB, pull-to-refresh, skeleton loading — map features preserved 2026-05-20 23:49:23 -04:00
shawn 88694e6b8e T6 Map Page Improvements: geolocation fallback, location coordinates, friendly error messages
- Improved geolocation error messages (maps error codes to friendly text: Location blocked, GPS unavailable, GPS timed out)
- Added location data to list_assets API (location_latitude, longitude, address, building_name, building_number, name) via LEFT JOIN
- Map pins now use location coordinates as direct fallback when asset lacks own lat/lng
- GeocodeAndPin now falls back to location address fields when asset address is empty
- All 96 map-related tests pass (66 API + 30 frontend)
2026-05-20 23:33:47 -04:00
shawn 0aa9e1f0d8 T4b - Build Navigate Route-Finding UI
- Added GPS-aware empty state with location needed/gps off messaging
- Added GPS warning banner with 'enable location access' link in Navigate tab
- Show user's current location pin + accuracy circle on map when GPS available
- Enhanced destination search: GPS-aware filtering, badges for nav status
- Back button navigates to previous tab (or Map tab as fallback)
- Added navBackToPrevious() function with proper switchTab recursion avoidance
- Added CSS: .gps-warning, .gps-enable-link, .nav-route-info, .nav-route-item
- navigateToAsset() now goes to Nav tab even without GPS to show warning UI
- Improved search results: shows GPS on/off badge, disables non-navigable assets
- All navigation and smoke tests pass (5/5)
2026-05-20 23:23:01 -04:00
shawn b31f8e8a89 feat(navigate): GPS-aware empty state, route info CSS refactor, user location marker on map
- Add GPS warning banner with enable-location link when GPS unavailable
- Refactor route info cards from inline styles to CSS classes
- Update empty state messaging based on GPS availability
- Show user location marker with accuracy circle on nav map when GPS available
- Hide map container when no GPS (show only empty state + search)
2026-05-20 23:15:27 -04:00
shawn 845523c65b fix: toggle aria-hidden on modal open/close for screen reader accessibility 2026-05-20 23:10:19 -04:00
shawn 1a87427295 T1: Finalize empty states & onboarding — geofence point format fix
- Handle both [lat, lng] array and {lat, lng} object formats in geofence rendering
- All empty states verified: consistent icon + heading + description + CTA pattern
- Onboarding welcome overlay with localStorage flag
2026-05-20 23:00:07 -04:00
shawn 51f9bcfacd fix: reports page — add asset lat/lng to /api/visits for map button, fix null field guard in form error handler 2026-05-20 22:58:50 -04:00
shawn 40fdc9dca6 T3: Search & Filter UX improvements
- Add missing assetCountBar div with count bar styling
- Add missing clearFiltersBtn with show/hide logic
- Add .search-highlight CSS for matching term highlighting
- Add .ai-location CSS for location context in asset items
- Add loading spinner overlay during search debounce
- Fix filter pills to use JS string escaping (not HTML escaping)
- Add 'make' filter parameter to API query
- Include 'make' in filter detection for count bar
2026-05-20 22:53:50 -04:00
shawn 389f1f11d2 fix: Navigate tab switching — remove early return that prevented tab panel DOM swap
The switchTab function had an early return after closeNavigate() that
skipped the actual tab panel switching (slide animation, active class
management). closeNavigate() only cleans up navigation state (map, data,
UI elements) — it does NOT switch panels. This caused the Navigate
panel to remain visible permanently after the first visit, overlapping
with any subsequent tab content.

Removed the early return so the normal tab switching logic runs after
closeNavigate() cleanup.
2026-05-20 22:53:13 -04:00
shawn 6c1d5c3910 feat: add comprehensive empty states & onboarding
- Enhanced empty-state design pattern (icon + heading + description + CTA)
- Customers: 'No Customers Yet' with Add Customer CTA
- Locations: 'No Locations Yet' with Add Location CTA
- Dashboard: Full empty state when no assets exist, preserves activity card
- Activity Feed: Enhanced empty state with Add Asset CTA
- Reports: Guidance cards explaining what each section shows when empty
- Onboarding: Welcome overlay for first-time users (localStorage flag)
- CSS: empty-state-card, guidance-card, esc-* classes, onboard-overlay
2026-05-20 22:36:11 -04:00
shawn e66ed51e7f fix(navigate): fix Navigate tab routing, add empty state, route search, mode toggle
- Fixed switchTab recursion bug when leaving tabNavigate (set currentTab early + return)
- Fixed closeNavigate to not call switchTab (caller handles tab transition)
- Added renderNavPage call in switchTab lifecycle for tabNavigate
- Added empty state with asset search on Navigate page
- Added route-finding: search assets, navigate from search results
- Added driving/walking mode toggle with OSRM route display
- Added ETA display for driving mode
- Fixed route polyline to use OSRM road geometry when available
- Added switchNavMode function for mode switching
- Improved directions label (Walking vs Driving)
2026-05-20 22:35:49 -04:00
shawn fae2a84213 feat: custom modal system - alert/confirm/prompt/form, animations, ESC/click-outside close, focus trap, aria, full test coverage 2026-05-20 22:33:00 -04:00
shawn 0c0d3e6be3 T6 - Map Page Improvements: clustering, real GPS heatmap, dismissible GPS warning, address geocoding fallback, legend
- Add Leaflet.markercluster CDN + cluster group for dense asset pins
- Replace placebo heatmap with real check-in GPS data (/api/checkins/heatmap)
- GPS warning dismissible with ✕ button + re-enable option
- GPS-denied fallback: geocode asset addresses via /api/geocode-address when lat/lng missing
- Add map legend showing category colors for asset markers
- Fix modal HTML: add prompt area + modalActions id for prompt/modals
- Add modal prompt CSS, legend CSS, GPS badge dismiss styles
2026-05-20 22:32:13 -04:00
shawn 828fd5c482 T5 - Dashboard Fixes: active technicians, interactive bars, quick actions, caching
- Fix Active Technicians: query users WHERE role='technician' instead of counting from visit data
- Interactive bar charts: hover tooltips show label+count, click drills down to filtered asset list
- Quick action icons: coin for Assets, Sites, Check-in replaces CSV export buttons
- No visit data: suggestion card with 'Add a Check-in' button
- No manufacturer data: hide section entirely when empty
- 30-second TTL cache on both backend (/api/stats) and frontend (loadDashboard)
- Add dashboard cache module with time-based invalidation
2026-05-20 22:25:24 -04:00
shawn d844429f56 Fix: test_map_api.py DB isolation — set CANTEEN_DB_PATH inside client fixture
Previously relied on module-level env var which got overwritten
when frontend tests' conftest.py test_db_path fixture ran first.
Now explicitly sets env var inside client fixture + invalidates
caches before import. Also fixed importlib order.
2026-05-20 19:57:39 -04:00
shawn dd02fb36d1 Refactor: dedicated navigation page (tabNavigate)
- New tabNavigate panel with its own map, route info, walking directions, Google Maps link
- Added to bottom nav bar (🧭 Nav) and drawer nav (🧭 Navigate)
- navigateToAsset() now switches to tabNavigate instead of cluttering the map tab
- Back button returns to previous tab
- Auto-cleanup navMap when leaving the tab
2026-05-20 19:24:54 -04:00
shawn ffe9090668 Fix: move GPS geocode block outside payload object literal
The F2 auto-populate address block was accidentally inserted inside
the payload={} object literal, causing a JS syntax error that
broke the entire script and prevented the login screen from showing.
2026-05-20 18:35:23 -04:00
shawn d7f2e07c68 feat: 6 UX improvements for field technicians
F1: Scan existing asset → auto check-in (handleBarcode now autoCheckins)
F2: Auto-generate address from GPS reverse geocode (Nominatim, all 3 modes)
F3: Larger camera viewport (3:4 aspect ratio, full-width)
F4: Building # already syncs to Address/Trailer field (existing)
F5: Parking location GPS button already exists (fillGpsParking)
F6: Navigation tool with route line on map (existing navigateToAsset)

Backend: Added GET /api/geocode endpoint for reverse geocoding
2026-05-20 18:01:39 -04:00
shawn b69bffe44a feat: auto check-in on barcode scan of existing assets
- handleBarcode() now calls autoCheckin() after finding asset via barcode
- showScannedAsset() no longer shows manual checkin card
- Replace 'Check In' button with 'Auto checked in' indicator
- Add CSS for sr-checkin-done span
2026-05-20 17:57:04 -04:00
shawn c1020ac702 feat: add 📍 GPS button next to parking_location to fill current coordinates 2026-05-20 17:55:16 -04:00
shawn 02b3ba665e feat: oninput handler copies building # to trailer number
- Manual asset form: manBuildingNumber → manAddress (address/trailer field)
- Location form: locFormBldgNum → locFormTrailer
2026-05-20 17:55:00 -04:00