From ec25bf07253525eb23c2170f6dd7d0358110af88 Mon Sep 17 00:00:00 2001 From: Tim Howitz Date: Sun, 4 May 2025 16:04:44 +0100 Subject: [PATCH] Created styling standard --- .vscode/settings.json | 62 +++++ eslint.config.mjs | 13 +- next.config.ts | 2 +- package-lock.json | 2 +- package.json | 2 +- src/app/api/earthquakes/route.ts | 4 +- src/app/api/functions/csvReadWrite.ts | 146 ++++++------ src/app/api/login/route.ts | 8 +- src/app/api/observatories/route.ts | 4 +- src/app/api/signup/route.ts | 10 +- src/app/contact-us/page.tsx | 4 +- src/app/earthquakes/page.tsx | 10 +- src/app/globals.css | 18 +- src/app/observatories/page.tsx | 10 +- src/app/our-mission/page.tsx | 116 ++++------ src/app/page.tsx | 240 ++++++++++--------- src/app/shop/page.tsx | 12 +- src/app/the-team/page.tsx | 2 +- src/app/user/page.tsx | 10 +- src/app/warehouse/page.tsx | 3 +- src/components/AuthModal.tsx | 4 +- src/components/map.tsx | 319 +++++++++++++------------- src/components/navbar.tsx | 16 +- src/types/Event.ts | 14 +- src/utils/fetcher.ts | 2 +- src/utils/getMagnitudeColour.tsx | 23 +- 26 files changed, 551 insertions(+), 505 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c519660 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,62 @@ +{ + "editor.insertSpaces": true, + // Ignores the warning when Git is missing + "git.ignoreMissingGitWarning": true, + // Disable crash reports being sent to Microsoft. + "telemetry.enableCrashReporter": false, + // Disable usage data and errors being sent to Microsoft. + "telemetry.enableTelemetry": false, + "editor.formatOnSave": true, + "cmake.configureOnOpen": false, + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8", + "editor.formatOnSave": true + }, + "autopep8.args": ["--max-line-length=200"], + "editor.detectIndentation": false, + "git.confirmSync": false, + "editor.rulers": [ + { + "column": 120 + } + ], + "git.autofetch": true, + "files.autoSave": "onFocusChange", + "cSpell.language": "en-GB", + "C_Cpp.formatting": "disabled", + "cmake.showOptionsMovedNotification": false, + "diffEditor.renderSideBySide": false, + "terminal.integrated.commandsToSkipShell": ["matlab.interrupt"], + "[matlab]": { + "editor.defaultFormatter": "AffenWiesel.matlab-formatter" + }, + "workbench.editorAssociations": { + "*.pdf": "latex-workshop-pdf-hook" + }, + "notebook.output.textLineLimit": 50, + "prettier.printWidth": 130, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "prettier.useTabs": true, + "editor.tabSize": 4, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[cpp]": { + "editor.defaultFormatter": "ms-vscode.cpptools" + }, + "C_Cpp.default.cppStandard": "gnu++23", + "C_Cpp.default.cStandard": "gnu23", + "diffEditor.hideUnchangedRegions.enabled": true, + "python.createEnvironment.trigger": "off", + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "importSorter.generalConfiguration.sortOnBeforeSave": true, + "cSpell.words": [ + "vars" + ] +} + + diff --git a/eslint.config.mjs b/eslint.config.mjs index c85fb67..1ea5b2f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -6,11 +6,12 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, + baseDirectory: __dirname, }); -const eslintConfig = [ - ...compat.extends("next/core-web-vitals", "next/typescript"), -]; - -export default eslintConfig; +export default tseslint.config([...compat.extends("next/core-web-vitals", "next/typescript")], { + rules: { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error", + }, +}); diff --git a/next.config.ts b/next.config.ts index e9ffa30..7921f35 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + /* config options here */ }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 9ade85b..db6057c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8524,4 +8524,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index f14f100..b7bbc99 100644 --- a/package.json +++ b/package.json @@ -47,4 +47,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/src/app/api/earthquakes/route.ts b/src/app/api/earthquakes/route.ts index 17c6a27..114ba70 100644 --- a/src/app/api/earthquakes/route.ts +++ b/src/app/api/earthquakes/route.ts @@ -1,6 +1,6 @@ -import { NextResponse } from 'next/server'; +import { NextResponse } from "next/server"; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; const usingPrisma = false; let prisma: PrismaClient; diff --git a/src/app/api/functions/csvReadWrite.ts b/src/app/api/functions/csvReadWrite.ts index 29eb79b..250e4c9 100644 --- a/src/app/api/functions/csvReadWrite.ts +++ b/src/app/api/functions/csvReadWrite.ts @@ -1,69 +1,69 @@ -import path from "path"; -import fs from "fs"; import csv from "csv-parser"; +import fs from "fs"; +import path from "path"; export type User = { - name: string; - email: string; - password: string; - accessLevel: string; + name: string; + email: string; + password: string; + accessLevel: string; }; /** * @returns {array} - Array of Objects containing user data */ export async function readUserCsv(): Promise { - return new Promise((resolve, reject) => { - // Dynamic CSV location generation - let csvPath = path.dirname(__dirname); // /login - csvPath = path.dirname(csvPath); // /api - csvPath = path.dirname(csvPath); // /app - csvPath = path.dirname(csvPath); // /src - csvPath = path.dirname(csvPath); // /[project] - csvPath = path.dirname(csvPath); // /termor-tracker - csvPath = path.join(csvPath, "src", "databases", "Users.csv"); + return new Promise((resolve, reject) => { + // Dynamic CSV location generation + let csvPath = path.dirname(__dirname); // /login + csvPath = path.dirname(csvPath); // /api + csvPath = path.dirname(csvPath); // /app + csvPath = path.dirname(csvPath); // /src + csvPath = path.dirname(csvPath); // /[project] + csvPath = path.dirname(csvPath); // /termor-tracker + csvPath = path.join(csvPath, "src", "databases", "Users.csv"); - // Forms array for user data - let results: User[] = []; - // Reads data and adds it to results - fs.createReadStream(csvPath) - .pipe(csv()) - .on("data", (data) => results.push(data)) - .on("end", () => { - resolve(results); - }) - .on("error", (error) => { - reject(error); - }); - }); + // Forms array for user data + let results: User[] = []; + // Reads data and adds it to results + fs.createReadStream(csvPath) + .pipe(csv()) + .on("data", (data) => results.push(data)) + .on("end", () => { + resolve(results); + }) + .on("error", (error) => { + reject(error); + }); + }); } export function findUserByEmail(users: User[], email: string): User | undefined { - return users.find((user) => user.email === email); + return users.find((user) => user.email === email); } export async function passwordStrengthCheck(password: string): Promise { - if (password.length < 8) { - return "short"; - } else if (password.length > 16) { - return "long"; - } - const lowercaseRegex = /[a-z]/; - const uppercaseRegex = /[A-Z]/; - const digitRegex = /\d/; - const specialCharRegex = /[!@#$%^&*]/; - if (!lowercaseRegex.test(password)) { - return "no lower"; - } else if (!uppercaseRegex.test(password)) { - return "no upper"; - } else if (!digitRegex.test(password)) { - return "no digit"; - } else if (!specialCharRegex.test(password)) { - return "no special"; - } else { - return "secure"; - } - return "end of function"; + if (password.length < 8) { + return "short"; + } else if (password.length > 16) { + return "long"; + } + const lowercaseRegex = /[a-z]/; + const uppercaseRegex = /[A-Z]/; + const digitRegex = /\d/; + const specialCharRegex = /[!@#$%^&*]/; + if (!lowercaseRegex.test(password)) { + return "no lower"; + } else if (!uppercaseRegex.test(password)) { + return "no upper"; + } else if (!digitRegex.test(password)) { + return "no digit"; + } else if (!specialCharRegex.test(password)) { + return "no special"; + } else { + return "secure"; + } + return "end of function"; } /** @@ -72,28 +72,28 @@ export async function passwordStrengthCheck(password: string): Promise { * @returns {Promise} */ export async function writeUserCsv(users: User[]): Promise { - return new Promise((resolve, reject) => { - // Dynamic CSV location generation - let csvPath = path.dirname(__dirname); // /login - csvPath = path.dirname(csvPath); // /api - csvPath = path.dirname(csvPath); // /app - csvPath = path.dirname(csvPath); // /src - csvPath = path.dirname(csvPath); // /[project] - csvPath = path.dirname(csvPath); // /termor-tracker - csvPath = path.join(csvPath, "src", "databases", "Users.csv"); + return new Promise((resolve, reject) => { + // Dynamic CSV location generation + let csvPath = path.dirname(__dirname); // /login + csvPath = path.dirname(csvPath); // /api + csvPath = path.dirname(csvPath); // /app + csvPath = path.dirname(csvPath); // /src + csvPath = path.dirname(csvPath); // /[project] + csvPath = path.dirname(csvPath); // /termor-tracker + csvPath = path.join(csvPath, "src", "databases", "Users.csv"); - // Prepare CSV data as a string - const headers = "name,email,password,level"; // CSV headers - const rows = users.map((user) => `${user.name},${user.email},${user.password},${user.accessLevel}`); - const csvData = `${headers}\n${rows.join("\n")}`; + // Prepare CSV data as a string + const headers = "name,email,password,level"; // CSV headers + const rows = users.map((user) => `${user.name},${user.email},${user.password},${user.accessLevel}`); + const csvData = `${headers}\n${rows.join("\n")}`; - // Write data to the file - fs.writeFile(csvPath, csvData, (error) => { - if (error) { - reject(error); // Reject promise on error - } else { - resolve(); // Resolve the promise if successful - } - }); - }); -} \ No newline at end of file + // Write data to the file + fs.writeFile(csvPath, csvData, (error) => { + if (error) { + reject(error); // Reject promise on error + } else { + resolve(); // Resolve the promise if successful + } + }); + }); +} diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 9ff9622..a5ef4f7 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,9 +1,9 @@ -import bcrypt from 'bcrypt'; -import { NextResponse } from 'next/server'; +import bcrypt from "bcrypt"; +import { NextResponse } from "next/server"; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; -import { findUserByEmail, readUserCsv, User } from '../functions/csvReadWrite'; +import { findUserByEmail, readUserCsv, User } from "../functions/csvReadWrite"; const usingPrisma = false; let prisma: PrismaClient; diff --git a/src/app/api/observatories/route.ts b/src/app/api/observatories/route.ts index 3f17067..e4021df 100644 --- a/src/app/api/observatories/route.ts +++ b/src/app/api/observatories/route.ts @@ -1,6 +1,6 @@ -import { NextResponse } from 'next/server'; +import { NextResponse } from "next/server"; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; const usingPrisma = false; let prisma: PrismaClient; diff --git a/src/app/api/signup/route.ts b/src/app/api/signup/route.ts index 8e27c71..9360f6c 100644 --- a/src/app/api/signup/route.ts +++ b/src/app/api/signup/route.ts @@ -1,11 +1,9 @@ -import bcrypt from 'bcrypt'; -import { NextResponse } from 'next/server'; +import bcrypt from "bcrypt"; +import { NextResponse } from "next/server"; -import { PrismaClient } from '@prisma/client'; +import { PrismaClient } from "@prisma/client"; -import { - findUserByEmail, passwordStrengthCheck, readUserCsv, User, writeUserCsv -} from '../functions/csvReadWrite'; +import { findUserByEmail, passwordStrengthCheck, readUserCsv, User, writeUserCsv } from "../functions/csvReadWrite"; const usingPrisma = false; let prisma: PrismaClient; diff --git a/src/app/contact-us/page.tsx b/src/app/contact-us/page.tsx index 4a5c07b..955d99c 100644 --- a/src/app/contact-us/page.tsx +++ b/src/app/contact-us/page.tsx @@ -1,6 +1,6 @@ "use client"; -import Image from 'next/image'; -import React, { useState } from 'react'; +import Image from "next/image"; +import React, { useState } from "react"; const ContactUs = () => { const [formData, setFormData] = useState({ diff --git a/src/app/earthquakes/page.tsx b/src/app/earthquakes/page.tsx index ca0200a..3b53d75 100644 --- a/src/app/earthquakes/page.tsx +++ b/src/app/earthquakes/page.tsx @@ -1,11 +1,11 @@ "use client"; -import { useMemo, useState } from 'react'; -import useSWR from 'swr'; +import { useMemo, useState } from "react"; +import useSWR from "swr"; -import Map from '@components/Map'; -import Sidebar from '@components/Sidebar'; -import { fetcher } from '@utils/fetcher'; +import Map from "@components/Map"; +import Sidebar from "@components/Sidebar"; +import { fetcher } from "@utils/fetcher"; export default function Earthquakes() { const [selectedEventId, setSelectedEventId] = useState(""); diff --git a/src/app/globals.css b/src/app/globals.css index 48e87e9..cf9ac1f 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -3,8 +3,8 @@ @tailwind utilities; :root { - --background: #ffffff; - --foreground: #171717; + --background: #ffffff; + --foreground: #171717; } /* @media (prefers-color-scheme: dark) { @@ -15,29 +15,29 @@ } */ 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; + @apply text-neutral-500 !important; } diff --git a/src/app/observatories/page.tsx b/src/app/observatories/page.tsx index 7d3e707..f608af9 100644 --- a/src/app/observatories/page.tsx +++ b/src/app/observatories/page.tsx @@ -1,11 +1,11 @@ "use client"; -import { useMemo, useState } from 'react'; -import useSWR from 'swr'; +import { useMemo, useState } from "react"; +import useSWR from "swr"; -import Sidebar from '@/components/Sidebar'; -import Map from '@components/Map'; -import { fetcher } from '@utils/fetcher'; +import Sidebar from "@/components/Sidebar"; +import Map from "@components/Map"; +import { fetcher } from "@utils/fetcher"; export default function Observatories() { const [selectedEventId, setSelectedEventId] = useState(""); diff --git a/src/app/our-mission/page.tsx b/src/app/our-mission/page.tsx index 7030f0b..a97e068 100644 --- a/src/app/our-mission/page.tsx +++ b/src/app/our-mission/page.tsx @@ -1,77 +1,53 @@ "use client"; const OurMission = () => { - return ( -
- {/* Overlay to Improve Text Readability */} -
+ return ( +
+ {/* Overlay to Improve Text Readability */} +
- {/* Content Area */} -
-

- Our Mission -

-

- At{" "} - - Earthquake Awareness Initiative - - , our mission is to help people worldwide prepare for and recover - from earthquakes. Through education, research, and innovative - technology, we work tirelessly to empower communities with the - knowledge they need to stay safe before, during, and after seismic - events. -

-

- We aim to bridge the gap between scientific research and community - awareness by providing resources, tools, and real-time updates for - earthquake preparedness. Together, we aspire to save lives, mitigate - impacts, and foster resilience against nature's powerful forces. -

-
-
- Education Icon -

Education

-

- Providing accessible resources to educate people about earthquake - preparedness. -

-
-
- Research Icon -

Research

-

- Supporting scientific studies to enhance understanding of seismic - activity. -

-
-
- Technology Icon -

Technology

-

- Leveraging innovation to deliver real-time alerts and safety - tools. -

-
-
-
-
- ); + {/* Content Area */} +
+

Our Mission

+

+ At Earthquake Awareness Initiative, our mission is to help people + worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work + tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events. +

+

+ We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and + real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster + resilience against nature's powerful forces. +

+
+
+ Education Icon +

Education

+

+ Providing accessible resources to educate people about earthquake preparedness. +

+
+
+ Research Icon +

Research

+

+ Supporting scientific studies to enhance understanding of seismic activity. +

+
+
+ Technology Icon +

Technology

+

+ Leveraging innovation to deliver real-time alerts and safety tools. +

+
+
+
+
+ ); return (
diff --git a/src/app/page.tsx b/src/app/page.tsx index 980b12e..490ba16 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,128 +1,126 @@ import Image from "next/image"; export default function Home() { - return ( -
-
-
- Background Image -
-
-
- Title Image -
-
+ return ( +
+
+
+ Background Image +
+
+
+ Title Image +
+
- +
+ {["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => ( +
+

{name}

+

+
+ ))} +
+

Spacer

+

Spacer

+
+ ); -
- {["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => ( -
-

{name}

-

-
- ))} -
-

Spacer

-

Spacer

-
- ); + // return ( + //
+ //
+ // Next.js logo + //
    + //
  1. + // Get started by editing{" "} + // + // src/app/page.tsx + // + // . + //
  2. + //
  3. Save and see your changes instantly.
  4. + //
