diff --git a/app.json b/app.json index 44e6854..a479d0f 100644 --- a/app.json +++ b/app.json @@ -1,11 +1,11 @@ { "expo": { - "name": "app", + "name": "PharmaTech", "slug": "pharmatech", "scheme": "pharmatech", "version": "0.2.0", "orientation": "portrait", - "icon": "./src/assets/images/PharmaTech-icon.png", + "icon": "./src/assets/images/Pharmatech_icon_1024.png", "userInterfaceStyle": "light", "newArchEnabled": true, "splash": { @@ -22,7 +22,7 @@ "android": { "permissions": ["ACCESS_FINE_LOCATION", "ACCESS_COARSE_LOCATION"], "adaptiveIcon": { - "foregroundImage": "./src/assets/images/PharmaTech-icon.png", + "foregroundImage": "./src/assets/images/Pharmatech_icon_1024.png", "backgroundColor": "#1C2143" }, "package": "com.pharmatech.pharmatech", diff --git a/declarations.d.ts b/declarations.d.ts index 933d520..805b73c 100644 --- a/declarations.d.ts +++ b/declarations.d.ts @@ -7,3 +7,12 @@ declare module '*.jpg' { const value: string; export default value; } + +declare module '@mapbox/polyline' { + interface Polyline { + decode(encoded: string, precision?: number): [number, number][]; + encode(coordinates: [number, number][], precision?: number): string; + } + const polyline: Polyline; + export = polyline; +} diff --git a/package-lock.json b/package-lock.json index 64e45d6..66f7062 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.0", "dependencies": { "@expo-google-fonts/poppins": "^0.2.3", + "@mapbox/polyline": "^1.2.1", "@pharmatech/sdk": "^0.4.21", "@ptomasroos/react-native-multi-slider": "^2.2.2", "@react-native-community/slider": "^4.5.6", @@ -3529,6 +3530,17 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/polyline": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@mapbox/polyline/-/polyline-1.2.1.tgz", + "integrity": "sha512-sn0V18O3OzW4RCcPoUIVDWvEGQaBNH9a0y5lgqrf5hUycyw1CzrhEoxV5irzrMNXKCkw1xRsZXcaVbsVZggHXA==", + "dependencies": { + "meow": "^9.0.0" + }, + "bin": { + "polyline": "bin/polyline.bin.js" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4787,6 +4799,12 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.13.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.5.tgz", @@ -4803,6 +4821,12 @@ "@types/node": "*" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -5623,6 +5647,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -6328,6 +6361,23 @@ "node": ">= 6" } }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001700", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", @@ -7097,6 +7147,40 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decimal.js": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", @@ -9340,6 +9424,15 @@ "node": ">= 10.x" } }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -10127,6 +10220,15 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -12067,6 +12169,18 @@ "tmpl": "1.0.5" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/marky": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/marky/-/marky-1.2.5.tgz", @@ -12114,6 +12228,53 @@ "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -12554,6 +12715,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -12810,6 +12985,63 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -13970,6 +14202,15 @@ } ] }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14775,6 +15016,153 @@ "node": ">=0.10.0" } }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -14843,7 +15231,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -15870,6 +16257,38 @@ "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "license": "CC0-1.0" + }, "node_modules/split": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", @@ -16857,6 +17276,15 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ts-api-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", @@ -17264,6 +17692,16 @@ "node": ">=10.12.0" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/validate-npm-package-name": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", diff --git a/package.json b/package.json index 0fe1cfc..aabcaf1 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@expo-google-fonts/poppins": "^0.2.3", + "@mapbox/polyline": "^1.2.1", "@pharmatech/sdk": "^0.4.21", "@ptomasroos/react-native-multi-slider": "^2.2.2", "@react-native-community/slider": "^4.5.6", diff --git a/src/assets/images/Pharmatech_icon_1024.png b/src/assets/images/Pharmatech_icon_1024.png new file mode 100644 index 0000000..a91ee61 Binary files /dev/null and b/src/assets/images/Pharmatech_icon_1024.png differ diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 8e492cb..546b92e 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -18,6 +18,7 @@ const ProductCard: React.FC = ({ category, originalPrice, discount, + promo, finalPrice, getQuantity, }) => { @@ -70,6 +71,7 @@ const ProductCard: React.FC = ({ quantity, image: imageUrl, discount: discount ?? 0, + promo, }); }} initialValue={getItemQuantity(id)} diff --git a/src/components/DeliveryMap.tsx b/src/components/DeliveryMap.tsx index 2ce4eee..aa73bf4 100644 --- a/src/components/DeliveryMap.tsx +++ b/src/components/DeliveryMap.tsx @@ -6,6 +6,7 @@ import { Colors } from '../styles/theme'; import PoppinsText from '../components/PoppinsText'; import { MapPinIcon, UserIcon } from 'react-native-heroicons/solid'; import { FontAwesome5 } from '@expo/vector-icons'; +import polyline from '@mapbox/polyline'; interface DeliveryMapProps { deliveryState: number; @@ -47,7 +48,13 @@ const DeliveryMap: React.FC = ({ const data = await response.json(); if (data.routes && data.routes.length > 0) { - const points = decodePolyline(data.routes[0].overview_polyline.points); + const decoded = polyline.decode( + data.routes[0].overview_polyline.points, + ); + const points = decoded.map(([latitude, longitude]) => ({ + latitude, + longitude, + })); setRoute(points); } else { console.error('No se pudo obtener la ruta.'); @@ -108,45 +115,6 @@ const DeliveryMap: React.FC = ({ } }, [deliveryLocation, branchLocation, customerLocation]); - // Decodificar la polyline de Google Maps - const decodePolyline = (encoded: string) => { - const points: { latitude: number; longitude: number }[] = []; - let index = 0; - const len = encoded.length; - let lat = 0, - lng = 0; - - while (index < len) { - let b, - shift = 0, - result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - const dlat = result & 1 ? ~(result >> 1) : result >> 1; - lat += dlat; - - shift = 0; - result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - const dlng = result & 1 ? ~(result >> 1) : result >> 1; - lng += dlng; - - points.push({ - latitude: lat / 1e5, - longitude: lng / 1e5, - }); - } - - return points; - }; - if ( // Solo espera si faltan datos críticos branchLocation.latitude === 0 || diff --git a/src/components/HistoryMap.tsx b/src/components/HistoryMap.tsx index 0f0d229..6fc25d9 100644 --- a/src/components/HistoryMap.tsx +++ b/src/components/HistoryMap.tsx @@ -3,7 +3,7 @@ import MapView, { Marker, Polyline } from 'react-native-maps'; import { StyleSheet, View, Alert, ViewStyle } from 'react-native'; import { Config } from '../config'; import { Colors } from '../styles/theme'; - +import polyline from '@mapbox/polyline'; import { MapPinIcon, UserIcon } from 'react-native-heroicons/solid'; import { FontAwesome5 } from '@expo/vector-icons'; @@ -74,7 +74,13 @@ const HistoryMap: React.FC = ({ const data = await response.json(); if (data.routes && data.routes.length > 0) { - const points = decodePolyline(data.routes[0].overview_polyline.points); + const decoded = polyline.decode( + data.routes[0].overview_polyline.points, + ); + const points = decoded.map(([latitude, longitude]) => ({ + latitude, + longitude, + })); setRoute(points); } else { console.error('No se pudo obtener la ruta.'); @@ -84,45 +90,6 @@ const HistoryMap: React.FC = ({ } }; - // Decodificar la polyline de Google Maps - const decodePolyline = (encoded: string) => { - const points: { latitude: number; longitude: number }[] = []; - let index = 0; - const len = encoded.length; - let lat = 0, - lng = 0; - - while (index < len) { - let b, - shift = 0, - result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - const dlat = result & 1 ? ~(result >> 1) : result >> 1; - lat += dlat; - - shift = 0; - result = 0; - do { - b = encoded.charCodeAt(index++) - 63; - result |= (b & 0x1f) << shift; - shift += 5; - } while (b >= 0x20); - const dlng = result & 1 ? ~(result >> 1) : result >> 1; - lng += dlng; - - points.push({ - latitude: lat / 1e5, - longitude: lng / 1e5, - }); - } - - return points; - }; - return ( { const { cartItems } = useCart(); const [isOpen, setIsOpen] = useState(false); + const dollarPrice = useDollarPrice(); const subtotal = cartItems.reduce( (sum, item) => sum + item.price * item.quantity, @@ -114,12 +116,27 @@ const OrderSummary = () => { -${formatPrice(totalDiscount)} - + {/* Mostrar total en bolívares */} + {dollarPrice && ( + + + Total en Bs + + + Bs{' '} + {((total / 100) * dollarPrice).toLocaleString('es-VE', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} + + + )} + {/* Total ${formatPrice(total)} - + */} )} @@ -249,6 +266,12 @@ const styles = StyleSheet.create({ color: Colors.textMain, marginTop: 4, }, + bolivarText: { + fontSize: FontSizes.b1.size, + lineHeight: FontSizes.b1.lineHeight, + color: Colors.primary, + marginBottom: 8, + }, }); export default OrderSummary; diff --git a/src/config.ts b/src/config.ts index 26c8d4d..e09cddc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,3 +4,4 @@ export const Config = { }; console.log('Variables de entorno disponibles:', process.env); +console.log('Google Maps API Key usada en JS:', Config.googleMapsApiKey); diff --git a/src/hooks/useDollarPrice.ts b/src/hooks/useDollarPrice.ts new file mode 100644 index 0000000..7afcdd2 --- /dev/null +++ b/src/hooks/useDollarPrice.ts @@ -0,0 +1,14 @@ +import { useEffect, useState } from 'react'; + +export function useDollarPrice() { + const [dollarPrice, setDollarPrice] = useState(null); + + useEffect(() => { + fetch('https://pydolarve.org/api/v1/dollar?page=bcv&monitor=usd') + .then((res) => res.json()) + .then((data) => setDollarPrice(data.price)) + .catch(() => setDollarPrice(null)); + }, []); + + return dollarPrice; +} diff --git a/src/redux/slices/cartSlice.ts b/src/redux/slices/cartSlice.ts index 017b1d9..20c4c2e 100644 --- a/src/redux/slices/cartSlice.ts +++ b/src/redux/slices/cartSlice.ts @@ -1,4 +1,5 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import type { Promo } from '@pharmatech/sdk'; export type CartItem = { id: string; @@ -7,6 +8,7 @@ export type CartItem = { quantity: number; image: string; discount?: number; + promo?: Promo; }; type CartState = { @@ -40,10 +42,10 @@ const cartSlice = createSlice({ (item) => item.id === action.payload.id, ); if (existing) { - // Si ya existe, actualiza cantidad y descuento correctamente existing.quantity = action.payload.quantity; existing.price = action.payload.price; - existing.discount = action.payload.discount; // <-- ¡Asegúrate de actualizar el descuento! + existing.discount = action.payload.discount; + existing.promo = action.payload.promo; } else { state.items.push({ ...action.payload }); } @@ -60,6 +62,7 @@ const cartSlice = createSlice({ quantity: number; discount?: number; price?: number; + promo?: Promo; }>, ) => { const item = state.items.find((item) => item.id === action.payload.id); @@ -71,6 +74,9 @@ const cartSlice = createSlice({ if (typeof action.payload.price === 'number') { item.price = action.payload.price; } + if (action.payload.promo) { + item.promo = action.payload.promo; + } } state.total = calculateTotal(state.items); }, diff --git a/src/screens/CartListScreen.tsx b/src/screens/CartListScreen.tsx index 35762bc..da499f3 100644 --- a/src/screens/CartListScreen.tsx +++ b/src/screens/CartListScreen.tsx @@ -15,27 +15,36 @@ import Button from '../components/Button'; import { TrashIcon } from 'react-native-heroicons/outline'; import { useRouter } from 'expo-router'; import { formatPrice } from '../utils/formatPrice'; +import { isPromoActive } from '../utils/promoUtils'; +import { useDollarPrice } from '../hooks/useDollarPrice'; const CartListScreen = () => { const router = useRouter(); const { cartItems, removeFromCart, updateCartQuantity } = useCart(); + const dollarPrice = useDollarPrice(); const subtotal = cartItems.reduce( (sum, item) => sum + item.price * item.quantity, 0, ); - // Total price sum + const totalDiscount = cartItems.reduce( (sum, item) => - sum + item.price * item.quantity * ((item.discount ?? 0) / 100), + sum + + item.price * + item.quantity * + ((item.promo && isPromoActive(item.promo) ? item.promo.discount : 0) / + 100), 0, - ); // Discount sum - const total = subtotal - totalDiscount; // Subtotal with discount + ); + + const total = subtotal - totalDiscount; const renderItem = ({ item }: { item: CartItem }) => { console.log('[CartListScreen] Renderizando item:', item); // <-- LOG // Usar el descuento del item, si existe, si no 0 - const discount = item.discount ?? 0; + const discount = + item.promo && isPromoActive(item.promo) ? item.promo.discount : 0; const discountedPrice = item.price * (1 - discount / 100); const totalDiscountedPrice = discountedPrice * item.quantity; const totalOriginalPrice = item.price * item.quantity; @@ -144,6 +153,21 @@ const CartListScreen = () => { -${formatPrice(totalDiscount)} + {/* Mostrar total en bolívares */} + {dollarPrice && ( + + + Total en Bs + + + Bs{' '} + {((total / 100) * dollarPrice).toLocaleString('es-VE', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} + + + )} Total @@ -161,7 +185,6 @@ const CartListScreen = () => { )} - ); }; @@ -282,9 +305,6 @@ const styles = StyleSheet.create({ color: Colors.textMain, marginBottom: 20, }, - height: { - height: 64, - }, row: { flexDirection: 'row', justifyContent: 'space-between', @@ -336,6 +356,12 @@ const styles = StyleSheet.create({ emptyCartButton: { marginTop: 16, }, + bolivarText: { + fontSize: FontSizes.b1.size, + lineHeight: FontSizes.b1.lineHeight, + color: Colors.primary, + marginBottom: 8, + }, }); export default CartListScreen; diff --git a/src/screens/DirectionScreen.tsx b/src/screens/DirectionScreen.tsx index 0a0f58a..12f4871 100644 --- a/src/screens/DirectionScreen.tsx +++ b/src/screens/DirectionScreen.tsx @@ -202,7 +202,7 @@ const styles = StyleSheet.create({ paddingTop: -20, }, height: { - height: 64, + height: 32, }, alertContainer: { position: 'absolute', diff --git a/src/screens/InProgressOrderScreen.tsx b/src/screens/InProgressOrderScreen.tsx index cebbf75..c625ba6 100644 --- a/src/screens/InProgressOrderScreen.tsx +++ b/src/screens/InProgressOrderScreen.tsx @@ -34,6 +34,7 @@ import { useRouter } from 'expo-router'; import { useDispatch } from 'react-redux'; import { clearCart } from '../redux/slices/cartSlice'; import { resetCheckout } from '../redux/slices/checkoutSlice'; +import { formatPrice } from '../utils/formatPrice'; const stepsLabels = [ 'Opciones de Compra', @@ -251,7 +252,7 @@ const InProgressOrderScreen = () => { Total: - ${order.totalPrice ? order.totalPrice : ''} + ${order.totalPrice ? formatPrice(order.totalPrice) : ''} @@ -349,7 +350,7 @@ const InProgressOrderScreen = () => { Total: - ${order.totalPrice ? order.totalPrice : ''} + ${order.totalPrice ? formatPrice(order.totalPrice) : ''} diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx index 716c083..672bb40 100644 --- a/src/screens/LoginScreen.tsx +++ b/src/screens/LoginScreen.tsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import { View, - Image, TouchableOpacity, StyleSheet, KeyboardAvoidingView, @@ -15,7 +14,7 @@ import Input from '../components/Input'; import PoppinsText from '../components/PoppinsText'; import { Colors, FontSizes } from '../styles/theme'; import Logo from '../assets/images/logos/PharmaTech_Logo.svg'; -import GoogleLogo from '../assets/images/logos/Google_Logo.png'; +//import GoogleLogo from '../assets/images/logos/Google_Logo.png'; import { AuthService } from '../services/auth'; import { UserService } from '../services/user'; import Alert from '../components/Alerts'; @@ -86,10 +85,10 @@ export default function LoginScreen() { router.push('/passwordRecovery'); }; - const handleGoogleLogin = () => { - console.log('Iniciar sesión con Google'); - // Google login logic - }; + // const handleGoogleLogin = () => { + // console.log('Iniciar sesión con Google'); + // // Google login logic + // }; const handleRegister = () => { router.replace('/register'); @@ -173,7 +172,7 @@ export default function LoginScreen() { loading={loading} /> {/* Google button */} - @@ -185,7 +184,7 @@ export default function LoginScreen() { Iniciar sesión con Google - + */} {/* Register link */} - @@ -210,9 +209,6 @@ const styles = StyleSheet.create({ zIndex: 100, elevation: 10, }, - height: { - height: 64, - }, safeArea: { flex: 1, }, diff --git a/src/screens/OrdersScreen.tsx b/src/screens/OrdersScreen.tsx index d2865b8..5433cbf 100644 --- a/src/screens/OrdersScreen.tsx +++ b/src/screens/OrdersScreen.tsx @@ -188,7 +188,6 @@ const OrdersScreen = () => { ))} - ); @@ -201,9 +200,6 @@ const styles = StyleSheet.create({ padding: 20, paddingTop: -20, }, - height: { - height: 64, - }, alertContainer: { position: 'absolute', width: 326, diff --git a/src/screens/ProductDetailScreen.tsx b/src/screens/ProductDetailScreen.tsx index 80c2f73..e08c84e 100644 --- a/src/screens/ProductDetailScreen.tsx +++ b/src/screens/ProductDetailScreen.tsx @@ -38,6 +38,7 @@ import Button from '../components/Button'; import { BranchService } from '../services/branches'; import { formatPrice } from '../utils/formatPrice'; import Alert from '../components/Alerts'; +import { isPromoActive } from '../utils/promoUtils'; const ProductDetailScreen: React.FC = () => { const { id, productId } = useLocalSearchParams<{ @@ -290,7 +291,11 @@ const ProductDetailScreen: React.FC = () => { const getProductDiscount = () => { // Busca promo en product.promo - if (product?.promo && typeof product.promo.discount === 'number') { + if ( + product?.promo && + typeof product.promo.discount === 'number' && + isPromoActive(product.promo) + ) { return product.promo.discount; } return 0; @@ -638,10 +643,17 @@ const ProductDetailScreen: React.FC = () => { } // Si hay stock y cantidad válida, agrega o actualiza en el carrito if (product?.id && quantity > 0) { - const promo = product.promo; - const discount = - typeof promo?.discount === 'number' ? promo.discount : 0; - updateCartQuantity(product.id, quantity); + const promo = + product.promo && isPromoActive(product.promo) + ? product.promo + : undefined; + const discount = promo ? promo.discount : 0; + updateCartQuantity( + product.id, + quantity, + discount, + product.price, + ); addToCart({ id: product.id, name: @@ -656,6 +668,7 @@ const ProductDetailScreen: React.FC = () => { quantity, image: images?.[0]?.url || 'https://via.placeholder.com/150', discount, + promo, }); } }} diff --git a/src/screens/delivery/DeliveryHomeScreen.tsx b/src/screens/delivery/DeliveryHomeScreen.tsx index 3485722..502fd41 100644 --- a/src/screens/delivery/DeliveryHomeScreen.tsx +++ b/src/screens/delivery/DeliveryHomeScreen.tsx @@ -28,13 +28,6 @@ import { } from '../../lib/deliverySocket/deliverySocket'; // Importar funciones de socket import { Socket } from 'socket.io-client'; // Importar el tipo Socket -// Definir el tipo para un leg de la API de Google Maps Directions -interface GoogleMapsLeg { - duration: { - value: number; // Duración en segundos - }; -} - const calculateTravelTime = async ( origin: { latitude: number; longitude: number }, destination: { latitude: number; longitude: number }, @@ -45,17 +38,20 @@ const calculateTravelTime = async ( ); const data = await response.json(); - if (data.routes && data.routes.length > 0) { - // Extraer el tiempo estimado en segundos + if ( + data.routes && + data.routes.length > 0 && + data.routes[0].legs && + data.routes[0].legs.length > 0 + ) { const durationInSeconds = data.routes[0].legs.reduce( - (total: number, leg: GoogleMapsLeg) => total + leg.duration.value, + (total: number, leg: { duration: { value: number } }) => + total + (leg.duration?.value || 0), 0, ); - - // Convertir a minutos return Math.ceil(durationInSeconds / 60); } else { - console.error('No se pudo calcular el tiempo estimado.'); + console.error('No se pudo calcular el tiempo estimado.', data); return 0; } } catch (error) { @@ -87,6 +83,23 @@ export default function DeliveryHomeScreen() { const fetchAssignedOrders = async () => { try { + // Solicitar permisos de ubicación antes de obtener la posición + const { status } = await Location.getForegroundPermissionsAsync(); + if (status !== 'granted') { + const { status: newStatus } = + await Location.requestForegroundPermissionsAsync(); + if (newStatus !== 'granted') { + showAlert( + 'error', + 'Permiso denegado', + 'No se pudo obtener la ubicación. No se pueden calcular los tiempos estimados.', + ); + setLoading(false); + setRefreshing(false); + return; + } + } + const jwt = await SecureStore.getItemAsync('auth_token'); if (!jwt) { throw new Error('Token de autenticación no encontrado'); diff --git a/src/screens/tab/HomeScreen.tsx b/src/screens/tab/HomeScreen.tsx index af1aa33..ad9942c 100644 --- a/src/screens/tab/HomeScreen.tsx +++ b/src/screens/tab/HomeScreen.tsx @@ -18,6 +18,7 @@ import type { Promo } from '@pharmatech/sdk'; import EmailVerificationModal from './EmailVerificationModal'; import { decodeJWT } from '../../helper/jwtHelper'; import { useNotifications } from '../../hooks/useNotifications'; +import { isPromoActive } from '../../utils/promoUtils'; export default function HomeScreen() { const [products, setProducts] = useState([]); @@ -73,15 +74,10 @@ export default function HomeScreen() { const productsData = await ProductService.getProducts(1, 20); if (productsData.success) { const pd = productsData.data.results; - // Filtra solo los que tengan stock > 0 const availableProducts = pd.filter((p) => p.stock > 0); const carouselProducts = availableProducts.map((p) => { - // Usa el descuento real si hay promo, si no, no lo agregues // @ts-expect-error: promo puede estar en p o en p.presentation const promo: Promo | undefined = p.promo ?? p.presentation.promo; - const discount: Promo['discount'] | undefined = promo?.discount - ? Math.round(promo.discount * 100) / 100 - : undefined; return { id: p.id, presentationId: p.presentation.id, @@ -97,7 +93,9 @@ export default function HomeScreen() { p.presentation.measurementUnit, category: p.product.categories[0].name, originalPrice: p.price, - ...(discount !== undefined ? { discount } : {}), + discount: + promo && isPromoActive(promo) ? promo.discount : undefined, + promo: promo && isPromoActive(promo) ? promo : undefined, finalPrice: p.price, quantity: getItemQuantity(p.id), getQuantity: (quantity: number) => { @@ -125,9 +123,8 @@ export default function HomeScreen() { // Usa el descuento real si hay promo, si no, no lo agregues // @ts-expect-error: promo puede estar en p o en p.presentation const promo: Promo | undefined = p.promo ?? p.presentation.promo; - const discount: Promo['discount'] | undefined = promo?.discount - ? Math.round(promo.discount * 100) / 100 - : undefined; + const discount: Promo['discount'] | undefined = + promo && isPromoActive(promo) ? promo.discount : undefined; return { id: p.id, presentationId: p.presentation.id, diff --git a/src/screens/tab/OffersScreen.tsx b/src/screens/tab/OffersScreen.tsx index adf3708..52c9117 100644 --- a/src/screens/tab/OffersScreen.tsx +++ b/src/screens/tab/OffersScreen.tsx @@ -11,8 +11,11 @@ import { Colors, FontSizes } from '../../styles/theme'; import Card from '../../components/Card'; import { ProductService } from '../../services/products'; import type { Promo } from '@pharmatech/sdk'; +import { isPromoActive } from '../../utils/promoUtils'; +import { useCart } from '../../hooks/useCart'; export default function OffersScreen() { + const { addToCart, updateCartQuantity, getItemQuantity } = useCart(); const [offers, setOffers] = useState< Array<{ id: string; @@ -23,6 +26,7 @@ export default function OffersScreen() { category: string; originalPrice: number; discount: number; + promo?: Promo; finalPrice: number; quantity: number; getQuantity: (quantity: number) => void; @@ -44,32 +48,55 @@ export default function OffersScreen() { (item) => hasPromo(item) && Boolean(item.promo?.discount), ); - const mappedPromoItems = promoItems.map((p) => { - const promo: Promo | undefined = hasPromo(p) ? p.promo : undefined; - const discount: Promo['discount'] = promo?.discount ?? 0; + const mappedPromoItems = promoItems + .filter((p) => hasPromo(p) && isPromoActive(p.promo)) + .map((p) => { + const promo: Promo | undefined = hasPromo(p) ? p.promo : undefined; + const discount: Promo['discount'] = promo?.discount ?? 0; - return { - id: p.id, - presentationId: p.presentation.id, - productId: p.product.id, - imageUrl: - p.product.images?.[0]?.url || 'https://via.placeholder.com/150', - name: - p.product.name + - ' ' + - p.presentation.name + - ' ' + - p.presentation.quantity + - ' ' + - p.presentation.measurementUnit, - category: p.product.categories?.[0]?.name || 'Sin categoría', - originalPrice: p.price, - discount, - finalPrice: p.price, - quantity: 0, - getQuantity: () => 0, - }; - }); + return { + id: p.id, + presentationId: p.presentation.id, + productId: p.product.id, + imageUrl: + p.product.images?.[0]?.url || 'https://via.placeholder.com/150', + name: + p.product.name + + ' ' + + p.presentation.name + + ' ' + + p.presentation.quantity + + ' ' + + p.presentation.measurementUnit, + category: p.product.categories?.[0]?.name || 'Sin categoría', + originalPrice: p.price, + discount, + promo, + finalPrice: p.price, + quantity: getItemQuantity(p.id), + getQuantity: (quantity: number) => { + addToCart({ + id: p.id, + name: + p.product.name + + ' ' + + p.presentation.name + + ' ' + + p.presentation.quantity + + ' ' + + p.presentation.measurementUnit, + price: p.price, + quantity, + image: + p.product.images?.[0]?.url || + 'https://via.placeholder.com/150', + discount, + promo, + }); + updateCartQuantity(p.id, quantity, discount, p.price); + }, + }; + }); setOffers(mappedPromoItems); } else { console.error('Error al cargar productos:', res.error); @@ -100,6 +127,7 @@ export default function OffersScreen() { category: string; originalPrice: number; discount: number; + promo?: Promo; finalPrice: number; quantity: number; getQuantity: (quantity: number) => void; @@ -114,6 +142,7 @@ export default function OffersScreen() { category={item.category} originalPrice={item.originalPrice} discount={item.discount} + promo={item.promo} finalPrice={item.finalPrice} quantity={item.quantity} getQuantity={item.getQuantity} @@ -126,16 +155,24 @@ export default function OffersScreen() { Ofertas especiales - item.id} - numColumns={2} - contentContainerStyle={styles.list} - renderItem={renderOffer} - columnWrapperStyle={styles.columnWrapper} - onRefresh={loadOffers} - refreshing={loading} - /> + {offers.length === 0 ? ( + + + No hay ofertas disponibles en este momento. + + + ) : ( + item.id} + numColumns={2} + contentContainerStyle={styles.list} + renderItem={renderOffer} + columnWrapperStyle={styles.columnWrapper} + onRefresh={loadOffers} + refreshing={loading} + /> + )} ); @@ -169,4 +206,15 @@ const styles = StyleSheet.create({ height: { height: 64, }, + emptyContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingVertical: 40, + }, + emptyText: { + fontSize: FontSizes.s1.size, + color: Colors.textLowContrast, + textAlign: 'center', + }, }); diff --git a/src/types/Product.ts b/src/types/Product.ts index a759249..20c884d 100644 --- a/src/types/Product.ts +++ b/src/types/Product.ts @@ -1,3 +1,5 @@ +import type { Promo } from '@pharmatech/sdk'; + export type Product = { id: string; presentationId: string; @@ -7,6 +9,7 @@ export type Product = { category: string; originalPrice?: number; discount?: number; + promo?: Promo; finalPrice: number; quantity: number; getQuantity: (quantity: number) => void; diff --git a/src/utils/promoUtils.ts b/src/utils/promoUtils.ts new file mode 100644 index 0000000..36c50e4 --- /dev/null +++ b/src/utils/promoUtils.ts @@ -0,0 +1,10 @@ +export function isPromoActive(promo?: { + startAt?: Date | string; + expiredAt?: Date | string; +}) { + if (!promo?.startAt || !promo?.expiredAt) return false; + const now = new Date(); + const start = new Date(promo.startAt); + const end = new Date(promo.expiredAt); + return now >= start && now <= end; +}