From 270257101cd13751d40c67dd48da4ed660738335 Mon Sep 17 00:00:00 2001 From: Tim Howitz Date: Sun, 23 Mar 2025 15:24:10 +0000 Subject: [PATCH] Big changes --- .gitignore | 42 +--- next-env.d.ts | 5 + package-lock.json | 324 ++++++++++++++++++++++++++++++ package.json | 4 + public/.DS_Store | Bin 0 -> 6148 bytes src/app/earthquakes/page.tsx | 75 ++++++- src/app/globals.css | 23 +++ src/app/layout.tsx | 6 +- src/app/observatories/page.tsx | 2 +- src/app/page.tsx | 6 +- src/components/InteractiveMap.tsx | 11 - src/components/Sidebar.tsx | 89 ++++++++ src/components/map.tsx | 234 +++++++++++++++++---- src/components/navbar.tsx | 2 +- src/types/Event.ts | 10 + src/utils/getMagnitudeColour.tsx | 20 ++ tsconfig.json | 8 +- 17 files changed, 761 insertions(+), 100 deletions(-) create mode 100644 next-env.d.ts create mode 100644 public/.DS_Store delete mode 100644 src/components/InteractiveMap.tsx create mode 100644 src/components/Sidebar.tsx create mode 100644 src/types/Event.ts create mode 100644 src/utils/getMagnitudeColour.tsx diff --git a/.gitignore b/.gitignore index 5ef6a52..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,41 +1 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env* - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +node_modules diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..1b3be08 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/package-lock.json b/package-lock.json index 6426661..a4d5d70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,15 @@ "version": "0.1.0", "dependencies": { "@prisma/client": "^6.4.1", + "@types/mapbox-gl": "^3.4.1", + "leaflet": "^1.9.4", + "mapbox-gl": "^3.10.0", "next": "15.1.7", "prisma": "^6.4.1", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.5.0", + "react-leaflet": "^5.0.0", "react-node": "^1.0.2" }, "devDependencies": { @@ -1077,6 +1081,56 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "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", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@mapbox/mapbox-gl-supported": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-3.0.0.tgz", + "integrity": "sha512-2XghOwu16ZwPJLOFVuIOaLbN0iKMn867evzXFyf0P22dqugezfJwLmdanAgU25ITvz1TvOfVP4jsDImlDJzcWg==", + "license": "BSD-3-Clause" + }, + "node_modules/@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "license": "ISC" + }, + "node_modules/@mapbox/tiny-sdf": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.6.tgz", + "integrity": "sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==", + "license": "BSD-2-Clause" + }, + "node_modules/@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "license": "BSD-3-Clause", + "dependencies": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "node_modules/@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@next/env": { "version": "15.1.7", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz", @@ -1347,6 +1401,17 @@ "@prisma/debug": "6.4.1" } }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1383,6 +1448,21 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1397,6 +1477,32 @@ "dev": true, "license": "MIT" }, + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "node_modules/@types/mapbox-gl": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-3.4.1.tgz", + "integrity": "sha512-NsGKKtgW93B+UaLPti6B7NwlxYlES5DpV5Gzj9F75rK5ALKsqSk15CiEHbOnTr09RGbr6ZYiCdI+59NNNcAImg==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "20.17.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", @@ -1407,6 +1513,12 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/pbf": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", + "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.0.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", @@ -1427,6 +1539,15 @@ "@types/react": "^19.0.0" } }, + "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==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.24.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", @@ -2174,6 +2295,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cheap-ruler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cheap-ruler/-/cheap-ruler-4.0.0.tgz", + "integrity": "sha512-0BJa8f4t141BYKQyn9NSQt1PguFQXMXwZiA5shfoaBYHAb2fFk2RAX+tiWMoQU+Agtzt3mdt0JtuyshAXqZ+Vw==", + "license": "ISC" + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2340,6 +2467,12 @@ "node": ">= 8" } }, + "node_modules/csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==", + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2564,6 +2697,12 @@ "node": ">= 0.4" } }, + "node_modules/earcut": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.1.tgz", + "integrity": "sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==", + "license": "ISC" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3507,6 +3646,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/geojson-vt": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", + "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", + "license": "ISC" + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -3577,6 +3722,12 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==", + "license": "MIT" + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -3693,6 +3844,12 @@ "dev": true, "license": "MIT" }, + "node_modules/grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==", + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -3799,6 +3956,26 @@ "node": ">=0.10.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4442,6 +4619,12 @@ "node": ">=4.0" } }, + "node_modules/kdbush": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", + "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "license": "ISC" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4472,6 +4655,12 @@ "node": ">=0.10" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -4549,6 +4738,46 @@ "dev": true, "license": "ISC" }, + "node_modules/mapbox-gl": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-3.10.0.tgz", + "integrity": "sha512-YnQxjlthuv/tidcxGYU2C8nRDVXMlAHa3qFhuOJeX4AfRP72OMRBf9ApL+M+k5VWcAXi2fcNOUVgphknjLumjA==", + "license": "SEE LICENSE IN LICENSE.txt", + "workspaces": [ + "src/style-spec", + "test/build/typings" + ], + "dependencies": { + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^3.0.0", + "@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", + "@types/geojson": "^7946.0.16", + "@types/geojson-vt": "^3.2.5", + "@types/mapbox__point-geometry": "^0.1.4", + "@types/mapbox__vector-tile": "^1.3.4", + "@types/pbf": "^3.0.5", + "@types/supercluster": "^7.1.3", + "cheap-ruler": "^4.0.0", + "csscolorparser": "~1.0.3", + "earcut": "^3.0.0", + "geojson-vt": "^4.0.2", + "gl-matrix": "^3.4.3", + "grid-index": "^1.1.0", + "kdbush": "^4.0.2", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^2.0.0", + "quickselect": "^3.0.0", + "serialize-to-js": "^3.1.2", + "supercluster": "^8.0.1", + "tinyqueue": "^3.0.0", + "vt-pbf": "^3.1.3" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -4632,6 +4861,12 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==", + "license": "MIT" + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5043,6 +5278,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pbf": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", + "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5242,6 +5490,12 @@ "dev": true, "license": "MIT" }, + "node_modules/potpack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", + "integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==", + "license": "ISC" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5302,6 +5556,12 @@ "react-is": "^16.13.1" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5344,6 +5604,12 @@ ], "license": "MIT" }, + "node_modules/quickselect": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-3.0.0.tgz", + "integrity": "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g==", + "license": "ISC" + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -5381,6 +5647,20 @@ "dev": true, "license": "MIT" }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/react-node": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/react-node/-/react-node-1.0.2.tgz", @@ -5534,6 +5814,15 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "license": "MIT", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5649,6 +5938,15 @@ "node": ">=10" } }, + "node_modules/serialize-to-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/serialize-to-js/-/serialize-to-js-3.1.2.tgz", + "integrity": "sha512-owllqNuDDEimQat7EPG0tH7JjO090xKNzUtYz6X+Sk2BXDnOCilDdNLwjWeFywG9xkJul1ULvtUQa9O4pUaY0w==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -6182,6 +6480,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supercluster": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz", + "integrity": "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.0.2" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6360,6 +6667,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyqueue": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-3.0.0.tgz", + "integrity": "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g==", + "license": "ISC" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6560,6 +6873,17 @@ "dev": true, "license": "MIT" }, + "node_modules/vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index c8c406c..f7b4242 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,15 @@ }, "dependencies": { "@prisma/client": "^6.4.1", + "@types/mapbox-gl": "^3.4.1", + "leaflet": "^1.9.4", + "mapbox-gl": "^3.10.0", "next": "15.1.7", "prisma": "^6.4.1", "react": "^19.0.0", "react-dom": "^19.0.0", "react-icons": "^5.5.0", + "react-leaflet": "^5.0.0", "react-node": "^1.0.2" }, "devDependencies": { diff --git a/public/.DS_Store b/public/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..63b3f4a3a0b93b2cc4c62283e7393d7d09c50eed GIT binary patch literal 6148 zcmeHKOHRWu5PdFPgd(A`K!OEQF3?KcAXH_+h7AXR{s^DYROM5a*>eZ3!1-9g8{3pL zakmInGt&6^Gtc9^D0U3M^q$AZKpQ|r7i_jz1Wd-oYu4~R!ZUV`E*|lM^eoFqwP@{7 z1yq6mrht6A5w3BI3=@>T-(i*yIVKy!3BCJejWu8!DWh`?*D^jTjEM0A?hFHFxWmlt z5WgABTP1#X8Gp&ONv;KVmN56I!rim+DLEaoZun$RTxW-?O*z91(^~e=$_%pFoY8qW zzH!L-^O7DB=bYe`d4l>i(#rh%jGUA4zzlO+A7F%g_DJhzroc+=n|Lo}Zc$TVjaX*k z?2vb{%2`m8DaOpc2y={RTIR5+ws8vZ%odAw9BQcwr~<0MQUTc?V!B}Bv2bX&4i@$b zKy0$x8td}QAe`7^;<0eZ5t?ySqN5sr#W0S}{@BMQ9t($#4&yH$#%DJEhGJ}X&L7)w zn8cx$s(>o6s=$_Ow&nakU4Q>yC230)Pz5$h0n_YX^m}|Hf40^hPR`nxenl6PxWb{O ju<*yRt;kV)K)1%RSSrNCW8siJH2o2 [ + { + id: "1234", + title: "Earthquake in Germany", + text1: "Magnitude 8.5", + text2: "30 minutes ago", + magnitude: 8.5, + longitude: 10.4515, // Near Berlin, Germany + latitude: 52.52, + }, + { + id: "2134", + title: "Earthquake in California", + text1: "Magnitude 5.3", + text2: "2 hours ago", + magnitude: 5.3, + longitude: -122.4194, // Near San Francisco, California, USA + latitude: 37.7749, + }, + { + id: "2314", + title: "Tremor in Japan", + text1: "Magnitude 4.7", + text2: "5 hours ago", + magnitude: 4.7, + longitude: 139.6917, // Near Tokyo, Japan + latitude: 35.6762, + }, + { + id: "2341", + title: "Tremor in Spain", + text1: "Magnitude 2.1", + text2: "10 hours ago", + magnitude: 2.1, + longitude: -3.7038, // Near Madrid, Spain + latitude: 40.4168, + }, + ], + [] + ); + return ( -
-

