diff --git a/src/app/earthquakes/page.tsx b/src/app/earthquakes/page.tsx index 5632ba1..b3a944b 100644 --- a/src/app/earthquakes/page.tsx +++ b/src/app/earthquakes/page.tsx @@ -2,65 +2,157 @@ import { useMemo, useState } from "react"; import useSWR from "swr"; - import Map from "@components/Map"; 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 axios from "axios"; -export default function Earthquakes() { - const [selectedEventId, setSelectedEventId] = useState(""); - const [hoveredEventId, setHoveredEventId] = useState(""); - // todo properly integrate loading - const { data, error, isLoading } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 5 })); +// --- SEARCH MODAL COMPONENT --- +function EarthquakeSearchModal({ open, onClose, onSelect }) { + const [search, setSearch] = useState(""); + const [results, setResults] = useState([]); + const [loading, setLoading] = useState(false); - const earthquakeEvents = useMemo( - () => - data && data.earthquakes - ? data.earthquakes - .map( - (x: Earthquake): GeologicalEvent => ({ - id: x.code, - title: `Earthquake in ${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()) // Remove Date conversion - : [], - [data] - ); + const handleSearch = async (e) => { + e.preventDefault(); + setLoading(true); + setResults([]); + try { + const res = await axios.post("/api/earthquakes/search", { query: search }); + setResults(res.data.earthquakes || []); + } catch (e) { + alert("Failed to search."); + } + setLoading(false); + }; - return ( -
-
- -
- -
- ); + if (!open) return null; + return ( +
+
+ +

Search Earthquakes

+
+ setSearch(e.target.value)} + className="flex-grow px-3 py-2 border rounded" + required + /> + +
+
+ {results.length === 0 && !loading && search !== "" && ( +

No results found.

+ )} +
    + {results.map(eq => ( +
  • { onSelect(eq); onClose(); }} + tabIndex={0} + > +
    + {eq.code} {" "} + {eq.location} {new Date(eq.date).toLocaleDateString()} +
    +
    = 7 ? "bg-red-500" : eq.magnitude >= 6 ? "bg-orange-400" : "bg-yellow-400"}`}> + {eq.magnitude} +
    +
  • + ))} +
+
+
+
+ ); } + +// --- MAIN PAGE COMPONENT --- +export default function Earthquakes() { + const [selectedEventId, setSelectedEventId] = useState(""); + const [hoveredEventId, setHoveredEventId] = useState(""); + + // Search modal state + const [searchModalOpen, setSearchModalOpen] = useState(false); + + // Fetch recent earthquakes as before + const { data, error, isLoading } = useSWR("/api/earthquakes", createPoster({ rangeDaysPrev: 5 })); + + // Prepare events for maps/sidebar + const earthquakeEvents = useMemo( + () => + data && data.earthquakes + ? data.earthquakes + .map( + (x: Earthquake): GeologicalEvent => ({ + id: x.code, + title: `Earthquake in ${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] + ); + + // Optional: show details of selected search result (not implemented here) + // const [selectedSearchResult, setSelectedSearchResult] = useState(null); + + return ( +
+
+ +
+ setSearchModalOpen(true)} // <-- important! + /> + setSearchModalOpen(false)} + onSelect={eq => { + setSelectedEventId(eq.code); // select on map/sidebar + // setSelectedSearchResult(eq); // you can use this if you want to show detail modal + }} + /> +
+ ); +} \ No newline at end of file diff --git a/src/app/earthquakes/search/route.ts b/src/app/earthquakes/search/route.ts new file mode 100644 index 0000000..48ca6c9 --- /dev/null +++ b/src/app/earthquakes/search/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from "next/server"; +import { prisma } from "@utils/prisma"; + +export async function POST(req: Request) { + try { + const { query } = await req.json(); + + // Find earthquakes where either code or location matches (case-insensitive) + const earthquakes = await prisma.earthquake.findMany({ + where: { + OR: [ + { code: { contains: query, mode: "insensitive" } }, + { location: { contains: query, mode: "insensitive" } } + ], + }, + orderBy: { date: "desc" }, + take: 20, // limit results + }); + + return NextResponse.json({ earthquakes, message: "Success" }); + } catch (error) { + console.error("Error in earthquake search", error); + return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index d44b733..f9b439e 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -16,6 +16,7 @@ interface SidebarProps { setHoveredEventId: Dispatch>; button1Name: string; button2Name: string; + onButton2Click?: () => void; } function MagnitudeNumber({ magnitude }: { magnitude: number }) { @@ -47,6 +48,7 @@ export default function Sidebar({ setHoveredEventId, button1Name, button2Name, + onButton2Click, }: SidebarProps) { const eventsContainerRef = useRef(null); @@ -68,17 +70,21 @@ export default function Sidebar({

{logTitle}

{logSubtitle}

- - - - - - -
+ + + + + {/* "Search Earthquakes" should NOT be wrapped in a Link! */} + +

{recentsTitle}

diff --git a/src/components/sidebar_e.tsx b/src/components/sidebar_e.tsx index 6c1dfe6..f2dfcbc 100644 --- a/src/components/sidebar_e.tsx +++ b/src/components/sidebar_e.tsx @@ -1,42 +1,73 @@ import Link from "next/link"; import React from "react"; -const Sidebar = () => { - return ( -
-
-

Log an Earthquake

-

- Record new earthquakes - time/date, location, magnitude, observatory and scientists -

- -
+type SidebarProps = { + logTitle: string; + logSubtitle: string; + recentsTitle: string; + events: any[]; // Or type this better if desired + button1Name: string; + button2Name: string; + onButton1Click?: () => void; + onButton2Click?: () => void; +} - {/* Section: Recent Events - Will need to be replaced with a link to the database*/} -
-

Recent Events

-
    -
  • -

    Earthquake in California

    -

    Magnitude 5.3

    -

    2 hours ago

    -
  • -
  • -

    Tremor in Japan

    -

    Magnitude 4.7

    -

    5 hours ago

    -
  • -
  • -

    Tremor in Spain

    -

    Magnitude 2.1

    -

    10 hours ago

    -
  • -
-
-
- ); +const Sidebar: React.FC = ({ + logTitle, + logSubtitle, + recentsTitle, + events, + button1Name, + button2Name, + onButton1Click, + onButton2Click, +}) => { + return ( +
+
+

{logTitle}

+

{logSubtitle}

+ + +
+
+

{recentsTitle}

+
    + {events.map((item, idx) => ( +
  • +
    +

    {item.title}

    +

    + {item.text2} +

    +
    +
    = 7 + ? "bg-red-500 text-white" + : item.magnitude >= 6 + ? "bg-orange-400 text-white" + : "bg-yellow-400 text-black" + }`}> + {item.magnitude} +
    +
  • + ))} +
+
+
+ ); }; -export default Sidebar; +export default Sidebar; \ No newline at end of file