diff --git a/package-lock.json b/package-lock.json index 5eea0da7..15e45315 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,12 +47,14 @@ "lodash": "^4.17.21", "lucide-react": "^0.381.0", "mapbox-gl": "^3.4.0", + "maplibre-gl": "^4.3.2", "nanoid": "^5.0.7", "next": "^14.2.3", "next-auth": "^4.23.2", "next-i18next": "^15.3.0", "next-themes": "^0.3.0", "nookies": "^2.5.2", + "radar-sdk-js": "^4.3.0", "react": "^18.2.0", "react-cookie": "^7.1.4", "react-dom": "^18.2.0", @@ -856,6 +858,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "dependencies": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + }, + "bin": { + "geojson-rewind": "geojson-rewind" + } + }, "node_modules/@mapbox/jsonlint-lines-primitives": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", @@ -900,6 +914,26 @@ "node": ">=6.0.0" } }, + "node_modules/@maplibre/maplibre-gl-style-spec": { + "version": "20.3.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.3.0.tgz", + "integrity": "sha512-eSiQ3E5LUSxAOY9ABXGyfNhout2iEa6mUxKeaQ9nJ8NL1NuaQYU7zKqzx/LEYcXe1neT4uYAgM1wYZj3fTSXtA==", + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "~2.0.2", + "@mapbox/unitbezier": "^0.0.1", + "json-stringify-pretty-compact": "^4.0.0", + "minimist": "^1.2.8", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "sort-object": "^3.0.3", + "tinyqueue": "^2.0.3" + }, + "bin": { + "gl-style-format": "dist/gl-style-format.mjs", + "gl-style-migrate": "dist/gl-style-migrate.mjs", + "gl-style-validate": "dist/gl-style-validate.mjs" + } + }, "node_modules/@neondatabase/serverless": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/@neondatabase/serverless/-/serverless-0.7.2.tgz", @@ -2276,8 +2310,15 @@ "node_modules/@types/geojson": { "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", - "dev": true + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + }, + "node_modules/@types/geojson-vt": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", + "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", + "dependencies": { + "@types/geojson": "*" + } }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", @@ -2300,12 +2341,32 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/junit-report-builder": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", + "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==" + }, "node_modules/@types/lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.4.tgz", "integrity": "sha512-wYCP26ZLxaT3R39kiN2+HcJ4kTd3U1waI/cY7ivWYqFP6pW3ZNpvi6Wd6PHZx7T/t8z0vlkXMg3QYLa7DZ/IJQ==", "dev": true }, + "node_modules/@types/mapbox__point-geometry": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", + "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==" + }, + "node_modules/@types/mapbox__vector-tile": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", + "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, "node_modules/@types/mapbox-gl": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.1.0.tgz", @@ -2323,6 +2384,11 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==" + }, "node_modules/@types/pg": { "version": "8.6.6", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.6.6.tgz", @@ -2368,6 +2434,14 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==" }, + "node_modules/@types/supercluster": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/supercluster/-/supercluster-7.1.3.tgz", + "integrity": "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA==", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", @@ -2888,6 +2962,14 @@ "dequal": "^2.0.3" } }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -3056,6 +3138,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -3266,6 +3356,23 @@ "node": ">=10.16.0" } }, + "node_modules/bytewise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", + "integrity": "sha512-rHuuseJ9iQ0na6UDhnrRVDh8YnWVlU6xM3VH6q/+yHDeUH2zIhUzP+2/h3LIrhLDBtTqzWpE3p3tP/boefskKQ==", + "dependencies": { + "bytewise-core": "^1.2.2", + "typewise": "^1.0.3" + } + }, + "node_modules/bytewise-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bytewise-core/-/bytewise-core-1.2.3.tgz", + "integrity": "sha512-nZD//kc78OOxeYtRlVk8/zXqTB4gf/nlguL1ggWA8FuchMyOxcyHR4QPQZMUmA7czC+YnaBrPUCubqAWe50DaA==", + "dependencies": { + "typewise-core": "^1.2" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -4460,6 +4567,17 @@ "node": ">=0.8.x" } }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4730,6 +4848,17 @@ "node": ">=6" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-symbol-description": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", @@ -4759,6 +4888,14 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gl-matrix": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", @@ -4802,6 +4939,30 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -5109,6 +5270,11 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -5259,6 +5425,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5516,6 +5690,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -5670,6 +5852,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-pretty-compact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==" + }, "node_modules/json5": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", @@ -5751,6 +5938,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -5950,6 +6145,47 @@ "vt-pbf": "^3.1.3" } }, + "node_modules/maplibre-gl": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.3.2.tgz", + "integrity": "sha512-/oXDsb9I+LkjweL/28aFMLDZoIcXKNEhYNAZDLA4xgTNkfvKQmV/r0KZdxEMcVthincJzdyc6Y4N8YwZtHKNnQ==", + "dependencies": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@maplibre/maplibre-gl-style-spec": "^20.2.0", + "@types/geojson": "^7946.0.14", + "@types/geojson-vt": "3.2.5", + "@types/junit-report-builder": "^3.0.2", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "global-prefix": "^3.0.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^2.0.0", + "supercluster": "^8.0.1", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + }, + "engines": { + "node": ">=16.14.0", + "npm": ">=8.1.0" + }, + "funding": { + "url": "https://github.com/maplibre/maplibre-gl-js?sponsor=1" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6011,7 +6247,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7143,6 +7378,17 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, + "node_modules/radar-sdk-js": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/radar-sdk-js/-/radar-sdk-js-4.3.0.tgz", + "integrity": "sha512-tmj5W6DdHNfm7x51Ns6pJ6bBx4Olcj65LHh54BRJNsguGoWVTfDaGEKOzao76whDv9V/oLsl2HKW2IDFZ+RXEQ==", + "dependencies": { + "@types/geojson": "^7946.0.10" + }, + "peerDependencies": { + "maplibre-gl": "^2.4.0 || ^3.0.0 || ^4.0.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -7689,6 +7935,31 @@ "node": ">= 0.4" } }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sh-syntax": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/sh-syntax/-/sh-syntax-0.4.2.tgz", @@ -7832,6 +8103,38 @@ "react-dom": "^18.0.0" } }, + "node_modules/sort-asc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-asc/-/sort-asc-0.2.0.tgz", + "integrity": "sha512-umMGhjPeHAI6YjABoSTrFp2zaBtXBej1a0yKkuMUyjjqu6FJsTF+JYwCswWDg+zJfk/5npWUUbd33HH/WLzpaA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-desc": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/sort-desc/-/sort-desc-0.2.0.tgz", + "integrity": "sha512-NqZqyvL4VPW+RAxxXnB8gvE1kyikh8+pR+T+CXLksVRN9eiQqkQlPwqWYU0mF9Jm7UnctShlxLyAt1CaBOTL1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sort-object": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sort-object/-/sort-object-3.0.3.tgz", + "integrity": "sha512-nK7WOY8jik6zaG9CRwZTaD5O7ETWDLZYMM12pqY8htll+7dYeqGfEUPcUBHOpSJg2vJOrvFIY2Dl5cX2ih1hAQ==", + "dependencies": { + "bytewise": "^1.1.0", + "get-value": "^2.0.2", + "is-extendable": "^0.1.1", + "sort-asc": "^0.2.0", + "sort-desc": "^0.2.0", + "union-value": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7859,6 +8162,51 @@ "source-map": "^0.6.0" } }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-string/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -8624,6 +8972,19 @@ "node": ">=14.17" } }, + "node_modules/typewise": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typewise/-/typewise-1.0.3.tgz", + "integrity": "sha512-aXofE06xGhaQSPzt8hlTY+/YWQhm9P0jYUp1f2XtmW/3Bk0qzXcyFWAtPoo2uTGQj1ZwbDuSyuxicq+aDo8lCQ==", + "dependencies": { + "typewise-core": "^1.2.0" + } + }, + "node_modules/typewise-core": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/typewise-core/-/typewise-core-1.2.0.tgz", + "integrity": "sha512-2SCC/WLzj2SbUwzFOzqMCkz5amXLlxtJqDKTICqg30x+2DZxcfZN2MvQZmGfXWKNWaKK9pBPsvkcwv8bF/gxKg==" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -8667,6 +9028,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/universal-cookie": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz", diff --git a/package.json b/package.json index 3bc54c1c..d0efba98 100644 --- a/package.json +++ b/package.json @@ -77,12 +77,14 @@ "lodash": "^4.17.21", "lucide-react": "^0.381.0", "mapbox-gl": "^3.4.0", + "maplibre-gl": "^4.3.2", "nanoid": "^5.0.7", "next": "^14.2.3", "next-auth": "^4.23.2", "next-i18next": "^15.3.0", "next-themes": "^0.3.0", "nookies": "^2.5.2", + "radar-sdk-js": "^4.3.0", "react": "^18.2.0", "react-cookie": "^7.1.4", "react-dom": "^18.2.0", diff --git a/src/components/map/index.tsx b/src/components/map/index.tsx index 8c9e0886..3980e13c 100644 --- a/src/components/map/index.tsx +++ b/src/components/map/index.tsx @@ -1,12 +1,26 @@ import { useAppConfig } from '@/hooks/use-app-config'; +import { ISearchResult } from '@/types/search-result'; +import { LngLatLike } from 'maplibre-gl'; import dynamic from 'next/dynamic'; +import { memo } from 'react'; -export default function MapLoader(props) { +interface IMapLoaderProps { + results?: ISearchResult[]; + center?: LngLatLike; + zoom?: number; + animate?: boolean; + boundsPadding?: number; + boundsZoom?: number; +} + +const MapLoader = memo(function MemoizedMapLoader(props) { const appConfig = useAppConfig(); const mapAdapterName = appConfig.adapters.map; - const MapComponent = dynamic(() => + const MapComponent = dynamic(() => import(`./${mapAdapterName}`).then((mod) => mod.default), ); return ; -} +}); + +export default MapLoader; diff --git a/src/components/map/mapbox/index.tsx b/src/components/map/mapbox/index.tsx index 26ef43bb..1d7dc506 100644 --- a/src/components/map/mapbox/index.tsx +++ b/src/components/map/mapbox/index.tsx @@ -18,12 +18,13 @@ export default function MapboxMap(props) { zoom: 12, animate: false, boundsPadding: 50, + ...props, }), - [appConfig?.features?.map?.center, MAPBOX_ACCESS_TOKEN], + [appConfig?.features?.map?.center, MAPBOX_ACCESS_TOKEN, props], ); return ( - + ); diff --git a/src/components/map/radar/components/map-markers.tsx b/src/components/map/radar/components/map-markers.tsx new file mode 100644 index 00000000..bba9dc74 --- /dev/null +++ b/src/components/map/radar/components/map-markers.tsx @@ -0,0 +1,91 @@ +import { ISearchResult } from '@/types/search-result'; +import { memo } from 'react'; +import Marker from './marker'; +import RenderHtml from '@/components/render-html'; +import { ReferralButton } from '@/components/referral-button'; +import { Globe, Phone } from 'lucide-react'; +import { useTranslation } from 'next-i18next'; + +export const Markers = memo(function MemoizedMarkers({ + results, + zoom, +}: { + results: ISearchResult[]; + zoom?: number; +}) { + const { t } = useTranslation(); + + if (!results || results.length === 0) return null; + + return ( + <> + {results.map((result) => { + if (result?.location?.point?.coordinates == null) return null; + + return ( + { + e.preventDefault(); + e.stopPropagation(); + const element = document.getElementById(result.id); + + document + .querySelectorAll('.outline') + .forEach((elem) => elem.classList.remove('outline')); + + if (element) { + element.classList.add('outline'); + element.scrollIntoView(); + } + }} + popup={ +
+

