diff --git a/docs/route-access-flow.md b/docs/route-access-flow.md index db96123..f66c13c 100644 --- a/docs/route-access-flow.md +++ b/docs/route-access-flow.md @@ -16,11 +16,11 @@ The app now has an interactive phone-first driver flow: 6. `ROUTES_FOUND` returns zero or more selectable route choices. Each choice carries company guidance, route access identifiers, and its own short-lived route-scoped driver token. 7. From the driver's point of view, multi-company assignments are just multiple routes; each route card shows the company/shop and route metadata attached to that route. 8. The app records required `LOCATION_INFORMATION` and `PERSONAL_INFORMATION` consent through the selected route token, then loads assigned-route detail for each route choice. -9. Loaded routes render in `Pending`, `In Progress`, and `Completed` tabs; a route card can open detail or start delivery. -10. Delivery start requests foreground location permission and moves to `delivery_active` only when permission is granted. +9. Every created child route renders in `Ready` until delivery starts. Driver assignment does not change this execution state; a route card can open detail or start delivery. +10. Delivery start requests foreground location permission, records `ROUTE_STARTED`, and moves the route to `In progress` only when permission is granted. 11. Live tracking starts at the company/pickup step, then proceeds through ordered stops without presenting turn-by-turn instruction UI. 12. Each stop can play a local area tip, open stop details, capture required proof photo, and record optional delivery notes, location-specific tips, and additional notes. -13. Delivery finish stops continuous location, records or queues `ROUTE_COMPLETED`, and discards route-scoped local retry items only after route completion is recorded. +13. Delivery finish stops continuous location, records or queues `ROUTE_COMPLETED`, moves the route to `Completed`, and discards route-scoped local retry items only after route completion is recorded. 14. An authoritative empty route list, `NOT_FOUND`, or a missing prior assignment removes that route from the app and clears only route-scoped cache. Network errors retain the last safe cache. 15. `NO_ASSIGNED_ROUTE`, `DISABLED`, `BLOCKED`, and API errors stay in safe user-visible states without exposing other tenant/driver data. diff --git a/package-lock.json b/package-lock.json index ea336c5..c3ad945 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,17 +11,17 @@ "@expo/vector-icons": "^15.0.2", "@maplibre/maplibre-react-native": "^11.3.4", "@react-native-async-storage/async-storage": "2.2.0", - "expo": "~56.0.15", + "expo": "~56.0.16", "expo-camera": "~56.0.8", - "expo-dev-client": "~56.0.22", + "expo-dev-client": "~56.0.23", "expo-font": "~56.0.7", - "expo-image-picker": "~56.0.20", - "expo-location": "~56.0.20", - "expo-notifications": "~56.0.20", + "expo-image-picker": "~56.0.21", + "expo-location": "~56.0.21", + "expo-notifications": "~56.0.21", "expo-secure-store": "~56.0.4", "expo-speech": "~56.0.3", "expo-status-bar": "~56.0.4", - "expo-task-manager": "~56.0.21", + "expo-task-manager": "~56.0.22", "libphonenumber-js": "1.13.6", "react": "19.2.3", "react-native": "0.85.3", @@ -1843,15 +1843,15 @@ } }, "node_modules/@expo/config": { - "version": "56.0.11", - "resolved": "https://registry.npmjs.org/@expo/config/-/config-56.0.11.tgz", - "integrity": "sha512-Rt3U9Rqr6midz/sDeubFN5fefR0KzHpcEEgAUnV98XsLSSTnfzIxoC1blICb7pUUscKc8TtlVyn7/Gita2wnOQ==", + "version": "56.0.12", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-56.0.12.tgz", + "integrity": "sha512-8X+FtflatWdFqBFlPxtyNRaj2dzMCPzi5wImO8IbhOLRKQVWyXdoLb26j0WjpWZaN1qo3Kf90t5yIcWdbbnfVA==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~56.0.11", + "@expo/config-plugins": "~56.0.13", "@expo/config-types": "^56.0.7", "@expo/json-file": "^10.2.0", - "@expo/require-utils": "^56.1.4", + "@expo/require-utils": "^56.1.5", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", @@ -1861,15 +1861,15 @@ } }, "node_modules/@expo/config-plugins": { - "version": "56.0.12", - "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.12.tgz", - "integrity": "sha512-UKjPEOkvxBzTvjghmjUECURDoJLPJIFIevB0JQCe8l9Fg4yfy2fabU5LU3Kfrmmu1/8et/93bucCnABEmoxYEw==", + "version": "56.0.13", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-56.0.13.tgz", + "integrity": "sha512-BWtRkvUKLb4xaMLTRXmrHPHl+RI5nKF2MKJEXODHmLtVgUlXOz66XSCU4bEvjfr8yYTx+8DQ0JO54Y7Y0kwokA==", "license": "MIT", "dependencies": { "@expo/config-types": "^56.0.7", "@expo/json-file": "~10.2.0", "@expo/plist": "^0.7.0", - "@expo/require-utils": "^56.1.4", + "@expo/require-utils": "^56.1.5", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", @@ -1948,9 +1948,9 @@ "license": "MIT" }, "node_modules/@expo/fingerprint": { - "version": "0.19.7", - "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.19.7.tgz", - "integrity": "sha512-Q04NyJE0E7qKGXepBjI8e0p983RrQGBWJcSICKyyLczsr5JhNuSmqw604aL7koaXG2ctrUL36qd332XiMS/s6w==", + "version": "0.19.8", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.19.8.tgz", + "integrity": "sha512-zM0UMJLxYJnYq0+IzASGUYwQulENnC4qmXCAwJYmjD66AG+qTfyab4yTmL2gbiWyBAsWv0vPtP+ahEhtsT2CjQ==", "license": "MIT", "dependencies": { "@expo/env": "^2.3.1", @@ -1969,20 +1969,6 @@ "fingerprint": "bin/cli.js" } }, - "node_modules/@expo/fingerprint/node_modules/@expo/env": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.4.1.tgz", - "integrity": "sha512-3c9Mg9x0HmGPEsVrGAGyEDJsNUOZ55cZvZ47/HLmXh7MHV9Zv7My73wThklKrObaBBoMfE4YqpKjYKDRzojpjQ==", - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "debug": "^4.3.4", - "getenv": "^2.0.0" - }, - "engines": { - "node": ">=20.12.0" - } - }, "node_modules/@expo/fingerprint/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -2020,12 +2006,12 @@ } }, "node_modules/@expo/image-utils": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.2.tgz", - "integrity": "sha512-qQUGaacqXduoFTCUQAMceIWYzlKU0xX4/BKTyh8TdVj0uHv9/W3MfHOy5yXoOqBVrSccXk++Gm4D/NfS5HnCNA==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@expo/image-utils/-/image-utils-0.10.3.tgz", + "integrity": "sha512-FgVp21Q05GRi08XwkzdZbBb/sBgCcP66snxlyN/HyisdtTcSbTr1qV5wkXYjq+6Q1xllXiKLmCwjDvydoN2vyg==", "license": "MIT", "dependencies": { - "@expo/require-utils": "^56.1.4", + "@expo/require-utils": "^56.1.5", "@expo/spawn-async": "^1.8.0", "chalk": "^4.0.0", "getenv": "^2.0.0", @@ -2035,12 +2021,12 @@ } }, "node_modules/@expo/inline-modules": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@expo/inline-modules/-/inline-modules-0.0.13.tgz", - "integrity": "sha512-26RllWesRmYsAAo70cRcR9DaqXPKJct9MIGxZteS+Tkg25ljOkFeG7fYEsSazZOLpAslKBiilqtZykVYrxSzCw==", + "version": "0.0.14", + "resolved": "https://registry.npmjs.org/@expo/inline-modules/-/inline-modules-0.0.14.tgz", + "integrity": "sha512-jHrqsgpb5QnnwGfxD6NrUAMW9JcS1+j/e8GiLSy+12Md7aRnEWqi347xZ8Q57tkow1iXdeVLXPeyn1VmmCB8fA==", "license": "MIT", "dependencies": { - "@expo/config-plugins": "~56.0.11" + "@expo/config-plugins": "~56.0.13" } }, "node_modules/@expo/json-file": { @@ -2054,12 +2040,12 @@ } }, "node_modules/@expo/local-build-cache-provider": { - "version": "56.0.9", - "resolved": "https://registry.npmjs.org/@expo/local-build-cache-provider/-/local-build-cache-provider-56.0.9.tgz", - "integrity": "sha512-VMJC5ul7dXPfD2OO16ObPi6ym4H8vlnCBUuCM/KeKh0OvLRub35X8OB2uYrFQ+vbWkPemAEOWvk0oOptpPxbzA==", + "version": "56.0.10", + "resolved": "https://registry.npmjs.org/@expo/local-build-cache-provider/-/local-build-cache-provider-56.0.10.tgz", + "integrity": "sha512-ki4bnpcWaZRfOdcN1TYLuN02k+R8UaUJ9rQuSOKlj471ZnDcrAn/muugA8xHiIbZ/hS5HGNIy5z5A/unTPjVUw==", "license": "MIT", "dependencies": { - "@expo/config": "~56.0.11", + "@expo/config": "~56.0.12", "chalk": "^4.1.2" } }, @@ -2100,9 +2086,9 @@ } }, "node_modules/@expo/osascript": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.7.0.tgz", - "integrity": "sha512-wKIXL8UtbuX4KwavPasIW3CUcgTbYfjzLcgUhjyKUAYDEqMaf6gmU1bqz3ffBPTokmX+G8/vFG1ZuI9etQWukA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@expo/osascript/-/osascript-2.7.1.tgz", + "integrity": "sha512-Zn03EX6In7ts2lPUW2ESUSkEhEWQN1qqsiXjadtZMJOuZRkMiAg1ZQHuvz9DjByDWNJ2pBwAGyrts9lj9k389g==", "license": "MIT", "dependencies": { "@expo/spawn-async": "^1.8.0" @@ -2112,12 +2098,12 @@ } }, "node_modules/@expo/package-manager": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.13.0.tgz", - "integrity": "sha512-s3W3eZafJDEyVL7W/jxj2Nz3eONKxSCU604S5xj8ijrVaRz83x0DnZznLf/UXQEI1w+FyibH68nHeQyk767b1A==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.13.1.tgz", + "integrity": "sha512-y/K+CaYYpZpNGZhSX4HyLT/vyIunFjNfyoxNysPBCefeLKI/VCx6f9LNPzrxayr3rCYO5bl9O8H+HRQK265Nkg==", "license": "MIT", "dependencies": { - "@expo/json-file": "^11.0.0", + "@expo/json-file": "^11.0.1", "@expo/spawn-async": "^1.8.0", "chalk": "^4.0.0", "npm-package-arg": "^11.0.0", @@ -2126,9 +2112,9 @@ } }, "node_modules/@expo/package-manager/node_modules/@expo/json-file": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-11.0.0.tgz", - "integrity": "sha512-pHJCETqFL5x5BzNV6cEPwjwuECgGmnl0bNmfHIJ6LM1tlh2eVXi5HEdit3zby/JO/B8Otk5cgcqtJXgvvUat3A==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-11.0.1.tgz", + "integrity": "sha512-zxHWj4MKKMAL29ZQSY/Fssx4Thluk40JmuGNaeS078wy/NhlFhnVi+rHHunulE3xJAJ0CM73m8VK2+GkF9eRwQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -2147,27 +2133,27 @@ } }, "node_modules/@expo/prebuild-config": { - "version": "56.0.19", - "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-56.0.19.tgz", - "integrity": "sha512-aP/7kGDPMmwY9C9cYMK4MfUiZk23KhN4AfnKz0eeRyg3Izw0OGdtzNDLIcchnqpDAwHsI0+LUHZhucGl1jQJ5A==", + "version": "56.0.20", + "resolved": "https://registry.npmjs.org/@expo/prebuild-config/-/prebuild-config-56.0.20.tgz", + "integrity": "sha512-S8K1lg1cqREpnUtWAhDSOitTVJssFVhJAAdtNSi1mZaG/HL5QHUB+dogHIHijfdWKBj4KPGGjBfo6GqdmzzgtA==", "license": "MIT", "dependencies": { - "@expo/config": "~56.0.11", - "@expo/config-plugins": "~56.0.12", + "@expo/config": "~56.0.12", + "@expo/config-plugins": "~56.0.13", "@expo/config-types": "^56.0.7", - "@expo/image-utils": "^0.10.2", + "@expo/image-utils": "^0.10.3", "@expo/json-file": "^10.2.0", "@react-native/normalize-colors": "0.85.3", "debug": "^4.3.1", - "expo-modules-autolinking": "~56.0.19", + "expo-modules-autolinking": "~56.0.20", "resolve-from": "^5.0.0", "semver": "^7.6.0" } }, "node_modules/@expo/require-utils": { - "version": "56.1.4", - "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.4.tgz", - "integrity": "sha512-IX7XXg9obnrH3ni0TClBbVKgqk0nT0bjTEPTBtbD+WgIeZ0hpcgwxMJH3j5uBsUqEAq1jqUAkJWrPcg7ZRSV7g==", + "version": "56.1.5", + "resolved": "https://registry.npmjs.org/@expo/require-utils/-/require-utils-56.1.5.tgz", + "integrity": "sha512-3wsdJLSpELhRPEMZCCREqAAFRGG23BUWJHGKIUxyqGkWvy5KPdzlBxGlVVb/6h9gNOflqgTtvTWaP2EuHoQ1HQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", @@ -5400,31 +5386,31 @@ } }, "node_modules/expo": { - "version": "56.0.15", - "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.15.tgz", - "integrity": "sha512-Tnas9Sq1fDY865rhSQ4266Kd4GULEUyBEBEchbNLQsiY6U+AAt4MgCKJUsRvUkNHdaZwnGzexy6hdnOjsgvNcA==", + "version": "56.0.16", + "resolved": "https://registry.npmjs.org/expo/-/expo-56.0.16.tgz", + "integrity": "sha512-6G1yfBKG3J16HBgF2ehdd1iZBlRmbDF/lB7SG30v4jojgvN03HZkENHujKfuGuBqDBlzkkXjZvpxRVvIUQTAMQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.0", - "@expo/cli": "^56.1.19", - "@expo/config": "~56.0.11", - "@expo/config-plugins": "~56.0.12", + "@expo/cli": "^56.1.20", + "@expo/config": "~56.0.12", + "@expo/config-plugins": "~56.0.13", "@expo/devtools": "~56.0.2", "@expo/dom-webview": "~56.0.6", - "@expo/fingerprint": "^0.19.7", - "@expo/local-build-cache-provider": "^56.0.9", + "@expo/fingerprint": "^0.19.8", + "@expo/local-build-cache-provider": "^56.0.10", "@expo/log-box": "^56.0.14", "@expo/metro": "~56.0.0", - "@expo/metro-config": "~56.0.16", + "@expo/metro-config": "~56.0.17", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~56.0.17", - "expo-asset": "~56.0.19", - "expo-constants": "~56.0.20", + "expo-asset": "~56.0.20", + "expo-constants": "~56.0.21", "expo-file-system": "~56.0.8", "expo-font": "~56.0.7", "expo-keep-awake": "~56.0.3", - "expo-modules-autolinking": "~56.0.19", - "expo-modules-core": "~56.0.20", + "expo-modules-autolinking": "~56.0.20", + "expo-modules-core": "~56.0.21", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" @@ -5491,9 +5477,9 @@ } }, "node_modules/expo-constants": { - "version": "56.0.20", - "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.20.tgz", - "integrity": "sha512-4HgoVvUiMvcqujr/CUj7L1tumLWj8WX0PLM77OriDPoaJrMEwUHd841m4o4IoUyMPVT8XTiTiPFwJtGali8+9Q==", + "version": "56.0.21", + "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-56.0.21.tgz", + "integrity": "sha512-q9LkK9tNTW2z/5hXgpwWoCXO1fBrqn4FgwTiLerM9WOu1wtg99NevAtG3BEG8t5Awtub+NsLidiFZzBat45RKw==", "license": "MIT", "dependencies": { "@expo/env": "~2.3.1" @@ -5504,13 +5490,13 @@ } }, "node_modules/expo-dev-client": { - "version": "56.0.22", - "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-56.0.22.tgz", - "integrity": "sha512-LKhgIlMu8DkmhTtfurpX9YMPsnv4QI1hCcDZqcFn+jlB+s6yE1THB9UWZ+BVtef8kYMzsw+BH3ClXvNkRd0vXw==", + "version": "56.0.23", + "resolved": "https://registry.npmjs.org/expo-dev-client/-/expo-dev-client-56.0.23.tgz", + "integrity": "sha512-3yQc4McvFTxqFpQe0wZ/Ek9bLXIy/ilnHBPy1UqUjzFfarJNd5FUoWp+1ovE8Gt59OPiIsKl+VzPq330hwQxkg==", "license": "MIT", "dependencies": { - "expo-dev-launcher": "~56.0.23", - "expo-dev-menu": "~56.0.19", + "expo-dev-launcher": "~56.0.24", + "expo-dev-menu": "~56.0.20", "expo-dev-menu-interface": "~56.0.0", "expo-manifests": "~56.0.4", "expo-updates-interface": "~56.0.1" @@ -5520,13 +5506,13 @@ } }, "node_modules/expo-dev-launcher": { - "version": "56.0.23", - "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-56.0.23.tgz", - "integrity": "sha512-OKgLAn6m15Lu+Qyg8WA0HPurFR9pxhsrHUvvafv8pTZHspB0BbOvBwhOJdxgiMfR2LHq4032cnMf8164Vzqeng==", + "version": "56.0.24", + "resolved": "https://registry.npmjs.org/expo-dev-launcher/-/expo-dev-launcher-56.0.24.tgz", + "integrity": "sha512-uzYYpWY2rdLUS+ct9Loku4LGdMp7XwBHPAhuHu/R0SMHl4IxFx8VLFAbOaO34NrDWqB6fqdze1qHoA6DxeuZIA==", "license": "MIT", "dependencies": { "@expo/schema-utils": "^56.0.2", - "expo-dev-menu": "~56.0.19", + "expo-dev-menu": "~56.0.20", "expo-manifests": "~56.0.4" }, "peerDependencies": { @@ -5535,9 +5521,9 @@ } }, "node_modules/expo-dev-menu": { - "version": "56.0.19", - "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-56.0.19.tgz", - "integrity": "sha512-sm6dfnkq3lgbn39Kd4yjFFm7UQAZqm6Hn3OpjagGtWBoWByQae3nKh1MF5K4Peh172TqUPJTOhjRasnZSkUkOQ==", + "version": "56.0.20", + "resolved": "https://registry.npmjs.org/expo-dev-menu/-/expo-dev-menu-56.0.20.tgz", + "integrity": "sha512-PJH+RtOWDV7E5oic7cLKN7+AO/oE3QPnHzooxwzGrcjiySCQOvkFC67SNfBCxXXaMFMRf2MwihqY4PllAVAUyw==", "license": "MIT", "dependencies": { "expo-dev-menu-interface": "~56.0.0" @@ -5580,9 +5566,9 @@ } }, "node_modules/expo-image-picker": { - "version": "56.0.20", - "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-56.0.20.tgz", - "integrity": "sha512-75xP7WA7Z8b4+5wYCu1cISg90Cu9CowneGMFuAgmmpNxCXLnBbrJXO6XqYIM69KAjEv7PApjgQF3h6RdBqus4w==", + "version": "56.0.21", + "resolved": "https://registry.npmjs.org/expo-image-picker/-/expo-image-picker-56.0.21.tgz", + "integrity": "sha512-0MH22LBIAhfDPXSAAsXdlcwRIz+Pn+O90h15ZnA1ZZD+H7mjSvsAyNwYVH/DM2ypjOgy0gL2+hvbDSNZSYh2eQ==", "license": "MIT", "dependencies": { "expo-image-loader": "~56.0.3" @@ -5598,12 +5584,12 @@ "license": "MIT" }, "node_modules/expo-location": { - "version": "56.0.20", - "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-56.0.20.tgz", - "integrity": "sha512-yQhkHgjW47dz7cbRg1UcsGWuWNQ1Yh+nEqmZkAdFdDVw6PXuxV63c8VNyXBzvuOgNuBd2TSstSuOb0t3tFJJZg==", + "version": "56.0.21", + "resolved": "https://registry.npmjs.org/expo-location/-/expo-location-56.0.21.tgz", + "integrity": "sha512-HTwhlF9eWwRI/LSI0JW08Y96aYfcGpScbEn7xIH4PgxUC80kHOHI5CP0v/GlkIzT2Qhfr6cP9Jw9wnPdBfV//g==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.10.2" + "@expo/image-utils": "^0.10.3" }, "peerDependencies": { "expo": "*" @@ -5622,12 +5608,12 @@ } }, "node_modules/expo-modules-autolinking": { - "version": "56.0.19", - "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.19.tgz", - "integrity": "sha512-ztzTzS21fbq4oJQItSgT+WWJMZXGsC0GdX/cDN/GBhCTPiZz8foHkVa14DiLa5CWd7pEZ9EeSMdHDcAEc2dZ5Q==", + "version": "56.0.20", + "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-56.0.20.tgz", + "integrity": "sha512-bIUaHtWlqQpculjhN1wERVBoVeRCefkW3zXY/SqiXSm/6uofZy3NRF1t5Co44lmBQczAs/lFR7BwA8lmUiGMnw==", "license": "MIT", "dependencies": { - "@expo/require-utils": "^56.1.4", + "@expo/require-utils": "^56.1.5", "@expo/spawn-async": "^1.8.0", "chalk": "^4.1.0", "commander": "^7.2.0" @@ -5637,9 +5623,9 @@ } }, "node_modules/expo-modules-core": { - "version": "56.0.20", - "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.20.tgz", - "integrity": "sha512-Xagwt/gC6sV1jiSnlFrYxLooZr90adLrpV64YlkXZjgZ6Kot89FF62e457RyejLtyDm2pOdy6FZphF1ReyK2Jw==", + "version": "56.0.21", + "resolved": "https://registry.npmjs.org/expo-modules-core/-/expo-modules-core-56.0.21.tgz", + "integrity": "sha512-lT413DZFuX/cSsOqGtZnqtyCGlY3F3FFDiVhwzB8wX4Ei9HZYPXhV59Mm8SNJquo8+JfLRi3arv3z5U4MqJRcA==", "license": "MIT", "dependencies": { "@expo/expo-modules-macros-plugin": "0.2.2", @@ -5667,16 +5653,16 @@ } }, "node_modules/expo-notifications": { - "version": "56.0.20", - "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-56.0.20.tgz", - "integrity": "sha512-+LsF3O51IDoIk68xxp9q9sSwNDlZWWsykWouQ+jCfXZOa0mi86elRBqWV4IXN4YOTGbx9zYvnIzkga0F0Qar/Q==", + "version": "56.0.21", + "resolved": "https://registry.npmjs.org/expo-notifications/-/expo-notifications-56.0.21.tgz", + "integrity": "sha512-j1rczqoAj0oBZYzcckMyILlJSLQdqOJuh7YzAckzTHkMuIPa7H0SF6IwcFIig1Tdqjku3aONqct6bbscY+qznw==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.10.2", + "@expo/image-utils": "^0.10.3", "abort-controller": "^3.0.0", "badgin": "^1.1.5", "expo-application": "~56.0.3", - "expo-constants": "~56.0.20" + "expo-constants": "~56.0.21" }, "peerDependencies": { "expo": "*", @@ -5723,9 +5709,9 @@ } }, "node_modules/expo-task-manager": { - "version": "56.0.21", - "resolved": "https://registry.npmjs.org/expo-task-manager/-/expo-task-manager-56.0.21.tgz", - "integrity": "sha512-yo0F2bnowFn/YbOidyuDTINbAxg/amFCL+XuCElFQEoK9/KsBJ5bwJbiPJl+rHP9ruUw1OMRuHieYjMCrus5QQ==", + "version": "56.0.22", + "resolved": "https://registry.npmjs.org/expo-task-manager/-/expo-task-manager-56.0.22.tgz", + "integrity": "sha512-EtYC7o5phj1nGQbdXwSF1fIm9Ws5oP/+wCMWS/Tl15OS3vrsG1INEyptKCNVoEEuXZ15wdhSlmp7J4Y2P5uO/A==", "license": "MIT", "dependencies": { "unimodules-app-loader": "~56.0.0" @@ -5745,28 +5731,28 @@ } }, "node_modules/expo/node_modules/@expo/cli": { - "version": "56.1.19", - "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.19.tgz", - "integrity": "sha512-k+oipwbu83WFnACtybvGZvDbbItarY4lfjkKgP9F5lml2ilvL/VvzsPhbhT7nRyFB3zPJq9SbNNKa45VDLoihA==", + "version": "56.1.20", + "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-56.1.20.tgz", + "integrity": "sha512-xWkKvOBkOE3G/PpU29fjmAjKpCIqTIVSxG8oA+E6pYUElcPZ9gRofAdTE321lUoJVHwEjKmpn93EHqsajItDTQ==", "license": "MIT", "dependencies": { "@expo/code-signing-certificates": "^0.0.6", - "@expo/config": "~56.0.11", - "@expo/config-plugins": "~56.0.12", + "@expo/config": "~56.0.12", + "@expo/config-plugins": "~56.0.13", "@expo/devcert": "^1.2.1", "@expo/env": "~2.3.1", - "@expo/image-utils": "^0.10.2", - "@expo/inline-modules": "^0.0.13", + "@expo/image-utils": "^0.10.3", + "@expo/inline-modules": "^0.0.14", "@expo/json-file": "^10.2.0", "@expo/log-box": "^56.0.14", "@expo/metro": "~56.0.0", - "@expo/metro-config": "~56.0.16", + "@expo/metro-config": "~56.0.17", "@expo/metro-file-map": "^56.0.3", "@expo/osascript": "^2.6.0", "@expo/package-manager": "^1.12.1", "@expo/plist": "^0.7.0", - "@expo/prebuild-config": "^56.0.19", - "@expo/require-utils": "^56.1.4", + "@expo/prebuild-config": "^56.0.20", + "@expo/require-utils": "^56.1.5", "@expo/router-server": "^56.0.16", "@expo/schema-utils": "^56.0.2", "@expo/spawn-async": "^1.8.0", @@ -5889,19 +5875,19 @@ } }, "node_modules/expo/node_modules/@expo/metro-config": { - "version": "56.0.16", - "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-56.0.16.tgz", - "integrity": "sha512-mxPZ23exC6kkEpPYQOteamaiWYER3uDk2IqKys7EJwtIKc3F1207Xtsdko/DNNG04DLwpN/WM2UlNl+Ak8uPRg==", + "version": "56.0.17", + "resolved": "https://registry.npmjs.org/@expo/metro-config/-/metro-config-56.0.17.tgz", + "integrity": "sha512-kxCDDCTPbd29gqVhic7fGAPzXbW+fNWerDeXNJdaoO7rutEwogEeud7UHpX4G0fZNMbio1eM5mAQa+edCm/IRw==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", - "@expo/config": "~56.0.11", + "@expo/config": "~56.0.12", "@expo/env": "~2.3.1", "@expo/json-file": "~10.2.0", "@expo/metro": "~56.0.0", - "@expo/require-utils": "^56.1.4", + "@expo/require-utils": "^56.1.5", "@expo/spawn-async": "^1.8.0", "@jridgewell/gen-mapping": "^0.3.13", "@jridgewell/remapping": "^2.3.5", @@ -6010,13 +5996,13 @@ } }, "node_modules/expo/node_modules/expo-asset": { - "version": "56.0.19", - "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-56.0.19.tgz", - "integrity": "sha512-huGY0bVfYUivNOir+iUEjjW9IbNHzLFNo8d6FGh22u1OsXcFR7Za3vpu5V1gMp0dc31wiqmsXkTazjm+Rro3vA==", + "version": "56.0.20", + "resolved": "https://registry.npmjs.org/expo-asset/-/expo-asset-56.0.20.tgz", + "integrity": "sha512-pjVw3JDiaq6Ee15UbDeZpiGNAHJqfOZwY61VPlFiznuuj7f/b2tvegujeIEu2+p6C2/tioZGGD6zWQuJ9Ob3uQ==", "license": "MIT", "dependencies": { - "@expo/image-utils": "^0.10.2", - "expo-constants": "~56.0.20" + "@expo/image-utils": "^0.10.3", + "expo-constants": "~56.0.21" }, "peerDependencies": { "expo": "*", diff --git a/package.json b/package.json index 289034f..71bae18 100644 --- a/package.json +++ b/package.json @@ -23,17 +23,17 @@ "@expo/vector-icons": "^15.0.2", "@maplibre/maplibre-react-native": "^11.3.4", "@react-native-async-storage/async-storage": "2.2.0", - "expo": "~56.0.15", + "expo": "~56.0.16", "expo-camera": "~56.0.8", - "expo-dev-client": "~56.0.22", + "expo-dev-client": "~56.0.23", "expo-font": "~56.0.7", - "expo-image-picker": "~56.0.20", - "expo-location": "~56.0.20", - "expo-notifications": "~56.0.20", + "expo-image-picker": "~56.0.21", + "expo-location": "~56.0.21", + "expo-notifications": "~56.0.21", "expo-secure-store": "~56.0.4", "expo-speech": "~56.0.3", "expo-status-bar": "~56.0.4", - "expo-task-manager": "~56.0.21", + "expo-task-manager": "~56.0.22", "libphonenumber-js": "1.13.6", "react": "19.2.3", "react-native": "0.85.3", diff --git a/src/app/AppRoot.tsx b/src/app/AppRoot.tsx index 505d91f..1da6923 100644 --- a/src/app/AppRoot.tsx +++ b/src/app/AppRoot.tsx @@ -2217,8 +2217,8 @@ function MyRoutesPage({ {isRouteCardExpanded ? '−' : '+'} @@ -2580,11 +2580,11 @@ function RouteSessionScreen({ const depotIsProcessing = routeStatus === 'active' && currentNavigationStepIndex === COMPANY_STEP_INDEX; const depotMeta = depotIsProcessing ? 'Pickup' : routeStatus === 'completed' || currentNavigationStepIndex > COMPANY_STEP_INDEX ? 'Done' : undefined; const depotMetaTone = depotIsProcessing ? 'blue' : 'green'; - const depotState = routeStatus === 'upcoming' || depotIsProcessing ? 'current' : 'completed'; + const depotState = routeStatus === 'ready' || depotIsProcessing ? 'current' : 'completed'; const currentTaskTitle = isCompanyStep ? 'Company Pickup' : stop === null ? 'Next Stop' : `Stop ${stop.sequence}`; const currentTaskAddress = isCompanyStep ? company?.pickupGuidance ?? 'Pickup point' : stop === null ? 'Stop address' : formatStopAddress(stop); const currentTaskPayment = stop === null ? null : formatAssignedRoutePaymentStatus(stop.normalizedPaymentStatus); - const primaryProgressAction = routeStatus === 'upcoming' + const primaryProgressAction = routeStatus === 'ready' ? { disabled: isStartingRoute, label: 'Start Session', loading: isStartingRoute, onPress: onStartRoute } : routeStatus === 'active' && allStopsCompleted ? { disabled: isFinishingRoute, label: 'Finish Route', loading: isFinishingRoute, onPress: onFinishRoute } @@ -2754,7 +2754,7 @@ function LiveTrackingScreen({ - + {payment !== null ? ( @@ -3806,7 +3806,7 @@ function getRouteStatus(deliveryStartResult: DeliveryStartResult | null, deliver return 'active'; } - return 'upcoming'; + return 'ready'; } function formatRouteStatus(status: RouteStatus): string { @@ -3815,10 +3815,8 @@ function formatRouteStatus(status: RouteStatus): string { return 'In progress'; case 'completed': return 'Completed'; - case 'unfinished': - return 'Unfinished'; - case 'upcoming': - return 'Pending'; + case 'ready': + return 'Ready'; } } @@ -3923,9 +3921,7 @@ function getChipTone(status: RouteStatus): 'blue' | 'green' | 'neutral' { return 'blue'; case 'completed': return 'green'; - case 'unfinished': - return 'neutral'; - case 'upcoming': + case 'ready': return 'neutral'; } } diff --git a/src/app/routeListBehavior.test.ts b/src/app/routeListBehavior.test.ts index 959b901..0121869 100644 --- a/src/app/routeListBehavior.test.ts +++ b/src/app/routeListBehavior.test.ts @@ -74,7 +74,7 @@ describe('routes list behavior', () => { assert.match(source, /setCollapsedRouteKey\(\(value\) => value === activeRouteCollapseKey \? null : activeRouteCollapseKey\)/u); assert.match(source, /\{activeSession\.route\.name\}<\/Text>/u); assert.match(source, /\{activeSession\.route\.deliveryDate\}<\/Text>/u); - assert.match(source, /label=\{formatRouteStatus\(activeRouteStatus \?\? 'upcoming'\)\}/u); + assert.match(source, /label=\{formatRouteStatus\(activeRouteStatus \?\? 'ready'\)\}/u); assert.doesNotMatch(source, / { assert.match(noAssignedRouteSource, /resetRouteProgress\(\)/u); assert.match(noAssignedRouteSource, /clearCachedRouteAccess\(\)/u); assert.doesNotMatch(noAssignedRouteSource, /setMessage\(/u); - assert.doesNotMatch(source, /Signed in\. No current or upcoming route is assigned/u); + assert.doesNotMatch(source, /Signed in\. No current or ready route is assigned/u); assert.match(source, /assignedRouteResult\.kind !== 'no_assigned_route'/u); assert.match(appStateRefreshSource, /screen === 'mainTabs'/u); assert.doesNotMatch(appStateRefreshSource, /selectedMainTab === 'routes'/u); diff --git a/src/domain/driverFlow/driverFlow.test.ts b/src/domain/driverFlow/driverFlow.test.ts index e1c6063..f9fe31e 100644 --- a/src/domain/driverFlow/driverFlow.test.ts +++ b/src/domain/driverFlow/driverFlow.test.ts @@ -57,7 +57,7 @@ describe('driver app MVP flow', () => { getMvpScenarioScreens().map((screen) => screen.title), [ 'Login / Driver Verification', - 'Upcoming Routes', + 'My Routes', 'Route Details', 'Route Session', 'Live Tracking', @@ -69,11 +69,10 @@ describe('driver app MVP flow', () => { ); }); - it('shows route lists by English delivery status tabs', () => { + it('shows route lists by lifecycle status tabs', () => { assert.deepEqual(getMvpRouteTabs(), [ - { id: 'upcoming', label: 'Pending' }, - { id: 'active', label: 'In Progress' }, - { id: 'unfinished', label: 'Unfinished' }, + { id: 'ready', label: 'Ready' }, + { id: 'active', label: 'In progress' }, { id: 'completed', label: 'Completed' }, ]); }); diff --git a/src/domain/driverFlow/driverFlow.ts b/src/domain/driverFlow/driverFlow.ts index ef92a4d..1a0e056 100644 --- a/src/domain/driverFlow/driverFlow.ts +++ b/src/domain/driverFlow/driverFlow.ts @@ -36,8 +36,8 @@ export type MvpScenarioScreen = { }; export type MvpRouteTab = { - id: 'active' | 'completed' | 'unfinished' | 'upcoming'; - label: 'Completed' | 'In Progress' | 'Pending' | 'Unfinished'; + id: 'active' | 'completed' | 'ready'; + label: 'Completed' | 'In progress' | 'Ready'; }; export type StopCompletionProofField = { @@ -75,9 +75,9 @@ export function getMvpScenarioScreens(): MvpScenarioScreen[] { }, { id: 'routeList', - title: 'Upcoming Routes', + title: 'My Routes', purpose: - 'Show current, unfinished, and future assigned routes from nearest date to farthest, grouped into Pending, In Progress, Unfinished, and Completed tabs.', + 'Show assigned routes from nearest date to farthest, grouped into Ready, In progress, and Completed tabs.', primaryAction: 'Start Session', }, // routeDetail is metadata for the read-only preview entry; the live operational screen is routeSession. @@ -135,9 +135,8 @@ export function getMvpScenarioScreens(): MvpScenarioScreen[] { export function getMvpRouteTabs(): MvpRouteTab[] { return [ - { id: 'upcoming', label: 'Pending' }, - { id: 'active', label: 'In Progress' }, - { id: 'unfinished', label: 'Unfinished' }, + { id: 'ready', label: 'Ready' }, + { id: 'active', label: 'In progress' }, { id: 'completed', label: 'Completed' }, ]; } diff --git a/src/domain/route/assignedRoute.test.ts b/src/domain/route/assignedRoute.test.ts index 9283d4d..e08ded6 100644 --- a/src/domain/route/assignedRoute.test.ts +++ b/src/domain/route/assignedRoute.test.ts @@ -80,7 +80,7 @@ describe('driver assigned route UX flow', () => { assert.deepEqual(result, { flowState: 'consent_recorded', kind: 'no_assigned_route', - message: 'No current or upcoming route is available for this driver and route context.', + message: 'No ready or in-progress route is available for this driver and route context.', }); }); @@ -330,7 +330,7 @@ describe('driver assigned route UX flow', () => { it('defines tab-level synthetic validation scenarios with safe coordinates and OSRM evidence expectations', () => { assert.deepEqual( assignedRouteValidationScenarios.map((scenario) => scenario.tab), - ['upcoming', 'active', 'completed'], + ['ready', 'active', 'completed'], ); for (const scenario of assignedRouteValidationScenarios) { diff --git a/src/domain/route/assignedRoute.ts b/src/domain/route/assignedRoute.ts index 031929d..b3b86fc 100644 --- a/src/domain/route/assignedRoute.ts +++ b/src/domain/route/assignedRoute.ts @@ -315,7 +315,7 @@ export async function loadAssignedRouteAfterConsent( return { flowState: 'consent_recorded', kind: 'no_assigned_route', - message: 'No current or upcoming route is available for this driver and route context.', + message: 'No ready or in-progress route is available for this driver and route context.', }; } diff --git a/src/domain/route/assignedRouteValidationScenarios.ts b/src/domain/route/assignedRouteValidationScenarios.ts index aa6ed7b..e52ed7d 100644 --- a/src/domain/route/assignedRouteValidationScenarios.ts +++ b/src/domain/route/assignedRouteValidationScenarios.ts @@ -1,6 +1,6 @@ import type { AssignedRoute } from './assignedRoute'; -export type AssignedRouteValidationTab = 'active' | 'completed' | 'upcoming'; +export type AssignedRouteValidationTab = 'active' | 'completed' | 'ready'; export type AssignedRouteValidationScenario = { expectedEvidence: { @@ -23,13 +23,13 @@ export const assignedRouteValidationScenarios: AssignedRouteValidationScenario[] hasOsrmMetrics: true, safeForOperationalSmoke: true, }, - id: 'validation-upcoming-osrm-ready', - label: 'Upcoming tab — synthetic Toronto route with OSRM geometry and route-level metrics', + id: 'validation-ready-osrm-route', + label: 'Ready tab — synthetic Toronto route with OSRM geometry and route-level metrics', route: buildValidationRoute({ deliveryDate: '2026-06-04', id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaa1', metrics: { distanceMeters: 6120.4, durationSeconds: 1680 }, - name: '[TEST] OSRM Upcoming Route', + name: '[TEST] OSRM Ready Route', stops: [ validationStop({ address1: '100 King St W', @@ -38,7 +38,7 @@ export const assignedRouteValidationScenarios: AssignedRouteValidationScenario[] longitude: -79.3817, orderName: '#TEST-OSRM-1001', postalCode: 'M5X 1A9', - recipientName: 'TEST Recipient Upcoming 1', + recipientName: 'TEST Recipient Ready 1', sequence: 1, }), validationStop({ @@ -49,12 +49,12 @@ export const assignedRouteValidationScenarios: AssignedRouteValidationScenario[] longitude: -79.3909, orderName: '#TEST-OSRM-1002', postalCode: 'M5V 1Z2', - recipientName: 'TEST Recipient Upcoming 2', + recipientName: 'TEST Recipient Ready 2', sequence: 2, }), ], }), - tab: 'upcoming', + tab: 'ready', }, { expectedEvidence: { diff --git a/src/domain/route/routeSessionClassification.test.ts b/src/domain/route/routeSessionClassification.test.ts index 382f22f..2aebb3d 100644 --- a/src/domain/route/routeSessionClassification.test.ts +++ b/src/domain/route/routeSessionClassification.test.ts @@ -11,7 +11,7 @@ import { const now = new Date('2026-06-02T13:00:00.000Z'); describe('assigned route session classification', () => { - it('retains past assigned routes as unfinished instead of dropping them', () => { + it('retains past unstarted assigned routes as ready instead of a fourth unfinished state', () => { const sessions = [ session('past', '2026-05-30', 'America/Toronto'), session('today', '2026-06-02', 'America/Toronto'), @@ -22,19 +22,10 @@ describe('assigned route session classification', () => { filterVisibleAssignedRouteSessions(sessions, { now, selectedRouteId: null, - selectedRouteStatus: 'upcoming', - selectedTab: 'unfinished', + selectedRouteStatus: 'ready', + selectedTab: 'ready', }).map((item) => item.route.id), - ['past'], - ); - assert.deepEqual( - filterVisibleAssignedRouteSessions(sessions, { - now, - selectedRouteId: null, - selectedRouteStatus: 'upcoming', - selectedTab: 'upcoming', - }).map((item) => item.route.id), - ['today', 'future'], + ['past', 'today', 'future'], ); }); @@ -46,18 +37,18 @@ describe('assigned route session classification', () => { now: boundaryNow, route: route('toronto-today', '2026-06-01', 'America/Toronto'), selectedRouteId: null, - selectedRouteStatus: 'upcoming', + selectedRouteStatus: 'ready', }), - 'upcoming', + 'ready', ); assert.equal( classifyAssignedRouteSession({ now: boundaryNow, route: route('seoul-past', '2026-06-01', 'Asia/Seoul'), selectedRouteId: null, - selectedRouteStatus: 'upcoming', + selectedRouteStatus: 'ready', }), - 'unfinished', + 'ready', ); }); @@ -73,7 +64,7 @@ describe('assigned route session classification', () => { ); }); - it('lets current-session completion override unfinished only for the selected route', () => { + it('lets current-session completion override ready only for the selected route', () => { assert.equal( classifyAssignedRouteSession({ now, @@ -90,17 +81,17 @@ describe('assigned route session classification', () => { selectedRouteId: 'selected', selectedRouteStatus: 'completed', }), - 'unfinished', + 'ready', ); }); - it('opens the first loaded past assigned route on the Unfinished tab', () => { + it('opens the first loaded past assigned route on the Ready tab', () => { assert.equal( getInitialAssignedRouteTab({ now, route: route('past-assigned', '2026-05-30', 'America/Toronto'), }), - 'unfinished', + 'ready', ); }); }); diff --git a/src/domain/route/routeSessionClassification.ts b/src/domain/route/routeSessionClassification.ts index ba8e5ef..216577e 100644 --- a/src/domain/route/routeSessionClassification.ts +++ b/src/domain/route/routeSessionClassification.ts @@ -1,4 +1,4 @@ -export type RouteSessionStatus = 'active' | 'completed' | 'unfinished' | 'upcoming'; +export type RouteSessionStatus = 'active' | 'completed' | 'ready'; export type RouteSessionClassificationRoute = { deliveryDate: string; @@ -28,10 +28,10 @@ export function classifyAssignedRouteSession(input: RouteSessionClassificationIn now: input.now, timezone: input.route.timezone, })) { - return 'unfinished'; + return 'ready'; } - return 'upcoming'; + return 'ready'; } export function filterVisibleAssignedRouteSessions( @@ -54,7 +54,7 @@ export function getInitialAssignedRouteTab(input: { now: input.now, route: input.route, selectedRouteId: null, - selectedRouteStatus: 'upcoming', + selectedRouteStatus: 'ready', }); }