Compare commits

..

2 Commits

Author SHA1 Message Date
6cd95fa0e4 Improved Sidebar styling 2025-06-01 14:18:44 +01:00
f88f783de9 Made some small improvements 2025-06-01 14:18:32 +01:00
7 changed files with 429 additions and 455 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/Event"; import GeologicalEvent from "@appTypes/GeologicalEvent";
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,27 +17,22 @@ 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 <button onClick={onClose} className="absolute right-4 top-4 text-gray-500 hover:text-black text-lg" aria-label="Close">
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 is a mistake 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> </p>
<button <button onClick={onClose} className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2">
onClick={onClose} OK
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2" </button>
>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("");
@ -51,10 +46,7 @@ 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( const { data, error, isLoading, mutate } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 10 }));
"/api/earthquakes",
createPoster({ rangeDaysPrev: 10 })
);
// Shape for Map/Sidebar // Shape for Map/Sidebar
const earthquakeEvents = useMemo( const earthquakeEvents = useMemo(
@ -71,12 +63,10 @@ export default function Earthquakes() {
text1: "", text1: "",
text2: getRelativeDate(x.date), text2: getRelativeDate(x.date),
date: x.date, date: x.date,
code: x.code,
}) })
) )
.sort( .sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime())
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [], : [],
[data] [data]
); );
@ -124,16 +114,9 @@ export default function Earthquakes() {
onSelect={(eq) => setSelectedEventId(eq.code)} onSelect={(eq) => setSelectedEventId(eq.code)}
/> />
{/* ---- LOGGING MODAL ---- */} {/* ---- LOGGING MODAL ---- */}
<EarthquakeLogModal <EarthquakeLogModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} />
open={logModalOpen}
onClose={() => setLogModalOpen(false)}
onSuccess={() => mutate()}
/>
{/* ---- NO ACCESS ---- */} {/* ---- NO ACCESS ---- */}
<NoAccessModal <NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} />
open={noAccessModalOpen}
onClose={() => setNoAccessModalOpen(false)}
/>
</div> </div>
); );
} }

View File

@ -38,7 +38,7 @@ body {
@apply text-xs text-neutral-600 !important; @apply text-xs text-neutral-600 !important;
} }
.mapboxgl-popup-content p+p { .mapboxgl-popup-content p + p {
@apply text-neutral-500 !important; @apply text-neutral-500 !important;
} }
@ -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(.6, 0, .2, 1); transition: top 0.9s cubic-bezier(0.6, 0, 0.2, 1);
} }
.lava-flood-overlay.lava-active { .lava-flood-overlay.lava-active {
top: 0; top: 0;
transition: top 0.33s cubic-bezier(.6, 0, .2, 1); transition: top 0.33s cubic-bezier(0.6, 0, 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(.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 + 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(.65, .05, .45, 1), opacity 0.5s; transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.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(.65, .05, .45, 1), opacity 0.6s; transition: transform 1.1s cubic-bezier(0.65, 0.05, 0.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/Event"; import GeologicalEvent from "@appTypes/GeologicalEvent";
import { useStoreState } from "@hooks/store"; import { useStoreState } from "@hooks/store";
function NoAccessModal({ open, onClose }) { function NoAccessModal({ open, onClose }) {
@ -15,17 +15,14 @@ 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 <button onClick={onClose} className="absolute right-4 top-4 text-gray-500 hover:text-black text-lg" aria-label="Close">
onClick={onClose} &times;
className="absolute right-4 top-4 text-gray-500 hover:text-black text-lg" </button>
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 <button onClick={onClose} className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2">
onClick={onClose} OK
className="bg-blue-600 text-white px-6 py-2 rounded hover:bg-blue-700 mt-2" </button>
>OK</button>
</div> </div>
</div> </div>
); );
@ -41,10 +38,7 @@ 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( const { data, error, isLoading, mutate } = useSWR("/api/observatories", fetcher);
"/api/observatories",
fetcher
);
const observatoryEvents = useMemo( const observatoryEvents = useMemo(
() => () =>
@ -60,10 +54,7 @@ export default function Observatories() {
text2: getRelativeDate(x.dateEstablished), text2: getRelativeDate(x.dateEstablished),
date: x.dateEstablished, date: x.dateEstablished,
})) }))
.sort( .sort((a: GeologicalEvent, b: GeologicalEvent) => new Date(b.date).getTime() - new Date(a.date).getTime())
(a: GeologicalEvent, b: GeologicalEvent) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
)
: [], : [],
[data] [data]
); );
@ -102,15 +93,8 @@ export default function Observatories() {
onButton1Click={handleLogClick} onButton1Click={handleLogClick}
button1Disabled={!canLogObservatory} button1Disabled={!canLogObservatory}
/> />
<LogObservatoryModal <LogObservatoryModal open={logModalOpen} onClose={() => setLogModalOpen(false)} onSuccess={() => mutate()} />
open={logModalOpen} <NoAccessModal open={noAccessModalOpen} onClose={() => setNoAccessModalOpen(false)} />
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/Event"; import Event from "@appTypes/GeologicalEvent";
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/Event"; import GeologicalEvent from "@appTypes/GeologicalEvent";
import getMagnitudeColor from "@utils/getMagnitudeColour"; import getMagnitudeColor from "@utils/getMagnitudeColour";
interface MapComponentProps { interface MapComponentProps {
@ -128,6 +128,7 @@ 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/Event"; import GeologicalEvent from "@appTypes/GeologicalEvent";
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,9 +100,13 @@ 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 ${hoveredEventId === event.id ? "bg-neutral-100" : "bg-white"} ${ className={`w-full border ${
selectedEventId === event.id ? "border-neutral-800" : "border-neutral-200" selectedEventId === event.id
} rounded-lg p-4 flex items-center gap-4 hover:bg-neutral-100 transition-colors duration-150 shadow-sm text-left`} ? "border-neutral-500 border-2 shadow-md"
: 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,8 +3,10 @@ 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;
} }