{result.name}

+ +
+ +
+ +
+ + + {t('call_to_action.call', { + ns: 'common', + })} + + + + + {t('call_to_action.view_website', { + ns: 'common', + })} + +
+
+ } + /> + ); + })} + + ); +}); diff --git a/src/components/map/radar/components/map.tsx b/src/components/map/radar/components/map.tsx new file mode 100644 index 00000000..30b4610d --- /dev/null +++ b/src/components/map/radar/components/map.tsx @@ -0,0 +1,45 @@ +import Radar from 'radar-sdk-js'; +import RadarMap from 'radar-sdk-js/dist/ui/RadarMap'; +import { useEffect, useState } from 'react'; +import { MapContextProvider } from '../mapContext'; +import 'radar-sdk-js/dist/radar.css'; + +export default function Map({ + children, + publishableKey, + zoom, + center, +}: { + children?: React.ReactNode; + publishableKey: string; + zoom?: number; + center?: [number, number]; +}) { + const [map, setMap] = useState(); + + useEffect(() => { + Radar.initialize(publishableKey); + const _map = Radar.ui.map({ + container: 'map', + style: 'radar-default-v1', + zoom: zoom || 0, + center, + }); + setMap(_map); + + return () => { + _map.remove(); + setMap(undefined); + }; + }, [publishableKey, zoom, center]); + + return ( + +
+
+ {children} +
+
+
+ ); +} diff --git a/src/components/map/radar/components/marker.tsx b/src/components/map/radar/components/marker.tsx new file mode 100644 index 00000000..b36f7f14 --- /dev/null +++ b/src/components/map/radar/components/marker.tsx @@ -0,0 +1,61 @@ +import { ReactElement, useContext, useEffect } from 'react'; +import { mapContext } from '../mapContext'; +import Radar from 'radar-sdk-js'; +import { renderToStaticMarkup } from 'react-dom/server'; +import RadarMarker from 'radar-sdk-js/dist/ui/RadarMarker'; + +export default function Marker({ + latitude, + longitude, + popup, + className, + onClick, + zoom, +}: { + latitude?: number; + longitude?: number; + popup?: ReactElement; + className?: string; + onClick?: (e: MouseEvent, marker: RadarMarker) => void; + zoom?: number; +}) { + const { map } = useContext(mapContext); + + useEffect(() => { + if (map == null) return; + if (latitude == null || longitude == null) return; + + const container = popup ? document.createElement('div') : undefined; + if (container != null && popup != null) { + container.innerHTML = renderToStaticMarkup(popup); + } + + const marker = Radar.ui + .marker({ + popup: { + element: container, + maxWidth: '320px', + }, + }) + .setLngLat([longitude, latitude]) + .addTo(map); + + if (className != null) { + marker.getElement().classList.add(className); + } + + const clickListener = (e: any) => { + return onClick?.(e, marker); + }; + + marker.getElement().addEventListener('click', clickListener); + + map?.fitToMarkers({ animate: false }); + + return () => { + marker.remove(); + }; + }, [longitude, latitude, map, popup, className, onClick, zoom]); + + return null; +} diff --git a/src/components/map/radar/index.tsx b/src/components/map/radar/index.tsx new file mode 100644 index 00000000..1121758c --- /dev/null +++ b/src/components/map/radar/index.tsx @@ -0,0 +1,25 @@ +import { useMemo } from 'react'; +import { getPublicConfig } from '@/pages/api/config'; +import Map from './components/map'; +import { useAppConfig } from '@/hooks/use-app-config'; +import { Markers } from './components/map-markers'; + +export default function RadarMap(props) { + const appConfig = useAppConfig(); + const RADAR_ACCESS_TOKEN = getPublicConfig('RADAR_ACCESS_TOKEN'); + + const mapProps = useMemo( + () => ({ + publishableKey: RADAR_ACCESS_TOKEN, + center: appConfig?.features?.map?.center, + zoom: 12, + }), + [RADAR_ACCESS_TOKEN, appConfig], + ); + + return ( + + + + ); +} diff --git a/src/components/map/radar/mapContext.ts b/src/components/map/radar/mapContext.ts new file mode 100644 index 00000000..79567e05 --- /dev/null +++ b/src/components/map/radar/mapContext.ts @@ -0,0 +1,9 @@ +import RadarMap from 'radar-sdk-js/dist/ui/RadarMap'; +import { createContext } from 'react'; + +interface IMapContext { + map?: RadarMap; +} + +export const mapContext = createContext({}); +export const MapContextProvider = mapContext.Provider; diff --git a/src/components/resource/components/information/index.tsx b/src/components/resource/components/information/index.tsx index 4b6c36ac..9726b5c6 100644 --- a/src/components/resource/components/information/index.tsx +++ b/src/components/resource/components/information/index.tsx @@ -1,4 +1,3 @@ -import { useAppConfig } from '@/hooks/use-app-config'; import { Card, CardContent, CardFooter } from '@/components/ui/card'; import Addresses from './addresses'; import PhoneNumbers from './phone-numbers'; @@ -11,7 +10,6 @@ import Eligibility from './eligibility'; import Fees from './fees'; import Languages from './languages'; import ServiceArea from './service-area'; -import { getPublicConfig } from '@/pages/api/config'; import { IResource } from '@/types/resource'; import MapLoader from '@/components/map'; @@ -20,9 +18,6 @@ type Props = { }; export default function ResourceInformation(props: Props) { - const appConfig = useAppConfig(); - const MAPBOX_ACCESS_TOKEN = getPublicConfig('MAPBOX_ACCESS_TOKEN'); - return ( @@ -33,9 +28,12 @@ export default function ResourceInformation(props: Props) {
diff --git a/src/components/results/components/result.tsx b/src/components/results/components/result.tsx index 85bacb6a..21ca8326 100644 --- a/src/components/results/components/result.tsx +++ b/src/components/results/components/result.tsx @@ -145,7 +145,9 @@ export function Result(props: Props) { ) : ( - {t('search.address_unavailable')} + + {t('search.address_unavailable')} +

{t('search.confidential_address')}

diff --git a/src/components/search/components/location-input.tsx b/src/components/search/components/location-input.tsx index c7a7f531..d57bcc7c 100644 --- a/src/components/search/components/location-input.tsx +++ b/src/components/search/components/location-input.tsx @@ -17,6 +17,7 @@ import { atomEffect } from 'jotai-effect'; import { setCookie, parseCookies } from 'nookies'; import { Locate, MapPin } from 'lucide-react'; import useMapAdapter from '@/lib/adapters/map/use-map-adapter'; +import { type LngLatLike } from 'maplibre-gl'; export const locationAtom = atom({ value: '', @@ -72,18 +73,14 @@ export default function LocationInput({ queryFn: async () => { if (!debouncedValue || debouncedValue.length < 2) return null; - const data = await locationAdapter.current.search( - debouncedValue, - router.locale, - cookies[SESSION_ID], - ); + const data = await locationAdapter.current.search(debouncedValue); return { group: t('search.suggestions'), items: - data?.suggestions?.map((suggestion) => ({ - value: suggestion.full_address, - mapbox_id: suggestion.mapbox_id, + data?.map((suggestion) => ({ + value: suggestion.address, + coordinates: suggestion.coordinates, })) ?? [], }; }, @@ -96,17 +93,14 @@ export default function LocationInput({ ); try { - const data = await locationAdapter.current.reverseGeocode( - coords, - router.locale, - ); + const data = await locationAdapter.current.reverseGeocode(coords); setLocation((prev) => ({ ...prev, coords: coords, - value: data?.features?.[0]?.place_name, + value: data?.address, })); - onInputChange?.(data?.features?.[0]?.place_name); + onInputChange?.(data?.address); onCoordChange?.(coords); } catch (err) { toast.error(t('search.geocoding_error'), { @@ -116,20 +110,14 @@ export default function LocationInput({ setIsFetching(false); } }, - [ - t, - onCoordChange, - router.locale, - locationAdapter, - setLocation, - onInputChange, - ], + [t, onCoordChange, locationAdapter, setLocation, onInputChange], ); const getUserLocation = useCallback(() => { setIsFetching(true); - const error = () => { + const error = (err) => { + console.error(err); toast.error(t('search.geocoding_error'), { description: t('search.geocoding_unable_to_retrieve'), }); @@ -155,19 +143,14 @@ export default function LocationInput({ onInputChange?.(value); }; - const onSelect = async (option: Option & { mapbox_id: string }) => { - const data = await locationAdapter.current.retrieve( - option.mapbox_id, - router.locale, - cookies[SESSION_ID], - ); - const coords = data?.features?.[0]?.geometry?.coordinates; + const onSelect = async (option: Option & { coordinates: LngLatLike }) => { + const coords = option.coordinates; setLocation((prev) => ({ ...prev, - coords: coords.join(','), + coords: coords.toString(), })); onValueSelect?.(option); - onCoordChange?.(coords.join(',')); + onCoordChange?.(coords.toString()); }; const filteredData = useMemo(() => { diff --git a/src/components/search/index.tsx b/src/components/search/index.tsx index 03d61c25..cf87a149 100644 --- a/src/components/search/index.tsx +++ b/src/components/search/index.tsx @@ -75,20 +75,13 @@ export default function Search() { if (value.location.length > 0 && location.coords.length === 0) { const data = await locationAdapter.current.forwardGeocode( value.location, - router.locale, ); setLocation({ - value: data?.features?.[0]?.properties?.full_address, - coords: [ - data?.features?.[0]?.properties?.coordinates?.longitude, - data?.features?.[0]?.properties?.coordinates?.latitude, - ].join(','), + value: data.address, + coords: data.coordinates.toString(), }); - urlParams.location = data?.features?.[0]?.properties?.full_address; - urlParams.coords = [ - data?.features?.[0]?.properties?.coordinates?.longitude, - data?.features?.[0]?.properties?.coordinates?.latitude, - ].join(','); + urlParams.location = data?.address; + urlParams.coords = data.coordinates.toString(); urlParams.distance = value.radius || '0'; } else if (value.location.length > 0 && location.coords.length > 0) { urlParams.location = value.location; diff --git a/src/lib/adapters/database/BaseDatabaseAdapter.ts b/src/lib/adapters/database/BaseDatabaseAdapter.ts index c4c6b8f4..f82b68e3 100644 --- a/src/lib/adapters/database/BaseDatabaseAdapter.ts +++ b/src/lib/adapters/database/BaseDatabaseAdapter.ts @@ -11,6 +11,14 @@ export abstract class BaseDatabaseAdapter { notFound = '404'; conflict = '409'; + constructor() { + if (typeof window !== 'undefined') { + throw new Error( + 'Database adapters are meant to be used on the server only.', + ); + } + } + abstract findResourceById( id: string, config: Config, diff --git a/src/lib/adapters/map/BaseMapAdapter.ts b/src/lib/adapters/map/BaseMapAdapter.ts index 8dc0ad5a..7cadd823 100644 --- a/src/lib/adapters/map/BaseMapAdapter.ts +++ b/src/lib/adapters/map/BaseMapAdapter.ts @@ -1,9 +1,20 @@ +import { LngLatLike } from 'maplibre-gl'; + +export interface MapSearchResult { + address: string; + coordinates: LngLatLike; +} + export abstract class BaseMapAdapter { - abstract search(query: string, locale: string, sessionId: string); + constructor() { + if (typeof window === 'undefined') { + throw new Error('Map adapters are meant to be used on the client only.'); + } + } - abstract retrieve(mapboxId: string, locale: string, sessionId: string); + abstract search(query: string): Promise; - abstract reverseGeocode(coords: string, locale: string); + abstract reverseGeocode(coords: string): Promise; - abstract forwardGeocode(address: string, locale: string); + abstract forwardGeocode(address: string): Promise; } diff --git a/src/lib/adapters/map/MapboxMapAdapter.ts b/src/lib/adapters/map/MapboxMapAdapter.ts index 5863c650..899a248c 100644 --- a/src/lib/adapters/map/MapboxMapAdapter.ts +++ b/src/lib/adapters/map/MapboxMapAdapter.ts @@ -1,44 +1,101 @@ import { getPublicConfig } from '@/pages/api/config'; import axios from 'axios'; -import { BaseMapAdapter } from './BaseMapAdapter'; +import router from 'next/router'; +import { BaseMapAdapter, MapSearchResult } from './BaseMapAdapter'; export default class MapboxMapAdapter extends BaseMapAdapter { private _mapboxAccessToken = ''; + private _axios; constructor() { super(); this._mapboxAccessToken = getPublicConfig('MAPBOX_ACCESS_TOKEN'); + this._axios = axios.create({ + baseURL: 'https://api.mapbox.com', + params: { + access_token: this._mapboxAccessToken, + country: 'US', + }, + }); } - async search(query, locale, sessionId) { - const res = await axios.get( - `https://api.mapbox.com/search/searchbox/v1/suggest?q=${query}&country=US&language=${locale}&access_token=${this._mapboxAccessToken}&session_token=${sessionId}`, - ); + async search(query): Promise { + const locale = router.locale; - return res.data; - } + const res = await this._axios.get('/search/geocode/v6/forward', { + params: { + q: query, + language: locale, + }, + }); - async retrieve(mapboxId, locale, sessionId) { - const res = await axios.get( - `https://api.mapbox.com/search/searchbox/v1/retrieve/${mapboxId}?language=${locale}&access_token=${this._mapboxAccessToken}&session_token=${sessionId}`, + return ( + res.data?.features?.map((feature) => ({ + address: feature?.properties?.full_address, + coordinates: + feature?.properties?.coordinates != null + ? [ + feature?.properties?.coordinates?.longitude, + feature?.properties?.coordinates?.latitude, + ] + : null, + })) ?? [] ); - - return res.data; } - async reverseGeocode(coords, locale) { - const res = await axios.get( - `https://api.mapbox.com/geocoding/v5/mapbox.places/${coords}.json?access_token=${this._mapboxAccessToken}&types=address&country=US&language=${locale}`, - ); + async reverseGeocode(coords): Promise { + const locale = router.locale; + const coordinates = coords.split(','); + + const res = await this._axios.get('/search/geocode/v6/reverse', { + params: { + longitude: coordinates[0], + latitude: coordinates[1], + types: 'address', + language: locale, + }, + }); + + const data = res.data?.features?.[0]; - return res.data; + if (!data) return null; + + return { + address: data?.properties?.full_address, + coordinates: + data?.properties?.coordinates != null + ? [ + data?.properties?.coordinates?.longitude, + data?.properties?.coordinates?.latitude, + ] + : null, + }; } - async forwardGeocode(address, locale) { - const res = await axios.get( - `https://api.mapbox.com/search/geocode/v6/forward?q=${address}&language=${locale}&access_token=${this._mapboxAccessToken}`, - ); + async forwardGeocode(address): Promise { + const locale = router.locale; + + const res = await this._axios.get('/search/geocode/v6/forward', { + params: { + q: address, + language: locale, + autocomplete: false, + }, + }); + + const data = res.data?.features?.[0]; + + if (!data) return null; - return res.data; + return { + address: data?.properties?.full_address, + coordinates: + data?.properties?.coordinates != null + ? [ + data?.properties?.coordinates?.longitude, + data?.properties?.coordinates?.latitude, + ] + : null, + }; } } diff --git a/src/lib/adapters/map/RadarMapAdapter.ts b/src/lib/adapters/map/RadarMapAdapter.ts new file mode 100644 index 00000000..3444001b --- /dev/null +++ b/src/lib/adapters/map/RadarMapAdapter.ts @@ -0,0 +1,71 @@ +import { getPublicConfig } from '@/pages/api/config'; +import { BaseMapAdapter, MapSearchResult } from './BaseMapAdapter'; +import axios from 'axios'; + +export default class RadarMapAdapter extends BaseMapAdapter { + private _radarMapAccessToken = ''; + private _baseUrl = 'https://api.radar.io/v1'; + private _axios; + + constructor() { + super(); + this._radarMapAccessToken = getPublicConfig('RADAR_ACCESS_TOKEN'); + this._axios = axios.create({ + baseURL: this._baseUrl, + headers: { + Authorization: this._radarMapAccessToken, + }, + params: { + country: 'US', + }, + }); + } + + async search(query: string): Promise { + const res = await this._axios.get(`/search/autocomplete`, { + params: { + query, + }, + }); + + return ( + res?.data?.addresses?.map((address) => ({ + address: address.formattedAddress, + coordinates: address?.geometry?.coordinates, + })) ?? [] + ); + } + + async reverseGeocode(coords: string): Promise { + const reverseCoords = coords.split(',').reverse().join(','); + const res = await this._axios.get('/geocode/reverse', { + params: { + coordinates: reverseCoords, + }, + }); + const data = res.data?.addresses?.[0]; + + if (!data) return null; + + return { + address: data.formattedAddress, + coordinates: data?.geometry?.coordinates, + }; + } + + async forwardGeocode(address: string): Promise { + const res = await this._axios.get('/geocode/forward', { + params: { + query: address, + }, + }); + const data = res.data?.addresses?.[0]; + + if (!data) return null; + + return { + address: data.formattedAddress, + coordinates: data?.geometry?.coordinates, + }; + } +} diff --git a/src/lib/adapters/map/use-map-adapter.ts b/src/lib/adapters/map/use-map-adapter.ts index 6833dced..325513ea 100644 --- a/src/lib/adapters/map/use-map-adapter.ts +++ b/src/lib/adapters/map/use-map-adapter.ts @@ -4,6 +4,7 @@ import { BaseMapAdapter } from './BaseMapAdapter'; const adapterMapping = { mapbox: import('./MapboxMapAdapter').then((mod) => mod.default), + radar: import('./RadarMapAdapter').then((mod) => mod.default), }; export default function useMapAdapter() { diff --git a/src/pages/api/config.ts b/src/pages/api/config.ts index ef893b1d..aa1739a7 100644 --- a/src/pages/api/config.ts +++ b/src/pages/api/config.ts @@ -5,6 +5,7 @@ */ const config = { MAPBOX_ACCESS_TOKEN: process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN, + RADAR_ACCESS_TOKEN: process.env.NEXT_PUBLIC_RADAR_ACCESS_TOKEN, GTM_CONTAINER_ID: process.env.NEXT_PUBLIC_GTM_CONTAINER_ID, }; diff --git a/src/pages/search/index.tsx b/src/pages/search/index.tsx index 04fb8d38..8ff6f4a5 100644 --- a/src/pages/search/index.tsx +++ b/src/pages/search/index.tsx @@ -1,7 +1,7 @@ import { GetServerSidePropsContext } from 'next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import nookies, { destroyCookie, setCookie } from 'nookies'; -import { useEffect, useMemo } from 'react'; +import { useEffect } from 'react'; import { useEventStore } from '@/hooks/use-event-store'; import { AppHeader } from '../../components/app-header'; import { AppFooter } from '../../components/app-footer'; @@ -22,7 +22,6 @@ import { USER_PREF_LAST_QUERY, USER_PREF_LOCATION, } from '@/constants/cookies'; -import { getPublicConfig } from '../api/config'; import { getServerSession } from 'next-auth'; import { authOptions } from '../api/auth/[...nextauth]'; import { getSearchAdapter } from '@/lib/adapters/search/get-search-adapter';