- // return ( - //
- //
- // Next.js logo - //
    - //
  1. - // Get started by editing{" "} - // - // src/app/page.tsx - // - // . - //
  2. - //
  3. Save and see your changes instantly.
  4. - //
- - // - //
- // - //
- // ); + // + //
+ // + //
+ // ); } diff --git a/src/app/shop/page.tsx b/src/app/shop/page.tsx index 29a28fa..02cda97 100644 --- a/src/app/shop/page.tsx +++ b/src/app/shop/page.tsx @@ -1,11 +1,11 @@ "use client"; -import Image from 'next/image'; -import { Dispatch, SetStateAction, useCallback, useState } from 'react'; +import Image from "next/image"; +import { Dispatch, SetStateAction, useCallback, useState } from "react"; -import Artifact from '@appTypes/Artifact'; -import { Currency } from '@appTypes/StoreModel'; -import Sidebar from '@components/Sidebar'; -import { useStoreState } from '@hooks/store'; +import Artifact from "@appTypes/Artifact"; +import { Currency } from "@appTypes/StoreModel"; +import Sidebar from "@components/Sidebar"; +import { useStoreState } from "@hooks/store"; // Artifacts Data const artifacts: Artifact[] = [ diff --git a/src/app/the-team/page.tsx b/src/app/the-team/page.tsx index 35471e6..821662f 100644 --- a/src/app/the-team/page.tsx +++ b/src/app/the-team/page.tsx @@ -1,5 +1,5 @@ "use client"; // Required for components in Next.js (using client-side rendering) -import Image from 'next/image'; +import Image from "next/image"; const teamMembers = [ { diff --git a/src/app/user/page.tsx b/src/app/user/page.tsx index 908e803..0f5acc9 100644 --- a/src/app/user/page.tsx +++ b/src/app/user/page.tsx @@ -1,7 +1,7 @@ export default function User() { - return ( -
-

