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 __dirname = dirname(__filename);
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
const compat = new FlatCompat({
|
||||||
baseDirectory: __dirname,
|
baseDirectory: __dirname,
|
||||||
});
|
});
|
||||||
|
|
||||||
const eslintConfig = [
|
export default tseslint.config([...compat.extends("next/core-web-vitals", "next/typescript")], {
|
||||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
rules: {
|
||||||
];
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
export default eslintConfig;
|
},
|
||||||
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
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",
|
"tailwindcss": "^3.4.1",
|
||||||
"typescript": "^5"
|
"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;
|
const usingPrisma = false;
|
||||||
let prisma: PrismaClient;
|
let prisma: PrismaClient;
|
||||||
|
|||||||
@ -1,69 +1,69 @@
|
|||||||
import path from "path";
|
|
||||||
import fs from "fs";
|
|
||||||
import csv from "csv-parser";
|
import csv from "csv-parser";
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
export type User = {
|
export type User = {
|
||||||
name: string;
|
name: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
accessLevel: string;
|
accessLevel: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {array} - Array of Objects containing user data
|
* @returns {array} - Array of Objects containing user data
|
||||||
*/
|
*/
|
||||||
export async function readUserCsv(): Promise<User[]> {
|
export async function readUserCsv(): Promise<User[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Dynamic CSV location generation
|
// Dynamic CSV location generation
|
||||||
let csvPath = path.dirname(__dirname); // /login
|
let csvPath = path.dirname(__dirname); // /login
|
||||||
csvPath = path.dirname(csvPath); // /api
|
csvPath = path.dirname(csvPath); // /api
|
||||||
csvPath = path.dirname(csvPath); // /app
|
csvPath = path.dirname(csvPath); // /app
|
||||||
csvPath = path.dirname(csvPath); // /src
|
csvPath = path.dirname(csvPath); // /src
|
||||||
csvPath = path.dirname(csvPath); // /[project]
|
csvPath = path.dirname(csvPath); // /[project]
|
||||||
csvPath = path.dirname(csvPath); // /termor-tracker
|
csvPath = path.dirname(csvPath); // /termor-tracker
|
||||||
csvPath = path.join(csvPath, "src", "databases", "Users.csv");
|
csvPath = path.join(csvPath, "src", "databases", "Users.csv");
|
||||||
|
|
||||||
// Forms array for user data
|
// Forms array for user data
|
||||||
let results: User[] = [];
|
let results: User[] = [];
|
||||||
// Reads data and adds it to results
|
// Reads data and adds it to results
|
||||||
fs.createReadStream(csvPath)
|
fs.createReadStream(csvPath)
|
||||||
.pipe(csv())
|
.pipe(csv())
|
||||||
.on("data", (data) => results.push(data))
|
.on("data", (data) => results.push(data))
|
||||||
.on("end", () => {
|
.on("end", () => {
|
||||||
resolve(results);
|
resolve(results);
|
||||||
})
|
})
|
||||||
.on("error", (error) => {
|
.on("error", (error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findUserByEmail(users: User[], email: string): User | undefined {
|
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> {
|
export async function passwordStrengthCheck(password: string): Promise<string> {
|
||||||
if (password.length < 8) {
|
if (password.length < 8) {
|
||||||
return "short";
|
return "short";
|
||||||
} else if (password.length > 16) {
|
} else if (password.length > 16) {
|
||||||
return "long";
|
return "long";
|
||||||
}
|
}
|
||||||
const lowercaseRegex = /[a-z]/;
|
const lowercaseRegex = /[a-z]/;
|
||||||
const uppercaseRegex = /[A-Z]/;
|
const uppercaseRegex = /[A-Z]/;
|
||||||
const digitRegex = /\d/;
|
const digitRegex = /\d/;
|
||||||
const specialCharRegex = /[!@#$%^&*]/;
|
const specialCharRegex = /[!@#$%^&*]/;
|
||||||
if (!lowercaseRegex.test(password)) {
|
if (!lowercaseRegex.test(password)) {
|
||||||
return "no lower";
|
return "no lower";
|
||||||
} else if (!uppercaseRegex.test(password)) {
|
} else if (!uppercaseRegex.test(password)) {
|
||||||
return "no upper";
|
return "no upper";
|
||||||
} else if (!digitRegex.test(password)) {
|
} else if (!digitRegex.test(password)) {
|
||||||
return "no digit";
|
return "no digit";
|
||||||
} else if (!specialCharRegex.test(password)) {
|
} else if (!specialCharRegex.test(password)) {
|
||||||
return "no special";
|
return "no special";
|
||||||
} else {
|
} else {
|
||||||
return "secure";
|
return "secure";
|
||||||
}
|
}
|
||||||
return "end of function";
|
return "end of function";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,28 +72,28 @@ export async function passwordStrengthCheck(password: string): Promise<string> {
|
|||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
export async function writeUserCsv(users: User[]): Promise<void> {
|
export async function writeUserCsv(users: User[]): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// Dynamic CSV location generation
|
// Dynamic CSV location generation
|
||||||
let csvPath = path.dirname(__dirname); // /login
|
let csvPath = path.dirname(__dirname); // /login
|
||||||
csvPath = path.dirname(csvPath); // /api
|
csvPath = path.dirname(csvPath); // /api
|
||||||
csvPath = path.dirname(csvPath); // /app
|
csvPath = path.dirname(csvPath); // /app
|
||||||
csvPath = path.dirname(csvPath); // /src
|
csvPath = path.dirname(csvPath); // /src
|
||||||
csvPath = path.dirname(csvPath); // /[project]
|
csvPath = path.dirname(csvPath); // /[project]
|
||||||
csvPath = path.dirname(csvPath); // /termor-tracker
|
csvPath = path.dirname(csvPath); // /termor-tracker
|
||||||
csvPath = path.join(csvPath, "src", "databases", "Users.csv");
|
csvPath = path.join(csvPath, "src", "databases", "Users.csv");
|
||||||
|
|
||||||
// Prepare CSV data as a string
|
// Prepare CSV data as a string
|
||||||
const headers = "name,email,password,level"; // CSV headers
|
const headers = "name,email,password,level"; // CSV headers
|
||||||
const rows = users.map((user) => `${user.name},${user.email},${user.password},${user.accessLevel}`);
|
const rows = users.map((user) => `${user.name},${user.email},${user.password},${user.accessLevel}`);
|
||||||
const csvData = `${headers}\n${rows.join("\n")}`;
|
const csvData = `${headers}\n${rows.join("\n")}`;
|
||||||
|
|
||||||
// Write data to the file
|
// Write data to the file
|
||||||
fs.writeFile(csvPath, csvData, (error) => {
|
fs.writeFile(csvPath, csvData, (error) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error); // Reject promise on error
|
reject(error); // Reject promise on error
|
||||||
} else {
|
} else {
|
||||||
resolve(); // Resolve the promise if successful
|
resolve(); // Resolve the promise if successful
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
import { NextResponse } from 'next/server';
|
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;
|
const usingPrisma = false;
|
||||||
let prisma: PrismaClient;
|
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;
|
const usingPrisma = false;
|
||||||
let prisma: PrismaClient;
|
let prisma: PrismaClient;
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import bcrypt from 'bcrypt';
|
import bcrypt from "bcrypt";
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
import { PrismaClient } from '@prisma/client';
|
import { PrismaClient } from "@prisma/client";
|
||||||
|
|
||||||
import {
|
import { findUserByEmail, passwordStrengthCheck, readUserCsv, User, writeUserCsv } from "../functions/csvReadWrite";
|
||||||
findUserByEmail, passwordStrengthCheck, readUserCsv, User, writeUserCsv
|
|
||||||
} from '../functions/csvReadWrite';
|
|
||||||
|
|
||||||
const usingPrisma = false;
|
const usingPrisma = false;
|
||||||
let prisma: PrismaClient;
|
let prisma: PrismaClient;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
|
|
||||||
const ContactUs = () => {
|
const ContactUs = () => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from "react";
|
||||||
import useSWR from 'swr';
|
import useSWR from "swr";
|
||||||
|
|
||||||
import Map from '@components/Map';
|
import Map from "@components/Map";
|
||||||
import Sidebar from '@components/Sidebar';
|
import Sidebar from "@components/Sidebar";
|
||||||
import { fetcher } from '@utils/fetcher';
|
import { fetcher } from "@utils/fetcher";
|
||||||
|
|
||||||
export default function Earthquakes() {
|
export default function Earthquakes() {
|
||||||
const [selectedEventId, setSelectedEventId] = useState("");
|
const [selectedEventId, setSelectedEventId] = useState("");
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @media (prefers-color-scheme: dark) {
|
/* @media (prefers-color-scheme: dark) {
|
||||||
@ -15,29 +15,29 @@
|
|||||||
} */
|
} */
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--foreground);
|
color: var(--foreground);
|
||||||
background: var(--background);
|
background: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Increase specificity and use !important where necessary */
|
/* Increase specificity and use !important where necessary */
|
||||||
.mapboxgl-popup .mapboxgl-popup-content {
|
.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 */
|
/* Hide the popup tip */
|
||||||
.mapboxgl-popup .mapboxgl-popup-tip {
|
.mapboxgl-popup .mapboxgl-popup-tip {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Child elements */
|
/* Child elements */
|
||||||
.mapboxgl-popup-content h3 {
|
.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 {
|
.mapboxgl-popup-content p {
|
||||||
@apply text-xs text-neutral-600 !important;
|
@apply text-xs text-neutral-600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-popup-content p + p {
|
.mapboxgl-popup-content p + p {
|
||||||
@apply text-neutral-500 !important;
|
@apply text-neutral-500 !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from "react";
|
||||||
import useSWR from 'swr';
|
import useSWR from "swr";
|
||||||
|
|
||||||
import Sidebar from '@/components/Sidebar';
|
import Sidebar from "@/components/Sidebar";
|
||||||
import Map from '@components/Map';
|
import Map from "@components/Map";
|
||||||
import { fetcher } from '@utils/fetcher';
|
import { fetcher } from "@utils/fetcher";
|
||||||
|
|
||||||
export default function Observatories() {
|
export default function Observatories() {
|
||||||
const [selectedEventId, setSelectedEventId] = useState("");
|
const [selectedEventId, setSelectedEventId] = useState("");
|
||||||
|
|||||||
@ -1,77 +1,53 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
const OurMission = () => {
|
const OurMission = () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="h-screen relative bg-fixed bg-cover bg-center text-white py-10"
|
className="h-screen relative bg-fixed bg-cover bg-center text-white py-10"
|
||||||
style={{ backgroundImage: "url('destruction.jpg')" }}
|
style={{ backgroundImage: "url('destruction.jpg')" }}
|
||||||
>
|
>
|
||||||
{/* Overlay to Improve Text Readability */}
|
{/* Overlay to Improve Text Readability */}
|
||||||
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
|
<div className="absolute inset-0 bg-black bg-opacity-40"></div>
|
||||||
|
|
||||||
{/* Content Area */}
|
{/* Content Area */}
|
||||||
<div className="relative z-10 max-w-4xl mx-auto p-5 bg-white bg-opacity-90 shadow-lg rounded-lg">
|
<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">
|
<h1 className="text-3xl font-bold text-center text-gray-800 mb-6">Our Mission</h1>
|
||||||
Our Mission
|
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||||
</h1>
|
At <span className="font-semibold text-blue-600">Earthquake Awareness Initiative</span>, our mission is to help people
|
||||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work
|
||||||
At{" "}
|
tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events.
|
||||||
<span className="font-semibold text-blue-600">
|
</p>
|
||||||
Earthquake Awareness Initiative
|
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
||||||
</span>
|
We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and
|
||||||
, our mission is to help people worldwide prepare for and recover
|
real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster
|
||||||
from earthquakes. Through education, research, and innovative
|
resilience against nature's powerful forces.
|
||||||
technology, we work tirelessly to empower communities with the
|
</p>
|
||||||
knowledge they need to stay safe before, during, and after seismic
|
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
||||||
events.
|
<div className="flex flex-col items-center p-10">
|
||||||
</p>
|
<img src="education.png" alt="Education Icon" className="h-20 w-20 mb-8" />
|
||||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
<h3 className="text-xl font-bold text-gray-700 mb-2">Education</h3>
|
||||||
We aim to bridge the gap between scientific research and community
|
<p className="text-sm text-gray-500 text-center">
|
||||||
awareness by providing resources, tools, and real-time updates for
|
Providing accessible resources to educate people about earthquake preparedness.
|
||||||
earthquake preparedness. Together, we aspire to save lives, mitigate
|
</p>
|
||||||
impacts, and foster resilience against nature's powerful forces.
|
</div>
|
||||||
</p>
|
<div className="flex flex-col items-center p-10">
|
||||||
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
<img src="research.jpg" alt="Research Icon" className="h-20 w-20 mb-4" />
|
||||||
<div className="flex flex-col items-center p-10">
|
<h3 className="text-xl font-bold text-gray-700 mb-2">Research</h3>
|
||||||
<img
|
<p className="text-sm text-gray-500 text-center">
|
||||||
src="education.png"
|
Supporting scientific studies to enhance understanding of seismic activity.
|
||||||
alt="Education Icon"
|
</p>
|
||||||
className="h-20 w-20 mb-8"
|
</div>
|
||||||
/>
|
<div className="flex flex-col items-center p-10">
|
||||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Education</h3>
|
<img src="tech.jpg" alt="Technology Icon" className="h-20 w-20 mb-8" />
|
||||||
<p className="text-sm text-gray-500 text-center">
|
<h3 className="text-xl font-bold text-gray-700 mb-2">Technology</h3>
|
||||||
Providing accessible resources to educate people about earthquake
|
<p className="text-sm text-gray-500 text-center">
|
||||||
preparedness.
|
Leveraging innovation to deliver real-time alerts and safety tools.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center p-10">
|
</div>
|
||||||
<img
|
</div>
|
||||||
src="research.jpg"
|
</div>
|
||||||
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 (
|
return (
|
||||||
<div className="min-h-screen bg-neutral-100 flex flex-col items-center justify-center py-10">
|
<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">
|
<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";
|
import Image from "next/image";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen text-black">
|
<main className="min-h-screen text-black">
|
||||||
<div className="w-full relative overflow-hidden">
|
<div className="w-full relative overflow-hidden">
|
||||||
<div className="">
|
<div className="">
|
||||||
<Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image>
|
<Image height={2000} width={2000} alt="Background Image" src="/lava_flows.jpg"></Image>
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/10 to-black/40"></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%]">
|
<div className="absolute inset-0 top-[30%]">
|
||||||
<Image className="mx-auto" height={300} width={1000} alt="Title Image" src="/tremortrackertext.png"></Image>
|
<Image className="mx-auto" height={300} width={1000} alt="Title Image" src="/tremortrackertext.png"></Image>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
// return (
|
||||||
{["Earthquake 1", "Earthquake 2", "Earthquake 3"].map((name) => (
|
// <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)]">
|
||||||
<div className="px-5 py-5" key={name}>
|
// <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||||
<p className="ml-3">{name}</p>
|
// <Image
|
||||||
<p></p>
|
// className="dark:invert"
|
||||||
</div>
|
// src="/next.svg"
|
||||||
))}
|
// alt="Next.js logo"
|
||||||
</div>
|
// width={180}
|
||||||
<p className="mt-96">Spacer</p>
|
// height={38}
|
||||||
<p className="mt-96">Spacer</p>
|
// priority
|
||||||
</main>
|
// />
|
||||||
);
|
// <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="flex gap-4 items-center flex-col sm:flex-row">
|
||||||
// <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)]">
|
// <a
|
||||||
// <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
// 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"
|
||||||
// <Image
|
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||||
// className="dark:invert"
|
// target="_blank"
|
||||||
// src="/next.svg"
|
// rel="noopener noreferrer"
|
||||||
// alt="Next.js logo"
|
// >
|
||||||
// width={180}
|
// <Image
|
||||||
// height={38}
|
// className="dark:invert"
|
||||||
// priority
|
// src="/vercel.svg"
|
||||||
// />
|
// alt="Vercel logomark"
|
||||||
// <ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
// width={20}
|
||||||
// <li className="mb-2">
|
// height={20}
|
||||||
// Get started by editing{" "}
|
// />
|
||||||
// <code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
// Deploy now
|
||||||
// src/app/page.tsx
|
// </a>
|
||||||
// </code>
|
// <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"
|
||||||
// </li>
|
// href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||||
// <li>Save and see your changes instantly.</li>
|
// target="_blank"
|
||||||
// </ol>
|
// rel="noopener noreferrer"
|
||||||
|
// >
|
||||||
// <div className="flex gap-4 items-center flex-col sm:flex-row">
|
// Read our docs
|
||||||
// <a
|
// </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"
|
// </div>
|
||||||
// href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
// </main>
|
||||||
// target="_blank"
|
// <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||||
// rel="noopener noreferrer"
|
// <a
|
||||||
// >
|
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||||
// <Image
|
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||||
// className="dark:invert"
|
// target="_blank"
|
||||||
// src="/vercel.svg"
|
// rel="noopener noreferrer"
|
||||||
// alt="Vercel logomark"
|
// >
|
||||||
// width={20}
|
// <Image
|
||||||
// height={20}
|
// aria-hidden
|
||||||
// />
|
// src="/file.svg"
|
||||||
// Deploy now
|
// alt="File icon"
|
||||||
// </a>
|
// width={16}
|
||||||
// <a
|
// height={16}
|
||||||
// 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"
|
// Learn
|
||||||
// target="_blank"
|
// </a>
|
||||||
// rel="noopener noreferrer"
|
// <a
|
||||||
// >
|
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||||
// Read our docs
|
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||||
// </a>
|
// target="_blank"
|
||||||
// </div>
|
// rel="noopener noreferrer"
|
||||||
// </main>
|
// >
|
||||||
// <footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
// <Image
|
||||||
// <a
|
// aria-hidden
|
||||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
// src="/window.svg"
|
||||||
// href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
// alt="Window icon"
|
||||||
// target="_blank"
|
// width={16}
|
||||||
// rel="noopener noreferrer"
|
// height={16}
|
||||||
// >
|
// />
|
||||||
// <Image
|
// Examples
|
||||||
// aria-hidden
|
// </a>
|
||||||
// src="/file.svg"
|
// <a
|
||||||
// alt="File icon"
|
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||||
// width={16}
|
// href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||||
// height={16}
|
// target="_blank"
|
||||||
// />
|
// rel="noopener noreferrer"
|
||||||
// Learn
|
// >
|
||||||
// </a>
|
// <Image
|
||||||
// <a
|
// aria-hidden
|
||||||
// className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
// src="/globe.svg"
|
||||||
// href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
// alt="Globe icon"
|
||||||
// target="_blank"
|
// width={16}
|
||||||
// rel="noopener noreferrer"
|
// height={16}
|
||||||
// >
|
// />
|
||||||
// <Image
|
// Go to nextjs.org →
|
||||||
// aria-hidden
|
// </a>
|
||||||
// src="/window.svg"
|
// </footer>
|
||||||
// alt="Window icon"
|
// </div>
|
||||||
// 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";
|
"use client";
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
import { Dispatch, SetStateAction, useCallback, useState } from 'react';
|
import { Dispatch, SetStateAction, useCallback, useState } from "react";
|
||||||
|
|
||||||
import Artifact from '@appTypes/Artifact';
|
import Artifact from "@appTypes/Artifact";
|
||||||
import { Currency } from '@appTypes/StoreModel';
|
import { Currency } from "@appTypes/StoreModel";
|
||||||
import Sidebar from '@components/Sidebar';
|
import Sidebar from "@components/Sidebar";
|
||||||
import { useStoreState } from '@hooks/store';
|
import { useStoreState } from "@hooks/store";
|
||||||
|
|
||||||
// Artifacts Data
|
// Artifacts Data
|
||||||
const artifacts: Artifact[] = [
|
const artifacts: Artifact[] = [
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"use client"; // Required for components in Next.js (using client-side rendering)
|
"use client"; // Required for components in Next.js (using client-side rendering)
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
|
|
||||||
const teamMembers = [
|
const teamMembers = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
export default function User() {
|
export default function User() {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<p>User</p>
|
<p>User</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
import { useState, useMemo } from "react";
|
|
||||||
|
|
||||||
export default function Warehouse() {
|
export default function Warehouse() {
|
||||||
const [selectedEventId, setSelectedEventId] = useState("");
|
const [selectedEventId, setSelectedEventId] = useState("");
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import { FormEvent, MouseEvent, useEffect, useRef, useState } from 'react';
|
import { FormEvent, MouseEvent, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
interface AuthModalProps {
|
interface AuthModalProps {
|
||||||
isOpen: boolean; // bool for if the modal should be visible
|
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 "mapbox-gl/dist/mapbox-gl.css";
|
||||||
import { GiObservatory } from "react-icons/gi";
|
|
||||||
import Event from "@appTypes/Event";
|
import mapboxgl, { LngLatBounds } from "mapbox-gl";
|
||||||
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
|
||||||
import { userAgentFromString } from "next/server";
|
import { userAgentFromString } from "next/server";
|
||||||
|
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
|
import { GiObservatory } from "react-icons/gi";
|
||||||
|
|
||||||
|
import Event from "@appTypes/Event";
|
||||||
|
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
||||||
|
|
||||||
interface MapComponentProps {
|
interface MapComponentProps {
|
||||||
events: Event[];
|
events: Event[];
|
||||||
selectedEventId: Event["id"];
|
selectedEventId: Event["id"];
|
||||||
setSelectedEventId: Dispatch<SetStateAction<string>>;
|
setSelectedEventId: Dispatch<SetStateAction<string>>;
|
||||||
hoveredEventId: Event["id"];
|
hoveredEventId: Event["id"];
|
||||||
setHoveredEventId: Dispatch<SetStateAction<string>>;
|
setHoveredEventId: Dispatch<SetStateAction<string>>;
|
||||||
mapType: String;
|
mapType: String;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map component with location-style pulsing dots, animations, and tooltips
|
// Map component with location-style pulsing dots, animations, and tooltips
|
||||||
function MapComponent({ events, selectedEventId, setSelectedEventId, hoveredEventId, setHoveredEventId, mapType }: MapComponentProps) {
|
function MapComponent({
|
||||||
const map = useRef<mapboxgl.Map | null>(null);
|
events,
|
||||||
const markers = useRef<{ [key: string]: mapboxgl.Marker }>({});
|
selectedEventId,
|
||||||
const [mapBounds, setMapBounds] = useState<LngLatBounds>();
|
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) => {
|
const fitToBounds = useCallback((bounds: LngLatBounds) => {
|
||||||
if (map.current && bounds) {
|
if (map.current && bounds) {
|
||||||
map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 });
|
map.current!.fitBounds(bounds, { padding: 150, maxZoom: 10 });
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const showPopup = useCallback((eventId: string) => {
|
const showPopup = useCallback((eventId: string) => {
|
||||||
const marker = markers.current[eventId];
|
const marker = markers.current[eventId];
|
||||||
if (marker && map.current) {
|
if (marker && map.current) {
|
||||||
marker.getPopup()?.addTo(map.current);
|
marker.getPopup()?.addTo(map.current);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const clearAllPopups = useCallback(() => {
|
const clearAllPopups = useCallback(() => {
|
||||||
Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove());
|
Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const flyToEvent = useCallback((event: Event) => {
|
const flyToEvent = useCallback((event: Event) => {
|
||||||
if (map.current) {
|
if (map.current) {
|
||||||
map.current.flyTo({
|
map.current.flyTo({
|
||||||
center: [event.longitude, event.latitude],
|
center: [event.longitude, event.latitude],
|
||||||
zoom: 4,
|
zoom: 4,
|
||||||
speed: 1.5,
|
speed: 1.5,
|
||||||
curve: 1.42,
|
curve: 1.42,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ";
|
mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
map.current = new mapboxgl.Map({
|
map.current = new mapboxgl.Map({
|
||||||
container: "map-container",
|
container: "map-container",
|
||||||
style: "mapbox://styles/mapbox/light-v10",
|
style: "mapbox://styles/mapbox/light-v10",
|
||||||
center: [0, 0],
|
center: [0, 0],
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Map initialization failed:", error);
|
console.error("Map initialization failed:", error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
map.current.on("load", () => {
|
map.current.on("load", () => {
|
||||||
// Fit map to bounds
|
// Fit map to bounds
|
||||||
const bounds = new mapboxgl.LngLatBounds();
|
const bounds = new mapboxgl.LngLatBounds();
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
bounds.extend([event.longitude, event.latitude]);
|
bounds.extend([event.longitude, event.latitude]);
|
||||||
});
|
});
|
||||||
fitToBounds(bounds);
|
fitToBounds(bounds);
|
||||||
setMapBounds(bounds);
|
setMapBounds(bounds);
|
||||||
|
|
||||||
// Add markers with location pulse
|
// Add markers with location pulse
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
|
const quakeElement = document.createElement("div");
|
||||||
|
const dotElement = document.createElement("div");
|
||||||
|
const pulseElement = document.createElement("div");
|
||||||
|
|
||||||
const quakeElement = document.createElement("div");
|
if (event.magnitude) {
|
||||||
const dotElement = document.createElement("div");
|
const color = getMagnitudeColor(event.magnitude);
|
||||||
const pulseElement = document.createElement("div");
|
|
||||||
|
|
||||||
if (event.magnitude) {
|
// Create marker container
|
||||||
const color = getMagnitudeColor(event.magnitude);
|
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
|
// Central dot
|
||||||
quakeElement.style.width = "50px"; // Increased size to accommodate pulse
|
dotElement.style.width = "10px";
|
||||||
quakeElement.style.height = "50px";
|
dotElement.style.height = "10px";
|
||||||
quakeElement.style.position = "absolute";
|
dotElement.style.backgroundColor = color;
|
||||||
quakeElement.style.display = "flex";
|
dotElement.style.borderRadius = "50%";
|
||||||
quakeElement.style.alignItems = "center";
|
dotElement.style.position = "absolute";
|
||||||
quakeElement.style.justifyContent = "center";
|
dotElement.style.zIndex = "2"; // Ensure dot is above pulse
|
||||||
|
|
||||||
// Central dot
|
// Pulsing ring
|
||||||
dotElement.style.width = "10px";
|
pulseElement.className = "location-pulse";
|
||||||
dotElement.style.height = "10px";
|
pulseElement.style.width = "20px"; // Initial size
|
||||||
dotElement.style.backgroundColor = color;
|
pulseElement.style.height = "20px";
|
||||||
dotElement.style.borderRadius = "50%";
|
pulseElement.style.backgroundColor = `${color}80`; // Color with 50% opacity (hex alpha)
|
||||||
dotElement.style.position = "absolute";
|
pulseElement.style.borderRadius = "50%";
|
||||||
dotElement.style.zIndex = "2"; // Ensure dot is above pulse
|
pulseElement.style.position = "absolute";
|
||||||
|
pulseElement.style.zIndex = "1";
|
||||||
|
}
|
||||||
|
|
||||||
// Pulsing ring
|
// Observatory marker
|
||||||
pulseElement.className = "location-pulse";
|
//<GiObservatory />
|
||||||
pulseElement.style.width = "20px"; // Initial size
|
const observatoryElement = document.createElement("div");
|
||||||
pulseElement.style.height = "20px";
|
const root = createRoot(observatoryElement); // `createRoot` is now the standard API
|
||||||
pulseElement.style.backgroundColor = `${color}80`; // Color with 50% opacity (hex alpha)
|
root.render(<GiObservatory className="text-blue-600 text-2xl drop-shadow-lg" />);
|
||||||
pulseElement.style.borderRadius = "50%";
|
|
||||||
pulseElement.style.position = "absolute";
|
|
||||||
pulseElement.style.zIndex = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Observatory marker
|
quakeElement.appendChild(pulseElement);
|
||||||
//<GiObservatory />
|
quakeElement.appendChild(dotElement);
|
||||||
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);
|
const marker = new mapboxgl.Marker({ element: mapType === "observatories" ? observatoryElement : quakeElement })
|
||||||
quakeElement.appendChild(dotElement);
|
.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>
|
<div>
|
||||||
<h3>${event.title}</h3>
|
<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>
|
<p>${event.text2}</p>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
marker.setPopup(popup);
|
marker.setPopup(popup);
|
||||||
markers.current[event.id] = marker;
|
markers.current[event.id] = marker;
|
||||||
|
|
||||||
// Add hover events
|
// Add hover events
|
||||||
const markerDomElement = marker.getElement();
|
const markerDomElement = marker.getElement();
|
||||||
markerDomElement.style.cursor = "pointer"; // Optional: indicate interactivity
|
markerDomElement.style.cursor = "pointer"; // Optional: indicate interactivity
|
||||||
|
|
||||||
markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id));
|
markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id));
|
||||||
markerDomElement.addEventListener("mouseleave", () => setHoveredEventId(""));
|
markerDomElement.addEventListener("mouseleave", () => setHoveredEventId(""));
|
||||||
markerDomElement.addEventListener("click", () => setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id)));
|
markerDomElement.addEventListener("click", () =>
|
||||||
|
setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id))
|
||||||
|
);
|
||||||
|
|
||||||
// Cleanup event listeners on unmount
|
// Cleanup event listeners on unmount
|
||||||
markerDomElement.dataset.listenersAdded = "true"; // Mark for cleanup
|
markerDomElement.dataset.listenersAdded = "true"; // Mark for cleanup
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
map.current.on("error", (e) => {
|
map.current.on("error", (e) => {
|
||||||
console.error("Mapbox error:", e);
|
console.error("Mapbox error:", e);
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
map.current?.remove();
|
map.current?.remove();
|
||||||
};
|
};
|
||||||
}, [events, setSelectedEventId, setHoveredEventId, fitToBounds]);
|
}, [events, setSelectedEventId, setHoveredEventId, fitToBounds]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const event = events.find((x) => x.id === selectedEventId);
|
const event = events.find((x) => x.id === selectedEventId);
|
||||||
if (event) flyToEvent(event);
|
if (event) flyToEvent(event);
|
||||||
else if (!selectedEventId) {
|
else if (!selectedEventId) {
|
||||||
if (mapBounds) fitToBounds(mapBounds);
|
if (mapBounds) fitToBounds(mapBounds);
|
||||||
}
|
}
|
||||||
}, [events, selectedEventId, mapBounds, fitToBounds, clearAllPopups, flyToEvent]);
|
}, [events, selectedEventId, mapBounds, fitToBounds, clearAllPopups, flyToEvent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Clear all popups first
|
// Clear all popups first
|
||||||
clearAllPopups();
|
clearAllPopups();
|
||||||
|
|
||||||
// Handle both events if they exist and are different
|
// Handle both events if they exist and are different
|
||||||
if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) {
|
if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) {
|
||||||
showPopup(hoveredEventId);
|
showPopup(hoveredEventId);
|
||||||
showPopup(selectedEventId);
|
showPopup(selectedEventId);
|
||||||
}
|
}
|
||||||
// Handle single event case (either hovered or selected)
|
// Handle single event case (either hovered or selected)
|
||||||
else if (hoveredEventId || selectedEventId) {
|
else if (hoveredEventId || selectedEventId) {
|
||||||
showPopup(hoveredEventId || selectedEventId);
|
showPopup(hoveredEventId || selectedEventId);
|
||||||
}
|
}
|
||||||
}, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]);
|
}, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<div id="map-container" className="w-full h-full" />
|
<div id="map-container" className="w-full h-full" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS for location-style pulsing animation
|
// CSS for location-style pulsing animation
|
||||||
@ -211,10 +220,10 @@ const pulseStyles = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export default function Map(props: MapComponentProps) {
|
export default function Map(props: MapComponentProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style>{pulseStyles}</style>
|
<style>{pulseStyles}</style>
|
||||||
<MapComponent {...props} />
|
<MapComponent {...props} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Image from 'next/image';
|
import Image from "next/image";
|
||||||
import Link from 'next/link';
|
import Link from "next/link";
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from "next/navigation";
|
||||||
import { Dispatch, SetStateAction, useMemo, useState } from 'react';
|
import { Dispatch, SetStateAction, useMemo, useState } from "react";
|
||||||
import { FaRegUserCircle } from 'react-icons/fa';
|
import { FaRegUserCircle } from "react-icons/fa";
|
||||||
|
|
||||||
import { Currency } from '@appTypes/StoreModel';
|
import { Currency } from "@appTypes/StoreModel";
|
||||||
import AuthModal from '@components/AuthModal';
|
import AuthModal from "@components/AuthModal";
|
||||||
import { useStoreActions, useStoreState } from '@hooks/store';
|
import { useStoreActions, useStoreState } from "@hooks/store";
|
||||||
|
|
||||||
export default function Navbar({}: // currencySelector,
|
export default function Navbar({}: // currencySelector,
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
interface Event {
|
interface Event {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
magnitude?: number;
|
magnitude?: number;
|
||||||
longitude: number;
|
longitude: number;
|
||||||
latitude: number;
|
latitude: number;
|
||||||
text1: string;
|
text1: string;
|
||||||
text2: string;
|
text2: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Event;
|
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);
|
export const fetcher = (url: string) => axios.get(url).then((res) => res.data);
|
||||||
|
|||||||
@ -1,20 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import resolveConfig from "tailwindcss/resolveConfig";
|
import resolveConfig from "tailwindcss/resolveConfig";
|
||||||
|
|
||||||
import tailwindConfig from "../../tailwind.config";
|
import tailwindConfig from "../../tailwind.config";
|
||||||
|
|
||||||
function getMagnitudeColour(magnitude: number) {
|
function getMagnitudeColour(magnitude: number) {
|
||||||
const fullConfig = resolveConfig(tailwindConfig);
|
const fullConfig = resolveConfig(tailwindConfig);
|
||||||
const colors = fullConfig.theme.colors;
|
const colors = fullConfig.theme.colors;
|
||||||
|
|
||||||
if (magnitude >= 7) {
|
if (magnitude >= 7) {
|
||||||
return colors["red"][600];
|
return colors["red"][600];
|
||||||
} else if (magnitude >= 5) {
|
} else if (magnitude >= 5) {
|
||||||
return colors["orange"][500];
|
return colors["orange"][500];
|
||||||
} else if (magnitude >= 3) {
|
} else if (magnitude >= 3) {
|
||||||
return colors["amber"][500];
|
return colors["amber"][500];
|
||||||
} else {
|
} else {
|
||||||
return colors["blue"][400];
|
return colors["blue"][400];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default getMagnitudeColour;
|
export default getMagnitudeColour;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user