Compare commits

..

No commits in common. "6cd95fa0e44cffca24b84b676f8b2629b6757d42" and "6d25e7229244ace5a074cbaa6d84429762c364d0" have entirely different histories.

7 changed files with 455 additions and 429 deletions

View File

@ -6,7 +6,7 @@ import Sidebar from "@components/Sidebar";
import { createPoster } from "@utils/axiosHelpers"; import { createPoster } from "@utils/axiosHelpers";
import { Earthquake } from "@prismaclient"; import { Earthquake } from "@prismaclient";
import { getRelativeDate } from "@utils/formatters"; import { getRelativeDate } from "@utils/formatters";
import GeologicalEvent from "@appTypes/GeologicalEvent"; import GeologicalEvent from "@appTypes/Event";
import EarthquakeSearchModal from "@components/EarthquakeSearchModal"; import EarthquakeSearchModal from "@components/EarthquakeSearchModal";
import EarthquakeLogModal from "@components/EarthquakeLogModal"; // If you use a separate log modal import EarthquakeLogModal from "@components/EarthquakeLogModal"; // If you use a separate log modal
import { useStoreState } from "@hooks/store"; import { useStoreState } from "@hooks/store";
@ -17,22 +17,27 @@ function NoAccessModal({ open, onClose }) {
return ( return (
<div className="fixed z-50 inset-0 bg-black/40 flex items-center justify-center"> <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"> <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
onClick={onClose}
className="absolute right-4 top-4 text-gray-500 hover:text-black text-lg"
aria-label="Close"
>
&times; &times;
</button> </button>
<h2 className="font-bold text-xl mb-4">Access Denied</h2> <h2 className="font-bold text-xl mb-4">Access Denied</h2>
<p className="text-gray-600 mb-3"> <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 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
is a mistake
</p> </p>
<button onClick={onClose} className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2"> <button
OK onClick={onClose}
</button> className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2"
>OK</button>
</div> </div>
</div> </div>
); );
} }
export default function Earthquakes() { export default function Earthquakes() {
const [selectedEventId, setSelectedEventId] = useState(""); const [selectedEventId, setSelectedEventId] = useState("");
const [hoveredEventId, setHoveredEventId] = useState(""); const [hoveredEventId, setHoveredEventId] = useState("");
@ -46,7 +51,10 @@ export default function Earthquakes() {
const canLogEarthquake = role === "SCIENTIST" || role === "ADMIN"; const canLogEarthquake = role === "SCIENTIST" || role === "ADMIN";
// Fetch earthquakes (10 days recent) // Fetch earthquakes (10 days recent)
const { data, error, isLoading, mutate } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 10 })); const { data, error, isLoading, mutate } = useSWR(
"/api/earthquakes",
createPoster({ rangeDaysPrev: 10 })
);
// Shape for Map/Sidebar // Shape for Map/Sidebar
const earthquakeEvents = useMemo( const earthquakeEvents = useMemo(
@ -63,10 +71,12 @@ export default function Earthquakes() {
text1: "", text1: "",
text2: getRelativeDate(x.date), text2: getRelativeDate(x.date),
date: x.date, date: x.date,
code: x.code,
}) })
) )
.sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime()) .sort(
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [], : [],
[data] [data]
); );
@ -114,9 +124,16 @@ export default function Earthquakes() {
onSelect={(eq) => setSelectedEventId(eq.code)} onSelect={(eq) => setSelectedEventId(eq.code)}
/> />
{/* ---- LOGGING MODAL ---- */} {/* ---- LOGGING MODAL ---- */}
<EarthquakeLogModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} /> <EarthquakeLogModal
open={logModalOpen}
onClose={() => setLogModalOpen(false)}
onSuccess={() => mutate()}
/>
{/* ---- NO ACCESS ---- */} {/* ---- NO ACCESS ---- */}
<NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} /> <NoAccessModal
open={noAccessModalOpen}
onClose={() => setNoAccessModalOpen(false)}
/>
</div> </div>
); );
} }

View File

@ -79,12 +79,12 @@ body {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: flex-start; align-items: flex-start;
transition: top 0.9s cubic-bezier(0.6, 0, 0.2, 1); transition: top 0.9s cubic-bezier(.6, 0, .2, 1);
} }
.lava-flood-overlay.lava-active { .lava-flood-overlay.lava-active {
top: 0; top: 0;
transition: top 0.33s cubic-bezier(0.6, 0, 0.2, 1); transition: top 0.33s cubic-bezier(.6, 0, .2, 1);
} }
.lava-flood-overlay img, .lava-flood-overlay img,
@ -185,7 +185,7 @@ body {
} }
.shake-screen { .shake-screen {
animation: supershake 1s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; animation: supershake 1s cubic-bezier(.36, .07, .19, .97) both;
} }
/* ---- CRACK + COLLAPSE OVERLAY (X icon) ---- */ /* ---- CRACK + COLLAPSE OVERLAY (X icon) ---- */
@ -196,7 +196,7 @@ body {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
z-index: 9999; z-index: 9999;
transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.45, 1), opacity 0.5s; transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.5s;
will-change: transform, opacity; will-change: transform, opacity;
} }
@ -226,5 +226,5 @@ body {
.crack-collapse { .crack-collapse {
transform: perspective(900px) rotateX(75deg) translateY(80vh) scale(0.9); transform: perspective(900px) rotateX(75deg) translateY(80vh) scale(0.9);
opacity: 0; opacity: 0;
transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.45, 1), opacity 0.6s; transition: transform 1.1s cubic-bezier(.65, .05, .45, 1), opacity 0.6s;
} }