User

-
- ); + return ( +
+

User

+
+ ); } diff --git a/src/app/warehouse/page.tsx b/src/app/warehouse/page.tsx index 4d80506..67e0c65 100644 --- a/src/app/warehouse/page.tsx +++ b/src/app/warehouse/page.tsx @@ -1,6 +1,7 @@ "use client"; +import { useMemo, useState } from "react"; + import Sidebar from "@/components/Sidebar"; -import { useState, useMemo } from "react"; export default function Warehouse() { const [selectedEventId, setSelectedEventId] = useState(""); diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx index ac71c3c..645915c 100644 --- a/src/components/AuthModal.tsx +++ b/src/components/AuthModal.tsx @@ -1,6 +1,6 @@ "use client"; -import axios from 'axios'; -import { FormEvent, MouseEvent, useEffect, useRef, useState } from 'react'; +import axios from "axios"; +import { FormEvent, MouseEvent, useEffect, useRef, useState } from "react"; interface AuthModalProps { isOpen: boolean; // bool for if the modal should be visible diff --git a/src/components/map.tsx b/src/components/map.tsx index 9d2f7e8..80a2746 100644 --- a/src/components/map.tsx +++ b/src/components/map.tsx @@ -1,191 +1,200 @@ -import { useCallback, useState } from "react"; -import React, { useRef, useEffect, Dispatch, SetStateAction } from "react"; -import mapboxgl, { LngLatBounds } from "mapbox-gl"; import "mapbox-gl/dist/mapbox-gl.css"; -import { GiObservatory } from "react-icons/gi"; -import Event from "@appTypes/Event"; -import getMagnitudeColor from "@utils/getMagnitudeColour"; + +import mapboxgl, { LngLatBounds } from "mapbox-gl"; import { userAgentFromString } from "next/server"; +import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from "react"; import ReactDOM from "react-dom"; import { createRoot } from "react-dom/client"; +import { GiObservatory } from "react-icons/gi"; + +import Event from "@appTypes/Event"; +import getMagnitudeColor from "@utils/getMagnitudeColour"; interface MapComponentProps { - events: Event[]; - selectedEventId: Event["id"]; - setSelectedEventId: Dispatch>; - hoveredEventId: Event["id"]; - setHoveredEventId: Dispatch>; - mapType: String; + events: Event[]; + selectedEventId: Event["id"]; + setSelectedEventId: Dispatch>; + hoveredEventId: Event["id"]; + setHoveredEventId: Dispatch>; + mapType: String; } // Map component with location-style pulsing dots, animations, and tooltips -function MapComponent({ events, selectedEventId, setSelectedEventId, hoveredEventId, setHoveredEventId, mapType }: MapComponentProps) { - const map = useRef(null); - const markers = useRef<{ [key: string]: mapboxgl.Marker }>({}); - const [mapBounds, setMapBounds] = useState(); +function MapComponent({ + events, + selectedEventId, + setSelectedEventId, + hoveredEventId, + setHoveredEventId, + mapType, +}: MapComponentProps) { + const map = useRef(null); + const markers = useRef<{ [key: string]: mapboxgl.Marker }>({}); + const [mapBounds, setMapBounds] = useState(); - const fitToBounds = useCallback((bounds: LngLatBounds) => { - if (map.current && bounds) { - map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 }); - } - }, []); + const fitToBounds = useCallback((bounds: LngLatBounds) => { + if (map.current && bounds) { + map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 }); + } + }, []); - const showPopup = useCallback((eventId: string) => { - const marker = markers.current[eventId]; - if (marker && map.current) { - marker.getPopup()?.addTo(map.current); - } - }, []); + const showPopup = useCallback((eventId: string) => { + const marker = markers.current[eventId]; + if (marker && map.current) { + marker.getPopup()?.addTo(map.current); + } + }, []); - const clearAllPopups = useCallback(() => { - Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove()); - }, []); + const clearAllPopups = useCallback(() => { + Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove()); + }, []); - const flyToEvent = useCallback((event: Event) => { - if (map.current) { - map.current.flyTo({ - center: [event.longitude, event.latitude], - zoom: 4, - speed: 1.5, - curve: 1.42, - }); - } - }, []); + const flyToEvent = useCallback((event: Event) => { + if (map.current) { + map.current.flyTo({ + center: [event.longitude, event.latitude], + zoom: 4, + speed: 1.5, + curve: 1.42, + }); + } + }, []); - useEffect(() => { - mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ"; + useEffect(() => { + mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ"; - try { - map.current = new mapboxgl.Map({ - container: "map-container", - style: "mapbox://styles/mapbox/light-v10", - center: [0, 0], - zoom: 1, - }); - } catch (error) { - console.error("Map initialization failed:", error); - return; - } + try { + map.current = new mapboxgl.Map({ + container: "map-container", + style: "mapbox://styles/mapbox/light-v10", + center: [0, 0], + zoom: 1, + }); + } catch (error) { + console.error("Map initialization failed:", error); + return; + } - map.current.on("load", () => { - // Fit map to bounds - const bounds = new mapboxgl.LngLatBounds(); - events.forEach((event) => { - bounds.extend([event.longitude, event.latitude]); - }); - fitToBounds(bounds); - setMapBounds(bounds); + map.current.on("load", () => { + // Fit map to bounds + const bounds = new mapboxgl.LngLatBounds(); + events.forEach((event) => { + bounds.extend([event.longitude, event.latitude]); + }); + fitToBounds(bounds); + setMapBounds(bounds); - // Add markers with location pulse - events.forEach((event) => { + // Add markers with location pulse + events.forEach((event) => { + const quakeElement = document.createElement("div"); + const dotElement = document.createElement("div"); + const pulseElement = document.createElement("div"); - const quakeElement = document.createElement("div"); - const dotElement = document.createElement("div"); - const pulseElement = document.createElement("div"); + if (event.magnitude) { + const color = getMagnitudeColor(event.magnitude); - if (event.magnitude) { - const color = getMagnitudeColor(event.magnitude); + // Create marker container + quakeElement.style.width = "50px"; // Increased size to accommodate pulse + quakeElement.style.height = "50px"; + quakeElement.style.position = "absolute"; + quakeElement.style.display = "flex"; + quakeElement.style.alignItems = "center"; + quakeElement.style.justifyContent = "center"; - // Create marker container - quakeElement.style.width = "50px"; // Increased size to accommodate pulse - quakeElement.style.height = "50px"; - quakeElement.style.position = "absolute"; - quakeElement.style.display = "flex"; - quakeElement.style.alignItems = "center"; - quakeElement.style.justifyContent = "center"; + // Central dot + dotElement.style.width = "10px"; + dotElement.style.height = "10px"; + dotElement.style.backgroundColor = color; + dotElement.style.borderRadius = "50%"; + dotElement.style.position = "absolute"; + dotElement.style.zIndex = "2"; // Ensure dot is above pulse - // Central dot - dotElement.style.width = "10px"; - dotElement.style.height = "10px"; - dotElement.style.backgroundColor = color; - dotElement.style.borderRadius = "50%"; - dotElement.style.position = "absolute"; - dotElement.style.zIndex = "2"; // Ensure dot is above pulse + // Pulsing ring + pulseElement.className = "location-pulse"; + pulseElement.style.width = "20px"; // Initial size + pulseElement.style.height = "20px"; + pulseElement.style.backgroundColor = `${color}80`; // Color with 50% opacity (hex alpha) + pulseElement.style.borderRadius = "50%"; + pulseElement.style.position = "absolute"; + pulseElement.style.zIndex = "1"; + } - // Pulsing ring - pulseElement.className = "location-pulse"; - pulseElement.style.width = "20px"; // Initial size - pulseElement.style.height = "20px"; - pulseElement.style.backgroundColor = `${color}80`; // Color with 50% opacity (hex alpha) - pulseElement.style.borderRadius = "50%"; - pulseElement.style.position = "absolute"; - pulseElement.style.zIndex = "1"; - } + // Observatory marker + // + const observatoryElement = document.createElement("div"); + const root = createRoot(observatoryElement); // `createRoot` is now the standard API + root.render(); - // Observatory marker - // - const observatoryElement = document.createElement("div"); - const root = createRoot(observatoryElement); // `createRoot` is now the standard API - root.render( - - ); + quakeElement.appendChild(pulseElement); + quakeElement.appendChild(dotElement); - quakeElement.appendChild(pulseElement); - quakeElement.appendChild(dotElement); + const marker = new mapboxgl.Marker({ element: mapType === "observatories" ? observatoryElement : quakeElement }) + .setLngLat([event.longitude, event.latitude]) + .addTo(map.current!); - const marker = new mapboxgl.Marker({ element: mapType === "observatories" ? observatoryElement : quakeElement }).setLngLat([event.longitude, event.latitude]).addTo(map.current!); - - const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(` + const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(`

${event.title}

- ${ mapType === "observatories" ? `

${event.text1}

` : `

Magnitude: ${event.magnitude}

`} + ${mapType === "observatories" ? `

${event.text1}

` : `

Magnitude: ${event.magnitude}

`}

${event.text2}

`); - marker.setPopup(popup); - markers.current[event.id] = marker; + marker.setPopup(popup); + markers.current[event.id] = marker; - // Add hover events - const markerDomElement = marker.getElement(); - markerDomElement.style.cursor = "pointer"; // Optional: indicate interactivity + // Add hover events + const markerDomElement = marker.getElement(); + markerDomElement.style.cursor = "pointer"; // Optional: indicate interactivity - markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id)); - markerDomElement.addEventListener("mouseleave", () => setHoveredEventId("")); - markerDomElement.addEventListener("click", () => setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id))); + markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id)); + markerDomElement.addEventListener("mouseleave", () => setHoveredEventId("")); + markerDomElement.addEventListener("click", () => + setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id)) + ); - // Cleanup event listeners on unmount - markerDomElement.dataset.listenersAdded = "true"; // Mark for cleanup - }); - }); + // Cleanup event listeners on unmount + markerDomElement.dataset.listenersAdded = "true"; // Mark for cleanup + }); + }); - map.current.on("error", (e) => { - console.error("Mapbox error:", e); - }); + map.current.on("error", (e) => { + console.error("Mapbox error:", e); + }); - return () => { - map.current?.remove(); - }; - }, [events, setSelectedEventId, setHoveredEventId, fitToBounds]); + return () => { + map.current?.remove(); + }; + }, [events, setSelectedEventId, setHoveredEventId, fitToBounds]); - useEffect(() => { - const event = events.find((x) => x.id === selectedEventId); - if (event) flyToEvent(event); - else if (!selectedEventId) { - if (mapBounds) fitToBounds(mapBounds); - } - }, [events, selectedEventId, mapBounds, fitToBounds, clearAllPopups, flyToEvent]); + useEffect(() => { + const event = events.find((x) => x.id === selectedEventId); + if (event) flyToEvent(event); + else if (!selectedEventId) { + if (mapBounds) fitToBounds(mapBounds); + } + }, [events, selectedEventId, mapBounds, fitToBounds, clearAllPopups, flyToEvent]); - useEffect(() => { - // Clear all popups first - clearAllPopups(); + useEffect(() => { + // Clear all popups first + clearAllPopups(); - // Handle both events if they exist and are different - if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) { - showPopup(hoveredEventId); - showPopup(selectedEventId); - } - // Handle single event case (either hovered or selected) - else if (hoveredEventId || selectedEventId) { - showPopup(hoveredEventId || selectedEventId); - } - }, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]); + // Handle both events if they exist and are different + if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) { + showPopup(hoveredEventId); + showPopup(selectedEventId); + } + // Handle single event case (either hovered or selected) + else if (hoveredEventId || selectedEventId) { + showPopup(hoveredEventId || selectedEventId); + } + }, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]); - return ( -
-
-
- ); + return ( +
+
+
+ ); } // CSS for location-style pulsing animation @@ -211,10 +220,10 @@ const pulseStyles = ` `; export default function Map(props: MapComponentProps) { - return ( - <> - - - - ); -} \ No newline at end of file + return ( + <> + + + + ); +} diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx index 340dbc1..0de0eb8 100644 --- a/src/components/navbar.tsx +++ b/src/components/navbar.tsx @@ -1,13 +1,13 @@ "use client"; -import Image from 'next/image'; -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; -import { Dispatch, SetStateAction, useMemo, useState } from 'react'; -import { FaRegUserCircle } from 'react-icons/fa'; +import Image from "next/image"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { Dispatch, SetStateAction, useMemo, useState } from "react"; +import { FaRegUserCircle } from "react-icons/fa"; -import { Currency } from '@appTypes/StoreModel'; -import AuthModal from '@components/AuthModal'; -import { useStoreActions, useStoreState } from '@hooks/store'; +import { Currency } from "@appTypes/StoreModel"; +import AuthModal from "@components/AuthModal"; +import { useStoreActions, useStoreState } from "@hooks/store"; export default function Navbar({}: // currencySelector, { diff --git a/src/types/Event.ts b/src/types/Event.ts index 677b163..7f013b4 100644 --- a/src/types/Event.ts +++ b/src/types/Event.ts @@ -1,11 +1,11 @@ interface Event { - id: string; - title: string; - magnitude?: number; - longitude: number; - latitude: number; - text1: string; - text2: string; + id: string; + title: string; + magnitude?: number; + longitude: number; + latitude: number; + text1: string; + text2: string; } export default Event; diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts index a4ae79a..4185b5f 100644 --- a/src/utils/fetcher.ts +++ b/src/utils/fetcher.ts @@ -1,3 +1,3 @@ -import axios from 'axios'; +import axios from "axios"; export const fetcher = (url: string) => axios.get(url).then((res) => res.data); diff --git a/src/utils/getMagnitudeColour.tsx b/src/utils/getMagnitudeColour.tsx index 5a52eb3..677f7e5 100644 --- a/src/utils/getMagnitudeColour.tsx +++ b/src/utils/getMagnitudeColour.tsx @@ -1,20 +1,21 @@ "use client"; import resolveConfig from "tailwindcss/resolveConfig"; + import tailwindConfig from "../../tailwind.config"; function getMagnitudeColour(magnitude: number) { - const fullConfig = resolveConfig(tailwindConfig); - const colors = fullConfig.theme.colors; + const fullConfig = resolveConfig(tailwindConfig); + const colors = fullConfig.theme.colors; - if (magnitude >= 7) { - return colors["red"][600]; - } else if (magnitude >= 5) { - return colors["orange"][500]; - } else if (magnitude >= 3) { - return colors["amber"][500]; - } else { - return colors["blue"][400]; - } + if (magnitude >= 7) { + return colors["red"][600]; + } else if (magnitude >= 5) { + return colors["orange"][500]; + } else if (magnitude >= 3) { + return colors["amber"][500]; + } else { + return colors["blue"][400]; + } } export default getMagnitudeColour;