Earthquakes

- +
+
+ +
+ + {/* */}
); } diff --git a/src/app/globals.css b/src/app/globals.css index 968f6ad..48e87e9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -18,3 +18,26 @@ body { color: var(--foreground); background: var(--background); } + +/* Increase specificity and use !important where necessary */ +.mapboxgl-popup .mapboxgl-popup-content { + @apply rounded-xl p-4 px-5 drop-shadow-lg border border-neutral-300 max-w-xs !important; +} + +/* Hide the popup tip */ +.mapboxgl-popup .mapboxgl-popup-tip { + display: none !important; +} + +/* Child elements */ +.mapboxgl-popup-content h3 { + @apply text-sm font-medium text-neutral-800 !important; +} + +.mapboxgl-popup-content p { + @apply text-xs text-neutral-600 !important; +} + +.mapboxgl-popup-content p + p { + @apply text-neutral-500 !important; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5572567..c086fe6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import Navbar from "@/components/navbar"; +import Navbar from "@components/Navbar"; import { Inter } from "next/font/google"; import "./globals.css"; @@ -20,9 +20,9 @@ export default function RootLayout({ }>) { return ( - + - {children} +
{children}
); diff --git a/src/app/observatories/page.tsx b/src/app/observatories/page.tsx index 173b280..2e17d80 100644 --- a/src/app/observatories/page.tsx +++ b/src/app/observatories/page.tsx @@ -1,4 +1,4 @@ -import Sidebar from "@/components/sidebar_o"; +import Sidebar from "@components/sidebar_o"; export default function Observatories() { return ( diff --git a/src/app/page.tsx b/src/app/page.tsx index 0b61962..980b12e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -8,11 +8,13 @@ export default function Home() { Background Image
-
- Title Image +
+ Title Image
+ +
{["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => (
diff --git a/src/components/InteractiveMap.tsx b/src/components/InteractiveMap.tsx deleted file mode 100644 index 27870ac..0000000 --- a/src/components/InteractiveMap.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import InteractiveMap from "@/components/InteractiveMap"; - -export default function Home() { - return ( -
-

Interactive Map

- {/* Render InteractiveMap */} - -
- ); -} \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx new file mode 100644 index 0000000..5ce8b5f --- /dev/null +++ b/src/components/Sidebar.tsx @@ -0,0 +1,89 @@ +import React from "react"; +import { Dispatch, SetStateAction } from "react"; +import Link from "next/link"; +import { TbHexagon } from "react-icons/tb"; +import Event from "@appTypes/Event"; +import getMagnitudeColor from "@utils/getMagnitudeColour"; + +interface SidebarProps { + logTitle: string; + logSubtitle: string; + recentsTitle: string; + events: Event[]; + selectedEventId: Event["id"]; + setSelectedEventId: Dispatch>; + hoveredEventId: Event["id"]; + setHoveredEventId: Dispatch>; +} + +function MagnitudeNumber({ magnitude }: { magnitude: number }) { + // Convert magnitude to string with one decimal place + const magnitudeStr = magnitude.toFixed(1); + const [whole, decimal] = magnitudeStr.split("."); + + // Define color based on magnitude (0-10 scale) + return ( +
+ +
+
+ {whole} + . + {decimal} +
+
+
+ ); +} + +export default function Sidebar({ + logTitle, + logSubtitle, + recentsTitle, + events, + selectedEventId, + setSelectedEventId, + hoveredEventId, + setHoveredEventId, +}: SidebarProps) { + return ( +
+
+

{logTitle}

+

{logSubtitle}

+ + + +
+ +
+

{recentsTitle}

+
+ {events.map((event) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/map.tsx b/src/components/map.tsx index 6464349..ff2b88a 100644 --- a/src/components/map.tsx +++ b/src/components/map.tsx @@ -1,39 +1,203 @@ -import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; -import "leaflet/dist/leaflet.css"; // Leaflet CSS -import React from "react"; +import { useCallback, useState } from "react"; +import React, { useRef, useEffect, Dispatch, SetStateAction } from "react"; +import mapboxgl, { LngLatBounds } from "mapbox-gl"; +import "mapbox-gl/dist/mapbox-gl.css"; +import Event from "@appTypes/Event"; +import getMagnitudeColor from "@utils/getMagnitudeColour"; -const points = [ - { id: 1, name: "Point A", position: [51.505, -0.09] }, - { id: 2, name: "Point B", position: [51.515, -0.1] }, - { id: 3, name: "Point C", position: [51.525, -0.11] }, -]; +interface MapComponentProps { + events: Event[]; + selectedEventId: Event["id"]; + setSelectedEventId: Dispatch>; + hoveredEventId: Event["id"]; + setHoveredEventId: Dispatch>; +} -const InteractiveMap = () => { - return ( -
- - {/* Tile Layer for map background */} - +// Map component with location-style pulsing dots, animations, and tooltips +function MapComponent({ events, selectedEventId, setSelectedEventId, hoveredEventId, setHoveredEventId }: MapComponentProps) { + const map = useRef(null); + const markers = useRef<{ [key: string]: mapboxgl.Marker }>({}); + const [mapBounds, setMapBounds] = useState(); - {/* Add markers for points */} - {points.map((point) => ( - - {/* Popup when marker is clicked */} - {point.name} - {/* Optional hover tooltip */} - - ))} - -
- ); -}; + const fitToBounds = useCallback((bounds: LngLatBounds) => { + if (map.current && bounds) { + map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 }); + } + }, []); -export default InteractiveMap; \ No newline at end of file + const showPopup = useCallback((eventId: string) => { + const marker = markers.current[eventId]; + if (marker && map.current) { + marker.getPopup()?.addTo(map.current); + } + }, []); + + const clearAllPopups = useCallback(() => { + Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove()); + }, []); + + const flyToEvent = useCallback((event: Event) => { + if (map.current) { + map.current.flyTo({ + center: [event.longitude, event.latitude], + zoom: 4, + speed: 1.5, + curve: 1.42, + }); + } + }, []); + + useEffect(() => { + mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ"; + + try { + map.current = new mapboxgl.Map({ + container: "map-container", + style: "mapbox://styles/mapbox/light-v10", + center: [0, 0], + zoom: 1, + }); + } catch (error) { + console.error("Map initialization failed:", error); + return; + } + + map.current.on("load", () => { + // Fit map to bounds + const bounds = new mapboxgl.LngLatBounds(); + events.forEach((event) => { + bounds.extend([event.longitude, event.latitude]); + }); + fitToBounds(bounds); + setMapBounds(bounds); + + // Add markers with location pulse + events.forEach((event) => { + const color = getMagnitudeColor(event.magnitude); + + // Create marker container + const markerElement = document.createElement("div"); + markerElement.style.width = "20px"; // Increased size to accommodate pulse + markerElement.style.height = "20px"; + markerElement.style.position = "absolute"; + markerElement.style.display = "flex"; + markerElement.style.alignItems = "center"; + markerElement.style.justifyContent = "center"; + + // Central dot + const dotElement = document.createElement("div"); + dotElement.style.width = "8px"; + dotElement.style.height = "8px"; + dotElement.style.backgroundColor = color; + dotElement.style.borderRadius = "50%"; + dotElement.style.position = "absolute"; + dotElement.style.zIndex = "2"; // Ensure dot is above pulse + + // Pulsing ring + const pulseElement = document.createElement("div"); + pulseElement.className = "location-pulse"; + pulseElement.style.width = "20px"; // Initial size + pulseElement.style.height = "20px"; + pulseElement.style.backgroundColor = `${color}80`; // Color with 50% opacity (hex alpha) + pulseElement.style.borderRadius = "50%"; + pulseElement.style.position = "absolute"; + pulseElement.style.zIndex = "1"; + + markerElement.appendChild(pulseElement); + markerElement.appendChild(dotElement); + + const marker = new mapboxgl.Marker({ element: markerElement }).setLngLat([event.longitude, event.latitude]).addTo(map.current!); + + const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(` +
+

${event.title}

+

Magnitude: ${event.magnitude}

+

${event.text2}

+
+ `); + + marker.setPopup(popup); + markers.current[event.id] = marker; + + // Add hover events + const markerDomElement = marker.getElement(); + markerDomElement.style.cursor = "pointer"; // Optional: indicate interactivity + + markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id)); + markerDomElement.addEventListener("mouseleave", () => setHoveredEventId("")); + markerDomElement.addEventListener("click", () => setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id))); + + // Cleanup event listeners on unmount + markerDomElement.dataset.listenersAdded = "true"; // Mark for cleanup + }); + }); + + map.current.on("error", (e) => { + console.error("Mapbox error:", e); + }); + + return () => { + map.current?.remove(); + }; + }, [events, setSelectedEventId, setHoveredEventId, fitToBounds]); + + useEffect(() => { + const event = events.find((x) => x.id === selectedEventId); + if (event) flyToEvent(event); + else if (!selectedEventId) { + if (mapBounds) fitToBounds(mapBounds); + } + }, [events, selectedEventId, mapBounds, fitToBounds, clearAllPopups, flyToEvent]); + + useEffect(() => { + // Clear all popups first + clearAllPopups(); + + // Handle both events if they exist and are different + if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) { + showPopup(hoveredEventId); + showPopup(selectedEventId); + } + // Handle single event case (either hovered or selected) + else if (hoveredEventId || selectedEventId) { + showPopup(hoveredEventId || selectedEventId); + } + }, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]); + + return ( +
+
+
+ ); +} + +// CSS for location-style pulsing animation +const pulseStyles = ` + .location-pulse { + animation: locationPulse 2s infinite; + } + + @keyframes locationPulse { + 0% { + transform: scale(0.5); + opacity: 0.8; + } + 70% { + transform: scale(2); + opacity: 0; + } + 100% { + transform: scale(2); + opacity: 0; + } + } +`; + +export default function Map(props: MapComponentProps) { + return ( + <> + + + + ); +} diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index c8d3480..3a670b1 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -1,7 +1,7 @@ "use client"; import { useState } from "react"; -import AuthModal from "@/components/AuthModal"; +import AuthModal from "@components/AuthModal"; import { usePathname } from "next/navigation"; import Link from "next/link"; import Image from "next/image"; diff --git a/src/types/Event.ts b/src/types/Event.ts new file mode 100644 index 0000000..02a501f --- /dev/null +++ b/src/types/Event.ts @@ -0,0 +1,10 @@ +interface Event { + id: string; + title: string; + magnitude: number; + longitude: number; + latitude: number; + text2: string; +} + +export default Event; diff --git a/src/utils/getMagnitudeColour.tsx b/src/utils/getMagnitudeColour.tsx new file mode 100644 index 0000000..5a52eb3 --- /dev/null +++ b/src/utils/getMagnitudeColour.tsx @@ -0,0 +1,20 @@ +"use client"; +import resolveConfig from "tailwindcss/resolveConfig"; +import tailwindConfig from "../../tailwind.config"; + +function getMagnitudeColour(magnitude: number) { + const fullConfig = resolveConfig(tailwindConfig); + const colors = fullConfig.theme.colors; + + if (magnitude >= 7) { + return colors["red"][600]; + } else if (magnitude >= 5) { + return colors["orange"][500]; + } else if (magnitude >= 3) { + return colors["amber"][500]; + } else { + return colors["blue"][400]; + } +} + +export default getMagnitudeColour; diff --git a/tsconfig.json b/tsconfig.json index 03c4ffc..a4ead61 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "moduleResolution": "node", // Use "node" module resolution strategy + "moduleResolution": "node", // Use "node" module resolution strategy "forceConsistentCasingInFileNames": true, "target": "ESNext", "lib": ["dom", "dom.iterable", "esnext"], @@ -20,7 +20,11 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@components/*": ["./src/components/*"], + "@hooks/*": ["./src/hooks/*"], + "@utils/*": ["./src/utils/*"], + "@appTypes/*": ["./src/types/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],