logging works for earthquake
This commit is contained in:
parent
4438953fab
commit
d585cc908f
154
package-lock.json
generated
154
package-lock.json
generated
@ -28,6 +28,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mapbox-gl": "^3.10.0",
|
"mapbox-gl": "^3.10.0",
|
||||||
"next": "^15.1.7",
|
"next": "^15.1.7",
|
||||||
|
"next-auth": "^4.24.11",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prisma": "^6.4.1",
|
"prisma": "^6.4.1",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
@ -1070,6 +1071,15 @@
|
|||||||
"node": ">=12.4.0"
|
"node": ">=12.4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@panva/hkdf": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
@ -5670,6 +5680,47 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-auth": {
|
||||||
|
"version": "4.24.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.11.tgz",
|
||||||
|
"integrity": "sha512-pCFXzIDQX7xmHFs4KVH4luCjaCbuPRtZ9oBUjUhOk84mZ9WVPf94n87TxYI4rSRf9HmfHEF8Yep3JrYDVOo3Cw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.20.13",
|
||||||
|
"@panva/hkdf": "^1.0.2",
|
||||||
|
"cookie": "^0.7.0",
|
||||||
|
"jose": "^4.15.5",
|
||||||
|
"oauth": "^0.9.15",
|
||||||
|
"openid-client": "^5.4.0",
|
||||||
|
"preact": "^10.6.3",
|
||||||
|
"preact-render-to-string": "^5.1.19",
|
||||||
|
"uuid": "^8.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@auth/core": "0.34.2",
|
||||||
|
"next": "^12.2.5 || ^13 || ^14 || ^15",
|
||||||
|
"nodemailer": "^6.6.5",
|
||||||
|
"react": "^17.0.2 || ^18 || ^19",
|
||||||
|
"react-dom": "^17.0.2 || ^18 || ^19"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@auth/core": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"nodemailer": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/next-auth/node_modules/jose": {
|
||||||
|
"version": "4.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
|
||||||
|
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/next/node_modules/postcss": {
|
"node_modules/next/node_modules/postcss": {
|
||||||
"version": "8.4.31",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
@ -5708,6 +5759,12 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oauth": {
|
||||||
|
"version": "0.9.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||||
|
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
@ -5840,6 +5897,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/oidc-token-hash": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "^10.13.0 || >=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/on-finished": {
|
"node_modules/on-finished": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||||
@ -5861,6 +5927,51 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/openid-client": {
|
||||||
|
"version": "5.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz",
|
||||||
|
"integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jose": "^4.15.9",
|
||||||
|
"lru-cache": "^6.0.0",
|
||||||
|
"object-hash": "^2.2.0",
|
||||||
|
"oidc-token-hash": "^5.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openid-client/node_modules/jose": {
|
||||||
|
"version": "4.15.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz",
|
||||||
|
"integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/panva"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openid-client/node_modules/lru-cache": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"yallist": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/openid-client/node_modules/object-hash": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optionator": {
|
"node_modules/optionator": {
|
||||||
"version": "0.9.4",
|
"version": "0.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
|
||||||
@ -6212,6 +6323,28 @@
|
|||||||
"integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==",
|
"integrity": "sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/preact": {
|
||||||
|
"version": "10.26.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.8.tgz",
|
||||||
|
"integrity": "sha512-1nMfdFjucm5hKvq0IClqZwK4FJkGXhRrQstOQ3P4vp8HxKrJEMFcY6RdBRVTdfQS/UlnX6gfbPuTvaqx/bDoeQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/preact"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/preact-render-to-string": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"pretty-format": "^3.8.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"preact": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"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",
|
||||||
@ -6222,6 +6355,12 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pretty-format": {
|
||||||
|
"version": "3.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||||
|
"integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/prisma": {
|
"node_modules/prisma": {
|
||||||
"version": "6.8.2",
|
"version": "6.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.8.2.tgz",
|
||||||
@ -7927,6 +8066,15 @@
|
|||||||
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
|
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/uuid": {
|
||||||
|
"version": "8.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||||
|
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vary": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@ -8163,6 +8311,12 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/yallist": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz",
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mapbox-gl": "^3.10.0",
|
"mapbox-gl": "^3.10.0",
|
||||||
"next": "^15.1.7",
|
"next": "^15.1.7",
|
||||||
|
"next-auth": "^4.24.11",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"prisma": "^6.4.1",
|
"prisma": "^6.4.1",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
|
|||||||
@ -9,6 +9,29 @@ import { getRelativeDate } from "@utils/formatters";
|
|||||||
import GeologicalEvent from "@appTypes/Event";
|
import GeologicalEvent from "@appTypes/Event";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import EarthquakeLogModal from "@components/EarthquakeLogModal";
|
import EarthquakeLogModal from "@components/EarthquakeLogModal";
|
||||||
|
import { useStoreState } from "@hooks/store";
|
||||||
|
|
||||||
|
// --- NO ACCESS MODAL ---
|
||||||
|
function NoAccessModal({ open, onClose }) {
|
||||||
|
if (!open) return null;
|
||||||
|
return (
|
||||||
|
<div className="fixed z-50 inset-0 bg-black/40 flex items-center justify-center">
|
||||||
|
<div className="bg-white rounded-lg shadow-lg p-8 max-w-xs w-full text-center relative">
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="absolute right-4 top-4 text-gray-500 hover:text-black text-lg"
|
||||||
|
aria-label="Close"
|
||||||
|
>×</button>
|
||||||
|
<h2 className="font-bold text-xl mb-4">Access Denied</h2>
|
||||||
|
<p className="text-gray-600 mb-3">Sorry, you do not have access rights to Log an Earthquake. Please Log in here, or contact an Admin if you believe this is a mistake</p>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2"
|
||||||
|
>OK</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- SEARCH MODAL COMPONENT ---
|
// --- SEARCH MODAL COMPONENT ---
|
||||||
function EarthquakeSearchModal({ open, onClose, onSelect }) {
|
function EarthquakeSearchModal({ open, onClose, onSelect }) {
|
||||||
@ -86,10 +109,17 @@ export default function Earthquakes() {
|
|||||||
const [selectedEventId, setSelectedEventId] = useState("");
|
const [selectedEventId, setSelectedEventId] = useState("");
|
||||||
const [hoveredEventId, setHoveredEventId] = useState("");
|
const [hoveredEventId, setHoveredEventId] = useState("");
|
||||||
const [searchModalOpen, setSearchModalOpen] = useState(false);
|
const [searchModalOpen, setSearchModalOpen] = useState(false);
|
||||||
const [logModalOpen, setLogModalOpen] = useState(false); // <-- Move here!
|
const [logModalOpen, setLogModalOpen] = useState(false);
|
||||||
// Fetch recent earthquakes as before
|
const [noAccessModalOpen, setNoAccessModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const user = useStoreState((state) => state.user);
|
||||||
|
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
|
||||||
|
const canLogEarthquake = role === "SCIENTIST" || role === "ADMIN";
|
||||||
|
|
||||||
|
// Fetch recent earthquakes
|
||||||
const { data, error, isLoading, mutate } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 10 }));
|
const { data, error, isLoading, mutate } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 10 }));
|
||||||
// Prepare events for maps/sidebar
|
|
||||||
|
// Prepare events
|
||||||
const earthquakeEvents = useMemo(
|
const earthquakeEvents = useMemo(
|
||||||
() =>
|
() =>
|
||||||
data && data.earthquakes
|
data && data.earthquakes
|
||||||
@ -110,6 +140,16 @@ export default function Earthquakes() {
|
|||||||
: [],
|
: [],
|
||||||
[data]
|
[data]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This handler is always called, regardless of button state!
|
||||||
|
const handleLogClick = () => {
|
||||||
|
if (canLogEarthquake) {
|
||||||
|
setLogModalOpen(true);
|
||||||
|
} else {
|
||||||
|
setNoAccessModalOpen(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
|
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
|
||||||
<div className="flex-grow h-full">
|
<div className="flex-grow h-full">
|
||||||
@ -133,22 +173,24 @@ export default function Earthquakes() {
|
|||||||
setHoveredEventId={setHoveredEventId}
|
setHoveredEventId={setHoveredEventId}
|
||||||
button1Name="Log an Earthquake"
|
button1Name="Log an Earthquake"
|
||||||
button2Name="Search Earthquakes"
|
button2Name="Search Earthquakes"
|
||||||
onButton1Click={() => setLogModalOpen(true)} // Correct!
|
onButton1Click={handleLogClick} // <--- Important!
|
||||||
onButton2Click={() => setSearchModalOpen(true)}
|
onButton2Click={() => setSearchModalOpen(true)}
|
||||||
|
button1Disabled={!canLogEarthquake} // <--- For style only!
|
||||||
/>
|
/>
|
||||||
<EarthquakeSearchModal
|
<EarthquakeSearchModal
|
||||||
open={searchModalOpen}
|
open={searchModalOpen}
|
||||||
onClose={() => setSearchModalOpen(false)}
|
onClose={() => setSearchModalOpen(false)}
|
||||||
onSelect={(eq) => {
|
onSelect={(eq) => setSelectedEventId(eq.code)}
|
||||||
setSelectedEventId(eq.code);
|
|
||||||
// setSelectedSearchResult(eq); // optional
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<EarthquakeLogModal
|
<EarthquakeLogModal
|
||||||
open={logModalOpen}
|
open={logModalOpen}
|
||||||
onClose={() => setLogModalOpen(false)}
|
onClose={() => setLogModalOpen(false)}
|
||||||
onSuccess={() => mutate()} // To refresh
|
onSuccess={() => mutate()} // To refresh
|
||||||
/>
|
/>
|
||||||
|
<NoAccessModal
|
||||||
|
open={noAccessModalOpen}
|
||||||
|
onClose={() => setNoAccessModalOpen(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -2,15 +2,15 @@ import React, { useCallback, useRef, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function BottomFooter() {
|
export default function BottomFooter() {
|
||||||
// Lava flood state & timer
|
// ig easter egg
|
||||||
const [lavaActive, setLavaActive] = useState(false);
|
const [lavaActive, setLavaActive] = useState(false);
|
||||||
const lavaTimeout = useRef<any>(null);
|
const lavaTimeout = useRef<any>(null);
|
||||||
|
|
||||||
// LinkedIn shake state & timer
|
// LinkedIn easter egg
|
||||||
const [shaking, setShaking] = useState(false);
|
const [shaking, setShaking] = useState(false);
|
||||||
const shakeTimeout = useRef<any>(null);
|
const shakeTimeout = useRef<any>(null);
|
||||||
|
|
||||||
// Crack+collapse states for the X logo
|
// x easter egg
|
||||||
const [showCracks, setShowCracks] = useState(false);
|
const [showCracks, setShowCracks] = useState(false);
|
||||||
const [collapse, setCollapse] = useState(false);
|
const [collapse, setCollapse] = useState(false);
|
||||||
const crackTimeout = useRef<any>(null);
|
const crackTimeout = useRef<any>(null);
|
||||||
@ -131,6 +131,14 @@ export default function BottomFooter() {
|
|||||||
|
|
||||||
{/* Bottom bar */}
|
{/* Bottom bar */}
|
||||||
<div className="max-w-6xl mx-auto mt-8 pt-6 flex flex-col md:flex-row items-center justify-between border-t border-gray-200/30">
|
<div className="max-w-6xl mx-auto mt-8 pt-6 flex flex-col md:flex-row items-center justify-between border-t border-gray-200/30">
|
||||||
|
<div className="flex flex-row items-center w-full md:w-auto">
|
||||||
|
<img
|
||||||
|
src="/logo.png"
|
||||||
|
alt="TremorTracker logo"
|
||||||
|
className="h-16 w-auto mr-4 object-contain"
|
||||||
|
style={{ maxHeight: 75 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<span className="text-sm flex items-center">
|
<span className="text-sm flex items-center">
|
||||||
<span className="mr-2">©</span> TremorTracker 2025
|
<span className="mr-2">©</span> TremorTracker 2025
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import Link from "next/link";
|
|
||||||
import React, { Dispatch, SetStateAction, useEffect, useRef } from "react";
|
import React, { Dispatch, SetStateAction, useEffect, useRef } from "react";
|
||||||
import { TbHexagon } from "react-icons/tb";
|
import { TbHexagon } from "react-icons/tb";
|
||||||
|
|
||||||
import GeologicalEvent from "@appTypes/Event";
|
import GeologicalEvent from "@appTypes/Event";
|
||||||
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
||||||
|
|
||||||
@ -18,12 +16,12 @@ interface SidebarProps {
|
|||||||
button2Name: string;
|
button2Name: string;
|
||||||
onButton2Click?: () => void;
|
onButton2Click?: () => void;
|
||||||
onButton1Click?: () => void;
|
onButton1Click?: () => void;
|
||||||
|
button1Disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function MagnitudeNumber({ magnitude }: { magnitude: number }) {
|
function MagnitudeNumber({ magnitude }: { magnitude: number }) {
|
||||||
const magnitudeStr = magnitude.toFixed(1);
|
const magnitudeStr = magnitude.toFixed(1);
|
||||||
const [whole, decimal] = magnitudeStr.split(".");
|
const [whole, decimal] = magnitudeStr.split(".");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative" style={{ color: getMagnitudeColor(magnitude) }}>
|
<div className="relative" style={{ color: getMagnitudeColor(magnitude) }}>
|
||||||
<TbHexagon size={40} className="drop-shadow-sm" />
|
<TbHexagon size={40} className="drop-shadow-sm" />
|
||||||
@ -38,8 +36,6 @@ function MagnitudeNumber({ magnitude }: { magnitude: number }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo change sidebar event highlighting on selection
|
|
||||||
|
|
||||||
export default function Sidebar({
|
export default function Sidebar({
|
||||||
logTitle,
|
logTitle,
|
||||||
logSubtitle,
|
logSubtitle,
|
||||||
@ -53,9 +49,9 @@ export default function Sidebar({
|
|||||||
button2Name,
|
button2Name,
|
||||||
onButton2Click,
|
onButton2Click,
|
||||||
onButton1Click,
|
onButton1Click,
|
||||||
|
button1Disabled = false,
|
||||||
}: SidebarProps) {
|
}: SidebarProps) {
|
||||||
const eventsContainerRef = useRef<HTMLDivElement>(null);
|
const eventsContainerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedEventId && eventsContainerRef.current) {
|
if (selectedEventId && eventsContainerRef.current) {
|
||||||
const selectedEventElement = eventsContainerRef.current.querySelector(`[data-event-id="${selectedEventId}"]`);
|
const selectedEventElement = eventsContainerRef.current.querySelector(`[data-event-id="${selectedEventId}"]`);
|
||||||
@ -74,26 +70,20 @@ export default function Sidebar({
|
|||||||
<div className="px-6 pb-8 border-b border-neutral-200">
|
<div className="px-6 pb-8 border-b border-neutral-200">
|
||||||
<h2 className="text-2xl font-bold text-neutral-800 mb-2">{logTitle}</h2>
|
<h2 className="text-2xl font-bold text-neutral-800 mb-2">{logTitle}</h2>
|
||||||
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
|
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
|
||||||
|
|
||||||
{onButton1Click ? (
|
|
||||||
<button
|
<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"
|
className={`mt-4 w-full py-2 px-4 rounded-lg transition-colors duration-200 font-medium
|
||||||
|
${button1Disabled
|
||||||
|
? "bg-gray-300 text-gray-500 cursor-not-allowed"
|
||||||
|
: "bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
}`}
|
||||||
onClick={onButton1Click}
|
onClick={onButton1Click}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
|
||||||
{button1Name}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<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"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
{button1Name}
|
|
||||||
</button>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
tabIndex={button1Disabled ? -1 : 0}
|
||||||
|
aria-disabled={button1Disabled ? "true" : "false"}
|
||||||
|
>
|
||||||
|
{button1Name}
|
||||||
|
</button>
|
||||||
<button
|
<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"
|
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"
|
||||||
onClick={onButton2Click}
|
onClick={onButton2Click}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user