Created styling standard
This commit is contained in:
parent
92fbd9369c
commit
ec25bf0725
62
.vscode/settings.json
vendored
Normal file
62
.vscode/settings.json
vendored
Normal file
@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@ -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",
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
/* config options here */
|
||||
/* config options here */
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
2
package-lock.json
generated
2
package-lock.json
generated
@ -8524,4 +8524,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,4 +47,4 @@
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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<User[]> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export async function writeUserCsv(users: User[]): Promise<void> {
|
||||
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
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// 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
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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("");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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("");
|
||||
|
||||
@ -1,77 +1,53 @@
|
||||
"use client";
|
||||
|
||||
const OurMission = () => {
|
||||
return (
|
||||
<div
|
||||
className="h-screen relative bg-fixed bg-cover bg-center text-white py-10"
|
||||
style={{ backgroundImage: "url('destruction.jpg')" }}
|
||||
>
|
||||
{/* Overlay to Improve Text Readability */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
|
||||
return (
|
||||
<div
|
||||
className="h-screen relative bg-fixed bg-cover bg-center text-white py-10"
|
||||
style={{ backgroundImage: "url('destruction.jpg')" }}
|
||||
>
|
||||
{/* Overlay to Improve Text Readability */}
|
||||
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
|
||||
|
||||
{/* Content Area */}
|
||||
<div className="relative z-10 max-w-4xl mx-auto p-5 bg-white bg-opacity-90 shadow-lg rounded-lg">
|
||||
<h1 className="text-3xl font-bold text-center text-gray-800 mb-6">
|
||||
Our Mission
|
||||
</h1>
|
||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||
At{" "}
|
||||
<span className="font-semibold text-blue-600">
|
||||
Earthquake Awareness Initiative
|
||||
</span>
|
||||
, 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.
|
||||
</p>
|
||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||
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.
|
||||
</p>
|
||||
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img
|
||||
src="education.png"
|
||||
alt="Education Icon"
|
||||
className="h-20 w-20 mb-8"
|
||||
/>
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Education</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Providing accessible resources to educate people about earthquake
|
||||
preparedness.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img
|
||||
src="research.jpg"
|
||||
alt="Research Icon"
|
||||
className="h-20 w-20 mb-4"
|
||||
/>
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Research</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Supporting scientific studies to enhance understanding of seismic
|
||||
activity.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img
|
||||
src="tech.jpg"
|
||||
alt="Technology Icon"
|
||||
className="h-20 w-20 mb-8"
|
||||
/>
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Technology</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Leveraging innovation to deliver real-time alerts and safety
|
||||
tools.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
{/* Content Area */}
|
||||
<div className="relative z-10 max-w-4xl mx-auto p-5 bg-white bg-opacity-90 shadow-lg rounded-lg">
|
||||
<h1 className="text-3xl font-bold text-center text-gray-800 mb-6">Our Mission</h1>
|
||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||
At <span className="font-semibold text-blue-600">Earthquake Awareness Initiative</span>, 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.
|
||||
</p>
|
||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||
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.
|
||||
</p>
|
||||
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img src="education.png" alt="Education Icon" className="h-20 w-20 mb-8" />
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Education</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Providing accessible resources to educate people about earthquake preparedness.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img src="research.jpg" alt="Research Icon" className="h-20 w-20 mb-4" />
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Research</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Supporting scientific studies to enhance understanding of seismic activity.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-center p-10">
|
||||
<img src="tech.jpg" alt="Technology Icon" className="h-20 w-20 mb-8" />
|
||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Technology</h3>
|
||||
<p className="text-sm text-gray-500 text-center">
|
||||
Leveraging innovation to deliver real-time alerts and safety tools.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="min-h-screen bg-neutral-100 flex flex-col items-center justify-center py-10">
|
||||
<div className="max-w-4xl mx-auto p-5 bg-white shadow-lg rounded-lg">
|
||||
|
||||
240
src/app/page.tsx
240
src/app/page.tsx
@ -1,128 +1,126 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="min-h-screen text-black">
|
||||
<div className="w-full relative overflow-hidden">
|
||||
<div className="">
|
||||
<Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image>
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/10 to-black/40"></div>
|
||||
<div className="absolute inset-0 top-[30%]">
|
||||
<Image className="mx-auto" height={300} width={1000} alt="Title Image" src="/tremortrackertext.png"></Image>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<main className="min-h-screen text-black">
|
||||
<div className="w-full relative overflow-hidden">
|
||||
<div className="">
|
||||
<Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image>
|
||||
</div>
|
||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/10 to-black/40"></div>
|
||||
<div className="absolute inset-0 top-[30%]">
|
||||
<Image className="mx-auto" height={300} width={1000} alt="Title Image" src="/tremortrackertext.png"></Image>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="mx-auto mt-20 w-4/6 px-2 border border-black divide-y">
|
||||
{["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => (
|
||||
<div className="px-5 py-5" key={name}>
|
||||
<p className="ml-3">{name}</p>
|
||||
<p></p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-96">Spacer</p>
|
||||
<p className="mt-96">Spacer</p>
|
||||
</main>
|
||||
);
|
||||
|
||||
<div className="mx-auto mt-20 w-4/6 px-2 border border-black divide-y">
|
||||
{["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => (
|
||||
<div className="px-5 py-5" key={name}>
|
||||
<p className="ml-3">{name}</p>
|
||||
<p></p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-96">Spacer</p>
|
||||
<p className="mt-96">Spacer</p>
|
||||
</main>
|
||||
);
|
||||
// return (
|
||||
// <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
// <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
// <Image
|
||||
// className="dark:invert"
|
||||
// src="/next.svg"
|
||||
// alt="Next.js logo"
|
||||
// width={180}
|
||||
// height={38}
|
||||
// priority
|
||||
// />
|
||||
// <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
// <li className="mb-2">
|
||||
// Get started by editing{" "}
|
||||
// <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
// src/app/page.tsx
|
||||
// </code>
|
||||
// .
|
||||
// </li>
|
||||
// <li>Save and see your changes instantly.</li>
|
||||
// </ol>
|
||||
|
||||
// return (
|
||||
// <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
// <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
// <Image
|
||||
// className="dark:invert"
|
||||
// src="/next.svg"
|
||||
// alt="Next.js logo"
|
||||
// width={180}
|
||||
// height={38}
|
||||
// priority
|
||||
// />
|
||||
// <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
// <li className="mb-2">
|
||||
// Get started by editing{" "}
|
||||
// <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
// src/app/page.tsx
|
||||
// </code>
|
||||
// .
|
||||
// </li>
|
||||
// <li>Save and see your changes instantly.</li>
|
||||
// </ol>
|
||||
|
||||
// <div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
// <a
|
||||
// className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// className="dark:invert"
|
||||
// src="/vercel.svg"
|
||||
// alt="Vercel logomark"
|
||||
// width={20}
|
||||
// height={20}
|
||||
// />
|
||||
// Deploy now
|
||||
// </a>
|
||||
// <a
|
||||
// className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// Read our docs
|
||||
// </a>
|
||||
// </div>
|
||||
// </main>
|
||||
// <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/file.svg"
|
||||
// alt="File icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Learn
|
||||
// </a>
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/window.svg"
|
||||
// alt="Window icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Examples
|
||||
// </a>
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/globe.svg"
|
||||
// alt="Globe icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Go to nextjs.org →
|
||||
// </a>
|
||||
// </footer>
|
||||
// </div>
|
||||
// );
|
||||
// <div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
// <a
|
||||
// className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// className="dark:invert"
|
||||
// src="/vercel.svg"
|
||||
// alt="Vercel logomark"
|
||||
// width={20}
|
||||
// height={20}
|
||||
// />
|
||||
// Deploy now
|
||||
// </a>
|
||||
// <a
|
||||
// className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// Read our docs
|
||||
// </a>
|
||||
// </div>
|
||||
// </main>
|
||||
// <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/file.svg"
|
||||
// alt="File icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Learn
|
||||
// </a>
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/window.svg"
|
||||
// alt="Window icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Examples
|
||||
// </a>
|
||||
// <a
|
||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
// href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
// target="_blank"
|
||||
// rel="noopener noreferrer"
|
||||
// >
|
||||
// <Image
|
||||
// aria-hidden
|
||||
// src="/globe.svg"
|
||||
// alt="Globe icon"
|
||||
// width={16}
|
||||
// height={16}
|
||||
// />
|
||||
// Go to nextjs.org →
|
||||
// </a>
|
||||
// </footer>
|
||||
// </div>
|
||||
// );
|
||||
}
|
||||
|
||||
@ -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[] = [
|
||||
|
||||
@ -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 = [
|
||||
{
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
export default function User() {
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<p>User</p>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<p>User</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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("");
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<SetStateAction<string>>;
|
||||
hoveredEventId: Event["id"];
|
||||
setHoveredEventId: Dispatch<SetStateAction<string>>;
|
||||
mapType: String;
|
||||
events: Event[];
|
||||
selectedEventId: Event["id"];
|
||||
setSelectedEventId: Dispatch<SetStateAction<string>>;
|
||||
hoveredEventId: Event["id"];
|
||||
setHoveredEventId: Dispatch<SetStateAction<string>>;
|
||||
mapType: String;
|
||||
}
|
||||
|
||||
// Map component with location-style pulsing dots, animations, and tooltips
|
||||
function MapComponent({ events, selectedEventId, setSelectedEventId, hoveredEventId, setHoveredEventId, mapType }: MapComponentProps) {
|
||||
const map = useRef<mapboxgl.Map | null>(null);
|
||||
const markers = useRef<{ [key: string]: mapboxgl.Marker }>({});
|
||||
const [mapBounds, setMapBounds] = useState<LngLatBounds>();
|
||||
function MapComponent({
|
||||
events,
|
||||
selectedEventId,
|
||||
setSelectedEventId,
|
||||
hoveredEventId,
|
||||
setHoveredEventId,
|
||||
mapType,
|
||||
}: MapComponentProps) {
|
||||
const map = useRef<mapboxgl.Map | null>(null);
|
||||
const markers = useRef<{ [key: string]: mapboxgl.Marker }>({});
|
||||
const [mapBounds, setMapBounds] = useState<LngLatBounds>();
|
||||
|
||||
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
|
||||
//<GiObservatory />
|
||||
const observatoryElement = document.createElement("div");
|
||||
const root = createRoot(observatoryElement); // `createRoot` is now the standard API
|
||||
root.render(<GiObservatory className="text-blue-600 text-2xl drop-shadow-lg" />);
|
||||
|
||||
// Observatory marker
|
||||
//<GiObservatory />
|
||||
const observatoryElement = document.createElement("div");
|
||||
const root = createRoot(observatoryElement); // `createRoot` is now the standard API
|
||||
root.render(
|
||||
<GiObservatory className="text-blue-600 text-2xl drop-shadow-lg" />
|
||||
);
|
||||
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(`
|
||||
<div>
|
||||
<h3>${event.title}</h3>
|
||||
${ 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>
|
||||
</div>
|
||||
`);
|
||||
|
||||
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 (
|
||||
<div className="w-full h-full">
|
||||
<div id="map-container" className="w-full h-full" />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<div id="map-container" className="w-full h-full" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// CSS for location-style pulsing animation
|
||||
@ -211,10 +220,10 @@ const pulseStyles = `
|
||||
`;
|
||||
|
||||
export default function Map(props: MapComponentProps) {
|
||||
return (
|
||||
<>
|
||||
<style>{pulseStyles}</style>
|
||||
<MapComponent {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<style>{pulseStyles}</style>
|
||||
<MapComponent {...props} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
import axios from 'axios';
|
||||
import axios from "axios";
|
||||
|
||||
export const fetcher = (url: string) => axios.get(url).then((res) => res.data);
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user