Big changes

This commit is contained in:
Tim Howitz 2025-03-23 15:24:10 +00:00
parent beb2effbbd
commit 270257101c
17 changed files with 761 additions and 100 deletions

42
.gitignore vendored
View File

@ -1,41 +1 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. node_modules
# 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

5
next-env.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

324
package-lock.json generated
View File

@ -9,11 +9,15 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@prisma/client": "^6.4.1", "@prisma/client": "^6.4.1",
"@types/mapbox-gl": "^3.4.1",
"leaflet": "^1.9.4",
"mapbox-gl": "^3.10.0",
"next": "15.1.7", "next": "15.1.7",
"prisma": "^6.4.1", "prisma": "^6.4.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-leaflet": "^5.0.0",
"react-node": "^1.0.2" "react-node": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
@ -1077,6 +1081,56 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@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": { "node_modules/@next/env": {
"version": "15.1.7", "version": "15.1.7",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz",
@ -1347,6 +1401,17 @@
"@prisma/debug": "6.4.1" "@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": { "node_modules/@rtsao/scc": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@ -1383,6 +1448,21 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/@types/json-schema": {
"version": "7.0.15", "version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -1397,6 +1477,32 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/@types/node": {
"version": "20.17.19", "version": "20.17.19",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.19.tgz",
@ -1407,6 +1513,12 @@
"undici-types": "~6.19.2" "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": { "node_modules/@types/react": {
"version": "19.0.10", "version": "19.0.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
@ -1427,6 +1539,15 @@
"@types/react": "^19.0.0" "@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": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.24.1", "version": "8.24.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.24.1.tgz", "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" "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": { "node_modules/chokidar": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@ -2340,6 +2467,12 @@
"node": ">= 8" "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": { "node_modules/cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -2564,6 +2697,12 @@
"node": ">= 0.4" "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": { "node_modules/eastasianwidth": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
@ -3507,6 +3646,12 @@
"url": "https://github.com/sponsors/ljharb" "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": { "node_modules/get-intrinsic": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "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" "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": { "node_modules/glob": {
"version": "10.4.5", "version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@ -3693,6 +3844,12 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/has-bigints": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
@ -3799,6 +3956,26 @@
"node": ">=0.10.0" "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": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@ -4442,6 +4619,12 @@
"node": ">=4.0" "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": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@ -4472,6 +4655,12 @@
"node": ">=0.10" "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": { "node_modules/levn": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
@ -4549,6 +4738,46 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -4632,6 +4861,12 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT" "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": { "node_modules/mz": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
@ -5043,6 +5278,19 @@
"url": "https://github.com/sponsors/isaacs" "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": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -5242,6 +5490,12 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -5302,6 +5556,12 @@
"react-is": "^16.13.1" "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": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@ -5344,6 +5604,12 @@
], ],
"license": "MIT" "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": { "node_modules/react": {
"version": "19.0.0", "version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
@ -5381,6 +5647,20 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/react-node": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-node/-/react-node-1.0.2.tgz", "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" "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": { "node_modules/reusify": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@ -5649,6 +5938,15 @@
"node": ">=10" "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": { "node_modules/set-function-length": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "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": ">=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": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -6360,6 +6667,12 @@
"url": "https://github.com/sponsors/jonschlinkert" "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": { "node_modules/to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -6560,6 +6873,17 @@
"dev": true, "dev": true,
"license": "MIT" "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": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@ -11,11 +11,15 @@
}, },
"dependencies": { "dependencies": {
"@prisma/client": "^6.4.1", "@prisma/client": "^6.4.1",
"@types/mapbox-gl": "^3.4.1",
"leaflet": "^1.9.4",
"mapbox-gl": "^3.10.0",
"next": "15.1.7", "next": "15.1.7",
"prisma": "^6.4.1", "prisma": "^6.4.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-icons": "^5.5.0", "react-icons": "^5.5.0",
"react-leaflet": "^5.0.0",
"react-node": "^1.0.2" "react-node": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {

BIN
public/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -1,10 +1,77 @@
import Sidebar from "@/components/sidebar_e"; "use client";
import Sidebar from "@components/Sidebar";
import Map from "@components/Map";
import { useState, useMemo } from "react";
export default function Earthquakes() { export default function Earthquakes() {
const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState("");
const events = useMemo(
() => [
{
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 ( return (
<div className="min-h-screen"> <div className="h-full flex overflow-hidden">
<p>Earthquakes</p> <div className="flex-grow">
<Sidebar></Sidebar> <Map
events={events}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
></Map>
</div>
<Sidebar
logTitle="Log an Earthquake"
logSubtitle="Record new earthquakes - time/date, location, magnitude, observatory and scientists"
recentsTitle="Recent Earthquakes"
events={events}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
></Sidebar>
{/* <SidebarTest></SidebarTest> */}
</div> </div>
); );
} }

View File

@ -18,3 +18,26 @@ body {
color: var(--foreground); color: var(--foreground);
background: var(--background); 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;
}

View File

@ -1,5 +1,5 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import Navbar from "@/components/navbar"; import Navbar from "@components/Navbar";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
@ -20,9 +20,9 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en"> <html lang="en">
<body className={`${inter.variable} antialiased`}> <body className={`${inter.variable} h-[calc(100vh-3.5rem)] flex flex-col min-h-screen antialiased`}>
<Navbar></Navbar> <Navbar></Navbar>
{children} <div className="flex-1 overflow-y-auto">{children}</div>
</body> </body>
</html> </html>
); );

View File

@ -1,4 +1,4 @@
import Sidebar from "@/components/sidebar_o"; import Sidebar from "@components/sidebar_o";
export default function Observatories() { export default function Observatories() {
return ( return (

View File

@ -8,11 +8,13 @@ export default function Home() {
<Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image> <Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image>
</div> </div>
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/10 to-black/40"></div> <div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/10 to-black/40"></div>
<div className="absolute inset-0 top-[26%]"> <div className="absolute inset-0 top-[30%]">
<Image className="mx-auto" height={300} width={900} alt="Title Image" src="/tremortrackertext.png"></Image> <Image className="mx-auto" height={300} width={1000} alt="Title Image" src="/tremortrackertext.png"></Image>
</div> </div>
</div> </div>
<div className="mx-auto mt-20 w-4/6 px-2 border border-black divide-y"> <div className="mx-auto mt-20 w-4/6 px-2 border border-black divide-y">
{["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => ( {["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => (
<div className="px-5 py-5" key={name}> <div className="px-5 py-5" key={name}>

View File

@ -1,11 +0,0 @@
import InteractiveMap from "@/components/InteractiveMap";
export default function Home() {
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
<h1 className="text-2xl font-bold mb-4">Interactive Map</h1>
{/* Render InteractiveMap */}
<InteractiveMap />
</div>
);
}

View File

@ -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<SetStateAction<string>>;
hoveredEventId: Event["id"];
setHoveredEventId: Dispatch<SetStateAction<string>>;
}
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 (
<div className={`relative`} style={{ color: getMagnitudeColor(magnitude) }}>
<TbHexagon size={40} className="drop-shadow-sm" />
<div className="absolute inset-0 flex items-center justify-center">
<div className="flex items-baseline font-mono font-bold tracking-tight">
<span className="text-xl -mr-1">{whole}</span>
<span className="text-xs ml-[1.5px] -mr-[2.5px]">.</span>
<span className="text-xs -mr-[1px]">{decimal}</span>
</div>
</div>
</div>
);
}
export default function Sidebar({
logTitle,
logSubtitle,
recentsTitle,
events,
selectedEventId,
setSelectedEventId,
hoveredEventId,
setHoveredEventId,
}: SidebarProps) {
return (
<div className="flex flex-col py-6 h-full w-full max-w-xs bg-gradient-to-b from-neutral-100 to-neutral-50 shadow-lg">
<div className="px-6 pb-8 border-b border-neutral-200">
<h2 className="text-2xl font-bold text-neutral-800 mb-2">{logTitle}</h2>
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
<Link href="/">
<button className="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition-colors duration-200 font-medium">
Log Event
</button>
</Link>
</div>
<div className="flex-1 px-6 pt-6 overflow-y-auto">
<h2 className="text-xl font-bold text-neutral-800 mb-4">{recentsTitle}</h2>
<div className="space-y-3">
{events.map((event) => (
<button
key={event.title}
className={`w-full border ${hoveredEventId === event.id ? "bg-neutral-100" : "bg-white"} ${
selectedEventId === event.id ? "border-neutral-800" : "border-neutral-200"
} rounded-lg p-4 flex items-center gap-4 hover:bg-neutral-100 transition-colors duration-150 shadow-sm text-left`}
onClick={() => {
setSelectedEventId((prevEventId) => (prevEventId !== event.id ? event.id : ""));
}}
onMouseEnter={() => setHoveredEventId(event.id)}
onMouseLeave={() => setHoveredEventId("")}
>
<div className="flex-1">
<p className="text-sm font-medium text-neutral-800">{event.title}</p>
{/* Uncomment if you want to use text1 */}
{/* <p className="text-xs text-neutral-600">{event.text1}</p> */}
<p className="text-xs text-neutral-500 mt-1">{event.text2}</p>
</div>
<MagnitudeNumber magnitude={event.magnitude} />
</button>
))}
</div>
</div>
</div>
);
}

View File

@ -1,39 +1,203 @@
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet"; import { useCallback, useState } from "react";
import "leaflet/dist/leaflet.css"; // Leaflet CSS import React, { useRef, useEffect, Dispatch, SetStateAction } from "react";
import React 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 = [ interface MapComponentProps {
{ id: 1, name: "Point A", position: [51.505, -0.09] }, events: Event[];
{ id: 2, name: "Point B", position: [51.515, -0.1] }, selectedEventId: Event["id"];
{ id: 3, name: "Point C", position: [51.525, -0.11] }, setSelectedEventId: Dispatch<SetStateAction<string>>;
]; hoveredEventId: Event["id"];
setHoveredEventId: Dispatch<SetStateAction<string>>;
}
// Map component with location-style pulsing dots, animations, and tooltips
function MapComponent({ events, selectedEventId, setSelectedEventId, hoveredEventId, setHoveredEventId }: MapComponentProps) {
const map = useRef<mapboxgl.Map | null>(null);
const markers = useRef<{ [key: string]: mapboxgl.Marker }>({});
const [mapBounds, setMapBounds] = useState<LngLatBounds>();
const fitToBounds = useCallback((bounds: LngLatBounds) => {
if (map.current && bounds) {
map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 });
}
}, []);
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(`
<div>
<h3>${event.title}</h3>
<p>Magnitude: ${event.magnitude}</p>
<p>${event.text2}</p>
</div>
`);
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]);
const InteractiveMap = () => {
return ( return (
<div className="w-full h-96 flex justify-center items-center"> <div className="w-full h-full">
<MapContainer <div id="map-container" className="w-full h-full" />
center={[51.505, -0.09]} // Default map center
zoom={13} // Zoom level
style={{ height: "100%", width: "100%" }}
className="rounded-lg shadow-lg"
>
{/* Tile Layer for map background */}
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{/* Add markers for points */}
{points.map((point) => (
<Marker key={point.id} position={point.position}>
{/* Popup when marker is clicked */}
<Popup>{point.name}</Popup>
{/* Optional hover tooltip */}
</Marker>
))}
</MapContainer>
</div> </div>
); );
}; }
export default InteractiveMap; // 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 (
<>
<style>{pulseStyles}</style>
<MapComponent {...props} />
</>
);
}

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import AuthModal from "@/components/AuthModal"; import AuthModal from "@components/AuthModal";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";

10
src/types/Event.ts Normal file
View File

@ -0,0 +1,10 @@
interface Event {
id: string;
title: string;
magnitude: number;
longitude: number;
latitude: number;
text2: string;
}
export default Event;

View File

@ -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;

View File

@ -20,7 +20,11 @@
} }
], ],
"paths": { "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"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],