View File

@ -7,7 +7,7 @@ import LogObservatoryModal from "@/components/LogObservatoryModal"; // Adjust if
import { fetcher } from "@utils/axiosHelpers"; import { fetcher } from "@utils/axiosHelpers";
import { Observatory } from "@prismaclient"; import { Observatory } from "@prismaclient";
import { getRelativeDate } from "@utils/formatters"; import { getRelativeDate } from "@utils/formatters";
import GeologicalEvent from "@appTypes/GeologicalEvent"; import GeologicalEvent from "@appTypes/Event";
import { useStoreState } from "@hooks/store"; import { useStoreState } from "@hooks/store";
function NoAccessModal({ open, onClose }) { function NoAccessModal({ open, onClose }) {
@ -15,14 +15,17 @@ function NoAccessModal({ open, onClose }) {
return ( return (
<div className="fixed z-50 inset-0 bg-black/40 flex items-center justify-center"> <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"> <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
&times; onClick={onClose}
</button> 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> <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> <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"> <button
OK onClick={onClose}
</button> className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2"
>OK</button>
</div> </div>
</div> </div>
); );
@ -38,7 +41,10 @@ export default function Observatories() {
const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST"; const role: "GUEST" | "SCIENTIST" | "ADMIN" = user?.role ?? "GUEST";
const canLogObservatory = role === "SCIENTIST" || role === "ADMIN"; 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( const observatoryEvents = useMemo(
() => () =>
@ -54,7 +60,10 @@ export default function Observatories() {
text2: getRelativeDate(x.dateEstablished), text2: getRelativeDate(x.dateEstablished),
date: x.dateEstablished, date: x.dateEstablished,
})) }))
.sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime()) .sort(
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [], : [],
[data] [data]
); );
@ -93,8 +102,15 @@ export default function Observatories() {
onButton1Click={handleLogClick} onButton1Click={handleLogClick}
button1Disabled={!canLogObservatory} button1Disabled={!canLogObservatory}
/> />
<LogObservatoryModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} /> <LogObservatoryModal
<NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} /> open={logModalOpen}
onClose={() => setLogModalOpen(false)}
onSuccess={() => mutate()}
/>
<NoAccessModal
open={noAccessModalOpen}
onClose={() => setNoAccessModalOpen(false)}
/>
</div> </div>
); );
} }

View File

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

View File

@ -5,7 +5,7 @@ import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useSta
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { GiObservatory } from "react-icons/gi"; import { GiObservatory } from "react-icons/gi";
import GeologicalEvent from "@appTypes/GeologicalEvent"; import GeologicalEvent from "@appTypes/Event";
import getMagnitudeColor from "@utils/getMagnitudeColour"; import getMagnitudeColor from "@utils/getMagnitudeColour";
interface MapComponentProps { interface MapComponentProps {
@ -128,7 +128,6 @@ function MapComponent({
const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(` const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(`
<div> <div>
<h3>${event.title}</h3> <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>`} ${mapType === "observatories" ? `<p>${event.text1}</p>` : `<p>Magnitude: ${event.magnitude}</p>`}
<p>${event.text2}</p> <p>${event.text2}</p>
</div> </div>

View File

@ -1,6 +1,6 @@
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/GeologicalEvent"; import GeologicalEvent from "@appTypes/Event";
import getMagnitudeColor from "@utils/getMagnitudeColour"; import getMagnitudeColor from "@utils/getMagnitudeColour";
interface SidebarProps { interface SidebarProps {
logTitle: string; logTitle: string;
@ -71,13 +71,13 @@ export default function Sidebar({
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p> <p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
<button <button
className={`mt-4 w-full 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
button1Disabled
? "bg-gray-300 text-gray-500 cursor-not-allowed" ? "bg-gray-300 text-gray-500 cursor-not-allowed"
: "bg-blue-600 hover:bg-blue-700 text-white" : "bg-blue-600 hover:bg-blue-700 text-white"
}`} }`}
onClick={onButton1Click} onClick={onButton1Click}
type="button" type="button"
tabIndex={button1Disabled ? -1 : 0} tabIndex={button1Disabled ? -1 : 0}
aria-disabled={button1Disabled ? "true" : "false"} aria-disabled={button1Disabled ? "true" : "false"}
> >
@ -100,13 +100,9 @@ export default function Sidebar({
<button <button
key={event.id} key={event.id}
data-event-id={event.id} data-event-id={event.id}
className={`w-full border ${ className={`w-full border ${hoveredEventId === event.id ? "bg-neutral-100" : "bg-white"} ${
selectedEventId === event.id selectedEventId === event.id ? "border-neutral-800" : "border-neutral-200"
? "border-neutral-500 border-2 shadow-md" } rounded-lg p-4 flex items-center gap-4 hover:bg-neutral-100 transition-colors duration-150 shadow-sm text-left`}
: hoveredEventId === event.id
? "bg-neutral-100 shadow-sm"
: "bg-white border-neutral-200 shadow-sm"
} rounded-lg p-4 flex items-center gap-4 hover:bg-neutral-100 transition-colors duration-150 text-left`}
onClick={() => { onClick={() => {
setSelectedEventId((prevEventId) => (prevEventId !== event.id ? event.id : "")); setSelectedEventId((prevEventId) => (prevEventId !== event.id ? event.id : ""));
}} }}

View File

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