Made some small improvements

This commit is contained in:
Tim Howitz 2025-06-01 14:18:32 +01:00
parent 6d25e72292
commit f88f783de9
6 changed files with 313 additions and 343 deletions

View File

@ -6,134 +6,117 @@ import Sidebar from "@components/Sidebar";
import { createPoster } from "@utils/axiosHelpers";
import { Earthquake } from "@prismaclient";
import { getRelativeDate } from "@utils/formatters";
import GeologicalEvent from "@appTypes/Event";
import GeologicalEvent from "@appTypes/GeologicalEvent";
import EarthquakeSearchModal from "@components/EarthquakeSearchModal";
import EarthquakeLogModal from "@components/EarthquakeLogModal"; // If you use a separate log modal
import { useStoreState } from "@hooks/store";
// Optional: "No Access Modal" - as in your original
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"
>
&times;
</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>
);
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">
&times;
</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>
);
}
export default function Earthquakes() {
const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState("");
const [searchModalOpen, setSearchModalOpen] = useState(false);
const [logModalOpen, setLogModalOpen] = useState(false);
const [noAccessModalOpen, setNoAccessModalOpen] = useState(false);
const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState("");
const [searchModalOpen, setSearchModalOpen] = useState(false);
const [logModalOpen, setLogModalOpen] = useState(false);
const [noAccessModalOpen, setNoAccessModalOpen] = useState(false);
// Your user/role logic
const user = useStoreState((state) => state.user);
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
const canLogEarthquake = role === "SCIENTIST" || role === "ADMIN";
// Your user/role logic
const user = useStoreState((state) => state.user);
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
const canLogEarthquake = role === "SCIENTIST" || role === "ADMIN";
// Fetch earthquakes (10 days recent)
const { data, error, isLoading, mutate } = useSWR(
"/api/earthquakes",
createPoster({ rangeDaysPrev: 10 })
);
// Fetch earthquakes (10 days recent)
const { data, error, isLoading, mutate } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 10 }));
// Shape for Map/Sidebar
const earthquakeEvents = useMemo(
() =>
data && data.earthquakes
? data.earthquakes
.map(
(x: Earthquake): GeologicalEvent => ({
id: x.code,
title: `Earthquake in ${x.location || (x.code && x.code.split("-")[2])}`,
magnitude: x.magnitude,
longitude: x.longitude,
latitude: x.latitude,
text1: "",
text2: getRelativeDate(x.date),
date: x.date,
})
)
.sort(
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [],
[data]
);
// Shape for Map/Sidebar
const earthquakeEvents = useMemo(
() =>
data && data.earthquakes
? data.earthquakes
.map(
(x: Earthquake): GeologicalEvent => ({
id: x.code,
title: `Earthquake in ${x.location || (x.code && x.code.split("-")[2])}`,
magnitude: x.magnitude,
longitude: x.longitude,
latitude: x.latitude,
text1: "",
text2: getRelativeDate(x.date),
date: x.date,
code: x.code,
})
)
.sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime())
: [],
[data]
);
// Handler for log
const handleLogClick = () => {
if (canLogEarthquake) {
setLogModalOpen(true);
} else {
setNoAccessModalOpen(true);
}
};
// Handler for log
const handleLogClick = () => {
if (canLogEarthquake) {
setLogModalOpen(true);
} else {
setNoAccessModalOpen(true);
}
};
return (
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
<div className="flex-grow h-full">
<Map
events={earthquakeEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
mapType="Earthquakes"
/>
</div>
<Sidebar
logTitle="Log an Earthquake"
logSubtitle="Record new earthquakes - time/date, location, magnitude, observatory and scientists"
recentsTitle="Recent Earthquakes"
events={earthquakeEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
button1Name="Log an Earthquake"
button2Name="Search Earthquakes"
onButton1Click={handleLogClick}
onButton2Click={() => setSearchModalOpen(true)}
button1Disabled={!canLogEarthquake}
/>
{/* ---- SEARCH MODAL ---- */}
<EarthquakeSearchModal
open={searchModalOpen}
onClose={() => setSearchModalOpen(false)}
onSelect={(eq) => setSelectedEventId(eq.code)}
/>
{/* ---- LOGGING MODAL ---- */}
<EarthquakeLogModal
open={logModalOpen}
onClose={() => setLogModalOpen(false)}
onSuccess={() => mutate()}
/>
{/* ---- NO ACCESS ---- */}
<NoAccessModal
open={noAccessModalOpen}
onClose={() => setNoAccessModalOpen(false)}
/>
</div>
);
return (
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
<div className="flex-grow h-full">
<Map
events={earthquakeEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
mapType="Earthquakes"
/>
</div>
<Sidebar
logTitle="Log an Earthquake"
logSubtitle="Record new earthquakes - time/date, location, magnitude, observatory and scientists"
recentsTitle="Recent Earthquakes"
events={earthquakeEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
button1Name="Log an Earthquake"
button2Name="Search Earthquakes"
onButton1Click={handleLogClick}
onButton2Click={() => setSearchModalOpen(true)}
button1Disabled={!canLogEarthquake}
/>
{/* ---- SEARCH MODAL ---- */}
<EarthquakeSearchModal
open={searchModalOpen}
onClose={() => setSearchModalOpen(false)}
onSelect={(eq) => setSelectedEventId(eq.code)}
/>
{/* ---- LOGGING MODAL ---- */}
<EarthquakeLogModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} />
{/* ---- NO ACCESS ---- */}
<NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} />
</div>
);
}

View File

@ -3,8 +3,8 @@
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
--background: #ffffff;
--foreground: #171717;
}
/* @media (prefers-color-scheme: dark) {
@ -15,216 +15,216 @@
} */
body {
color: var(--foreground);
background: var(--background);
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;
@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;
display: none !important;
}
/* Child elements */
.mapboxgl-popup-content h3 {
@apply text-sm font-medium text-neutral-800 !important;
@apply text-sm font-medium text-neutral-800 !important;
}
.mapboxgl-popup-content p {
@apply text-xs text-neutral-600 !important;
@apply text-xs text-neutral-600 !important;
}
.mapboxgl-popup-content p+p {
@apply text-neutral-500 !important;
.mapboxgl-popup-content p + p {
@apply text-neutral-500 !important;
}
.icon-link {
/* default styles if needed */
/* default styles if needed */
}
.icon-link:hover,
.icon-link:focus {
background-color: #16424b;
background-color: #16424b;
}
.icon-link:hover h3,
.icon-link:focus h3,
.icon-link:hover p,
.icon-link:focus p {
color: #fff !important;
color: #fff !important;
}
.icon-link:hover h3,
.icon-link:hover p,
.icon-link:focus h3,
.icon-link:focus p {
color: #111;
/* or black */
color: #111;
/* or black */
}
/* ---- LAVA FLOOD OVERLAY ---- */
.lava-flood-overlay {
pointer-events: none;
position: fixed;
top: -100vh;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
display: flex;
justify-content: center;
align-items: flex-start;
transition: top 0.9s cubic-bezier(.6, 0, .2, 1);
pointer-events: none;
position: fixed;
top: -100vh;
left: 0;
right: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
display: flex;
justify-content: center;
align-items: flex-start;
transition: top 0.9s cubic-bezier(0.6, 0, 0.2, 1);
}
.lava-flood-overlay.lava-active {
top: 0;
transition: top 0.33s cubic-bezier(.6, 0, .2, 1);
top: 0;
transition: top 0.33s cubic-bezier(0.6, 0, 0.2, 1);
}
.lava-flood-overlay img,
.lava-gradient {
width: 100vw;
height: 100vh;
object-fit: cover;
pointer-events: none;
user-select: none;
filter: brightness(1.15) saturate(1.8) drop-shadow(0 0 80px #ff5500);
width: 100vw;
height: 100vh;
object-fit: cover;
pointer-events: none;
user-select: none;
filter: brightness(1.15) saturate(1.8) drop-shadow(0 0 80px #ff5500);
}
/* ---- INSANE SCREEN SHAKE (LinkedIn) ---- */
@keyframes supershake {
0% {
transform: translate(0, 0) rotate(0);
}
0% {
transform: translate(0, 0) rotate(0);
}
5% {
transform: translate(-20px, 5px) rotate(-2deg);
}
5% {
transform: translate(-20px, 5px) rotate(-2deg);
}
10% {
transform: translate(18px, -8px) rotate(2deg);
}
10% {
transform: translate(18px, -8px) rotate(2deg);
}
15% {
transform: translate(-22px, 8px) rotate(-4deg);
}
15% {
transform: translate(-22px, 8px) rotate(-4deg);
}
20% {
transform: translate(22px, -2px) rotate(4deg);
}
20% {
transform: translate(22px, -2px) rotate(4deg);
}
25% {
transform: translate(-18px, 12px) rotate(-2deg);
}
25% {
transform: translate(-18px, 12px) rotate(-2deg);
}
30% {
transform: translate(18px, -10px) rotate(2deg);
}
30% {
transform: translate(18px, -10px) rotate(2deg);
}
35% {
transform: translate(-22px, 14px) rotate(-4deg);
}
35% {
transform: translate(-22px, 14px) rotate(-4deg);
}
40% {
transform: translate(22px, -12px) rotate(4deg);
}
40% {
transform: translate(22px, -12px) rotate(4deg);
}
45% {
transform: translate(-18px, 8px) rotate(-2deg);
}
45% {
transform: translate(-18px, 8px) rotate(-2deg);
}
50% {
transform: translate(18px, -14px) rotate(4deg);
}
50% {
transform: translate(18px, -14px) rotate(4deg);
}
55% {
transform: translate(-22px, 12px) rotate(-4deg);
}
55% {
transform: translate(-22px, 12px) rotate(-4deg);
}
60% {
transform: translate(22px, -8px) rotate(2deg);
}
60% {
transform: translate(22px, -8px) rotate(2deg);
}
65% {
transform: translate(-18px, 10px) rotate(-2deg);
}
65% {
transform: translate(-18px, 10px) rotate(-2deg);
}
70% {
transform: translate(18px, -12px) rotate(2deg);
}
70% {
transform: translate(18px, -12px) rotate(2deg);
}
75% {
transform: translate(-22px, 14px) rotate(-4deg);
}
75% {
transform: translate(-22px, 14px) rotate(-4deg);
}
80% {
transform: translate(22px, -10px) rotate(4deg);
}
80% {
transform: translate(22px, -10px) rotate(4deg);
}
85% {
transform: translate(-18px, 8px) rotate(-2deg);
}
85% {
transform: translate(-18px, 8px) rotate(-2deg);
}
90% {
transform: translate(18px, -14px) rotate(2deg);
}
90% {
transform: translate(18px, -14px) rotate(2deg);
}
95% {
transform: translate(-20px, 5px) rotate(-2deg);
}
95% {
transform: translate(-20px, 5px) rotate(-2deg);
}
100% {
transform: translate(0, 0) rotate(0);
}
100% {
transform: translate(0, 0) rotate(0);
}
}
.shake-screen {
animation: supershake 1s cubic-bezier(.36, .07, .19, .97) both;
animation: supershake 1s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}
/* ---- CRACK + COLLAPSE OVERLAY (X icon) ---- */
.crack-overlay {
pointer-events: none;
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.5s;
will-change: transform, opacity;
pointer-events: none;
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
z-index: 9999;
transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.45, 1), opacity 0.5s;
will-change: transform, opacity;
}
.crack {
position: absolute;
pointer-events: none;
position: absolute;
pointer-events: none;
}
.crack1 {
width: 35vw;
left: 10vw;
top: 22vh;
opacity: 0.8;
width: 35vw;
left: 10vw;
top: 22vh;
opacity: 0.8;
}
.crack2 {
width: 32vw;
right: 12vw;
top: 42vh;
opacity: 0.7;
transform: rotate(-8deg);
width: 32vw;
right: 12vw;
top: 42vh;
opacity: 0.7;
transform: rotate(-8deg);
}
/* Add more .crackN classes if using more cracks */
/* Collapse falling effect */
.crack-collapse {
transform: perspective(900px) rotateX(75deg) translateY(80vh) scale(0.9);
opacity: 0;
transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.6s;
transform: perspective(900px) rotateX(75deg) translateY(80vh) scale(0.9);
opacity: 0;
transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.45, 1), opacity 0.6s;
}

View File

@ -7,110 +7,94 @@ import LogObservatoryModal from "@/components/LogObservatoryModal"; // Adjust if
import { fetcher } from "@utils/axiosHelpers";
import { Observatory } from "@prismaclient";
import { getRelativeDate } from "@utils/formatters";
import GeologicalEvent from "@appTypes/Event";
import GeologicalEvent from "@appTypes/GeologicalEvent";
import { useStoreState } from "@hooks/store";
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"
>&times;</button>
<h2 className="font-bold text-xl mb-4">No Access</h2>
<p className="text-gray-600 mb-3">Sorry, You do not have access rights, please log in or contact an Admin.</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>
);
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">
&times;
</button>
<h2 className="font-bold text-xl mb-4">No Access</h2>
<p className="text-gray-600 mb-3">Sorry, You do not have access rights, please log in or contact an Admin.</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>
);
}
export default function Observatories() {
const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState("");
const [logModalOpen, setLogModalOpen] = useState(false);
const [noAccessModalOpen, setNoAccessModalOpen] = useState(false);
const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState("");
const [logModalOpen, setLogModalOpen] = useState(false);
const [noAccessModalOpen, setNoAccessModalOpen] = useState(false);
const user = useStoreState((state) => state.user);
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
const canLogObservatory = role === "SCIENTIST" || role === "ADMIN";
const user = useStoreState((state) => state.user);
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
const canLogObservatory = role === "SCIENTIST" || role === "ADMIN";
const { data, error, isLoading, mutate } = useSWR(
"/api/observatories",
fetcher
);
const { data, error, isLoading, mutate } = useSWR("/api/observatories", fetcher);
const observatoryEvents = useMemo(
() =>
data && data.observatories
? data.observatories
.map((x: Observatory): GeologicalEvent & { isFunctional: boolean } => ({
id: x.id.toString(),
title: ` ${x.name}`,
longitude: x.longitude,
latitude: x.latitude,
isFunctional: x.isFunctional, // <-- include this!
text1: "",
text2: getRelativeDate(x.dateEstablished),
date: x.dateEstablished,
}))
.sort(
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [],
[data]
);
const observatoryEvents = useMemo(
() =>
data && data.observatories
? data.observatories
.map((x: Observatory): GeologicalEvent & { isFunctional: boolean } => ({
id: x.id.toString(),
title: ` ${x.name}`,
longitude: x.longitude,
latitude: x.latitude,
isFunctional: x.isFunctional, // <-- include this!
text1: "",
text2: getRelativeDate(x.dateEstablished),
date: x.dateEstablished,
}))
.sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime())
: [],
[data]
);
const handleLogClick = () => {
if (canLogObservatory) {
setLogModalOpen(true);
} else {
setNoAccessModalOpen(true);
}
};
const handleLogClick = () => {
if (canLogObservatory) {
setLogModalOpen(true);
} else {
setNoAccessModalOpen(true);
}
};
return (
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
<div className="flex-grow h-full">
<Map
events={observatoryEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
mapType="observatories"
/>
</div>
<Sidebar
logTitle="Observatory Mapping"
logSubtitle="Record and search observatories - time/date set-up, location, scientists and recent earthquakes"
recentsTitle="New Observatories"
events={observatoryEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
button1Name="Log a New Observatory"
button2Name="Search Observatories"
onButton1Click={handleLogClick}
button1Disabled={!canLogObservatory}
/>
<LogObservatoryModal
open={logModalOpen}
onClose={() => setLogModalOpen(false)}
onSuccess={() => mutate()}
/>
<NoAccessModal
open={noAccessModalOpen}
onClose={() => setNoAccessModalOpen(false)}
/>
</div>
);
return (
<div className="flex h-[calc(100vh-3.5rem)] w-full overflow-hidden">
<div className="flex-grow h-full">
<Map
events={observatoryEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
mapType="observatories"
/>
</div>
<Sidebar
logTitle="Observatory Mapping"
logSubtitle="Record and search observatories - time/date set-up, location, scientists and recent earthquakes"
recentsTitle="New Observatories"
events={observatoryEvents}
selectedEventId={selectedEventId}
setSelectedEventId={setSelectedEventId}
hoveredEventId={hoveredEventId}
setHoveredEventId={setHoveredEventId}
button1Name="Log a New Observatory"
button2Name="Search Observatories"
onButton1Click={handleLogClick}
button1Disabled={!canLogObservatory}
/>
<LogObservatoryModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} />
<NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} />
</div>
);
}

View File

@ -2,7 +2,7 @@ import Link from "next/link";
import React, { Dispatch, SetStateAction, useState } from "react";
import { TbHexagon } from "react-icons/tb";
import Event from "@appTypes/Event";
import Event from "@appTypes/GeologicalEvent";
import getMagnitudeColor from "@utils/getMagnitudeColour";
function setButton1(button1Name) {

View File

@ -5,7 +5,7 @@ import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useSta
import { createRoot } from "react-dom/client";
import { GiObservatory } from "react-icons/gi";
import GeologicalEvent from "@appTypes/Event";
import GeologicalEvent from "@appTypes/GeologicalEvent";
import getMagnitudeColor from "@utils/getMagnitudeColour";
interface MapComponentProps {
@ -111,12 +111,12 @@ function MapComponent({
}
const observatoryElement = document.createElement("div");
const root = createRoot(observatoryElement);
root.render(
<GiObservatory
className={`text-2xl drop-shadow-lg ${event.isFunctional === false ? "text-gray-400" : "text-blue-600"}`}
/>
);
const root = createRoot(observatoryElement);
root.render(
<GiObservatory
className={`text-2xl drop-shadow-lg ${event.isFunctional === false ? "text-gray-400" : "text-blue-600"}`}
/>
);
quakeElement.appendChild(pulseElement);
quakeElement.appendChild(dotElement);
@ -128,6 +128,7 @@ function MapComponent({
const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(`
<div>
<h3>${event.title}</h3>
${mapType !== "observatories" ? `<p style="margin-bottom:3px;">${event.code}` : null}
${mapType === "observatories" ? `<p>${event.text1}</p>` : `<p>Magnitude: ${event.magnitude}</p>`}
<p>${event.text2}</p>
</div>

View File

@ -3,8 +3,10 @@ interface GeologicalEvent {
date: Date;
title: string;
magnitude?: number;
code?: string;
longitude: number;
latitude: number;
isFunctional?: boolean;
text1: string;
text2: string;
}