diff --git a/package-lock.json b/package-lock.json
index 383ebc5..fc39165 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"bcryptjs": "^3.0.2",
"body-parser": "^2.2.0",
"csv-parser": "^3.2.0",
+ "easy-peasy": "^6.1.0",
"express": "^5.1.0",
"fs": "^0.0.1-security",
"jwt-decode": "^4.0.0",
@@ -55,6 +56,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@emnapi/runtime": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
@@ -1637,7 +1650,7 @@
"version": "19.0.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
@@ -1647,7 +1660,7 @@
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.0.0"
@@ -2803,7 +2816,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/csv-parser": {
@@ -3048,6 +3061,42 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/easy-peasy": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/easy-peasy/-/easy-peasy-6.1.0.tgz",
+ "integrity": "sha512-zW7mUATJRohNWWSrihmeFhS85jjsIHa1ptTDn6bSXa2DXh8Zeawfpmm1LtKa+Y6jNNz5aQlMjWZ8uuuVk5bdVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.22.6",
+ "fast-deep-equal": "^3.1.3",
+ "immer": "^9.0.21",
+ "redux": "^5.0.1",
+ "redux-thunk": "^3.1.0",
+ "ts-toolbelt": "^9.6.0",
+ "use-sync-external-store": "^1.4.0"
+ },
+ "peerDependencies": {
+ "@types/react": "^18.0 || ^19.0",
+ "@types/react-dom": "^18.0 || ^19.0",
+ "react": "^18.0 || ^19.0",
+ "react-dom": "^18.0 || ^19.0",
+ "react-native": ">=0.59"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ }
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -3844,7 +3893,6 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
- "dev": true,
"license": "MIT"
},
"node_modules/fast-glob": {
@@ -4561,6 +4609,16 @@
"node": ">= 4"
}
},
+ "node_modules/immer": {
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/import-fresh": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
@@ -6605,6 +6663,21 @@
"node": ">=0.10.0"
}
},
+ "node_modules/redux": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
+ "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
+ "license": "MIT"
+ },
+ "node_modules/redux-thunk": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
+ "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "redux": "^5.0.0"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -6628,6 +6701,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
"node_modules/regexp.prototype.flags": {
"version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -7770,6 +7849,12 @@
"dev": true,
"license": "Apache-2.0"
},
+ "node_modules/ts-toolbelt": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz",
+ "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==",
+ "license": "Apache-2.0"
+ },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -7953,6 +8038,15 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz",
+ "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
diff --git a/package.json b/package.json
index a93180e..6bec9ce 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"bcryptjs": "^3.0.2",
"body-parser": "^2.2.0",
"csv-parser": "^3.2.0",
+ "easy-peasy": "^6.1.0",
"express": "^5.1.0",
"fs": "^0.0.1-security",
"jwt-decode": "^4.0.0",
diff --git a/src/app/contact-us/page.tsx b/src/app/contact-us/page.tsx
index ac3ebc2..7c7f00e 100644
--- a/src/app/contact-us/page.tsx
+++ b/src/app/contact-us/page.tsx
@@ -1,173 +1,143 @@
"use client";
-import React, { useState } from "react";
import Image from "next/image";
+import React, { useState } from "react";
const ContactUs = () => {
- const [formData, setFormData] = useState({
- name: "",
- email: "",
- message: "",
- });
+ const [formData, setFormData] = useState({
+ name: "",
+ email: "",
+ message: "",
+ });
- const handleChange = (e) => {
- setFormData({ ...formData, [e.target.name]: e.target.value });
- };
+ const handleChange = (e) => {
+ setFormData({ ...formData, [e.target.name]: e.target.value });
+ };
- const handleSubmit = (e) => {
- e.preventDefault();
- console.log("Form submitted with data:", formData);
- alert("Thank you for reaching out! We will get back to you soon.");
- setFormData({ name: "", email: "", message: "" });
- };
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ console.log("Form submitted with data:", formData);
+ alert("Thank you for reaching out! We will get back to you soon.");
+ setFormData({ name: "", email: "", message: "" });
+ };
- return (
-
-
-
- {/* Overlay for readability */}
-
- {/* Container */}
-
- {/* Header */}
-
- Contact Us
-
-
- Have questions or concerns about earthquake preparedness? Contact us
- using the form below or through the provided contact details.
-
+ return (
+
+
- {/* Content Section */}
-
- {/* Contact Form Section */}
-
-
- );
+ {/* Contact Details Section */}
+
+
Get in Touch
+
+
Email
+
support@earthquakesafety.org
+
+
+
Phone
+
+1 800 123 4567
+
+
+
Address
+
123 Earthquake Ave, Prepared City, CA 98765
+
+
+
Follow Us
+
+
+
+
+
+
+ );
};
-export default ContactUs;
\ No newline at end of file
+export default ContactUs;
diff --git a/src/app/earthquakes/page.tsx b/src/app/earthquakes/page.tsx
index 5a24479..6cc05b2 100644
--- a/src/app/earthquakes/page.tsx
+++ b/src/app/earthquakes/page.tsx
@@ -1,80 +1,81 @@
"use client";
-import Sidebar from "@/components/sidebar";
-import Map from "@/components/map";
-import { useState, useMemo } from "react";
+import { useMemo, useState } from 'react';
+
+import Map from '@components/Map';
+import Sidebar from '@components/Sidebar';
export default function Earthquakes() {
- const [selectedEventId, setSelectedEventId] = useState("");
- const [hoveredEventId, setHoveredEventId] = useState("");
+ const [selectedEventId, setSelectedEventId] = useState("");
+ const [hoveredEventId, setHoveredEventId] = useState("");
- const events = useMemo(
- () => [
- {
- id: "1234",
- title: "Earthquake in Germany",
- text1: "Magnitude 8.5",
- text2: "30 minutes ago",
- magnitude: 8.5,
- longitude: 10.4515, // Near Berlin, Germany
- latitude: 52.52,
- },
- {
- id: "2134",
- title: "Earthquake in California",
- text1: "Magnitude 5.3",
- text2: "2 hours ago",
- magnitude: 5.3,
- longitude: -122.4194, // Near San Francisco, California, USA
- latitude: 37.7749,
- },
- {
- id: "2314",
- title: "Tremor in Japan",
- text1: "Magnitude 4.7",
- text2: "5 hours ago",
- magnitude: 4.7,
- longitude: 139.6917, // Near Tokyo, Japan
- latitude: 35.6762,
- },
- {
- id: "2341",
- title: "Tremor in Spain",
- text1: "Magnitude 2.1",
- text2: "10 hours ago",
- magnitude: 2.1,
- longitude: -3.7038, // Near Madrid, Spain
- latitude: 40.4168,
- },
- ],
- []
- );
+ const events = useMemo(
+ () => [
+ {
+ id: "1234",
+ title: "Earthquake in Germany",
+ text1: "Magnitude 8.5",
+ text2: "30 minutes ago",
+ magnitude: 8.5,
+ longitude: 10.4515, // Near Berlin, Germany
+ latitude: 52.52,
+ },
+ {
+ id: "2134",
+ title: "Earthquake in California",
+ text1: "Magnitude 5.3",
+ text2: "2 hours ago",
+ magnitude: 5.3,
+ longitude: -122.4194, // Near San Francisco, California, USA
+ latitude: 37.7749,
+ },
+ {
+ id: "2314",
+ title: "Tremor in Japan",
+ text1: "Magnitude 4.7",
+ text2: "5 hours ago",
+ magnitude: 4.7,
+ longitude: 139.6917, // Near Tokyo, Japan
+ latitude: 35.6762,
+ },
+ {
+ id: "2341",
+ title: "Tremor in Spain",
+ text1: "Magnitude 2.1",
+ text2: "10 hours ago",
+ magnitude: 2.1,
+ longitude: -3.7038, // Near Madrid, Spain
+ latitude: 40.4168,
+ },
+ ],
+ []
+ );
- return (
-
- );
+ return (
+
+ );
}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index f546284..4e92c23 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,29 +1,43 @@
+"use client";
import type { Metadata } from "next";
-import Navbar from "@/components/Navbar";
-import { Inter } from "next/font/google";
import "./globals.css";
+import { action, createStore, StoreProvider } from "easy-peasy";
+import { Inter } from "next/font/google";
+
+import { StoreModel } from "@appTypes/StoreModel";
+import Navbar from "@components/Navbar";
+
const inter = Inter({
- subsets: ["latin"],
- variable: "--font-inter",
+ subsets: ["latin"],
+ variable: "--font-inter",
});
-export const metadata: Metadata = {
- title: "Tremor Tracker",
- description: "Generated by tim",
-};
+const store = createStore({
+ currency: {
+ selectedCurrency: "GBP",
+ setSelectedCurrency: action((state, payload) => {
+ state.selectedCurrency = payload;
+ }),
+ currencies: ["GBP", "USD", "EUR"],
+ conversionRates: { GBP: 1, USD: 1.33, EUR: 1.17 },
+ tickers: { GBP: "£", USD: "$", EUR: "€" },
+ },
+});
export default function RootLayout({
- children,
+ children,
}: Readonly<{
- children: React.ReactNode;
+ children: React.ReactNode;
}>) {
- return (
-
-
-
- {children}
-
-
- );
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
}
diff --git a/src/app/observatories/page.tsx b/src/app/observatories/page.tsx
index 5e4dd6a..28f4dbf 100644
--- a/src/app/observatories/page.tsx
+++ b/src/app/observatories/page.tsx
@@ -1,68 +1,68 @@
"use client";
-import Sidebar from "@/components/sidebar";
-import Map from "@/components/map";
+import Sidebar from "@/components/Sidebar";
+import Map from "@/components/Map";
import { useState, useMemo } from "react";
export default function Observatories() {
- const [selectedEventId, setSelectedEventId] = useState("");
- const [hoveredEventId, setHoveredEventId] = useState("");
+ const [selectedEventId, setSelectedEventId] = useState("");
+ const [hoveredEventId, setHoveredEventId] = useState("");
- const events = useMemo(
- () => [
- {
- id: "1234",
- title: "Earthquake - Berlin Observatory",
- text1: "Logged by ",
- text2: "30 minutes ago",
- longitude: 10.4515, // Near Berlin, Germany
- latitude: 52.52,
- },
- {
- id: "2134",
- title: "New Observatory - Phuket, Thailand",
- text1: "Dr. Neil Armstrong",
- text2: "2 weeks ago",
- longitude: -122.4194,
- latitude: 37.7749,
- },
- {
- id: "2314",
- title: "Observatory Scientist Change",
- text1: "Dr. Samantha Green new lead scientist",
- text2: "1 month ago",
- longitude: 139.6917,
- latitude: 35.6762,
- },
- ],
- []
- );
+ const events = useMemo(
+ () => [
+ {
+ id: "1234",
+ title: "Earthquake - Berlin Observatory",
+ text1: "Logged by ",
+ text2: "30 minutes ago",
+ longitude: 10.4515, // Near Berlin, Germany
+ latitude: 52.52,
+ },
+ {
+ id: "2134",
+ title: "New Observatory - Phuket, Thailand",
+ text1: "Dr. Neil Armstrong",
+ text2: "2 weeks ago",
+ longitude: -122.4194,
+ latitude: 37.7749,
+ },
+ {
+ id: "2314",
+ title: "Observatory Scientist Change",
+ text1: "Dr. Samantha Green new lead scientist",
+ text2: "1 month ago",
+ longitude: 139.6917,
+ latitude: 35.6762,
+ },
+ ],
+ []
+ );
- return (
-
- );
+ return (
+
+ );
}
diff --git a/src/app/our-mission/page.tsx b/src/app/our-mission/page.tsx
index 24793b7..a7757d4 100644
--- a/src/app/our-mission/page.tsx
+++ b/src/app/our-mission/page.tsx
@@ -2,54 +2,46 @@
//export default function Page()
const OurMission = () => {
- return (
-
-
-
Our Mission
-
- At Earthquake Awareness Initiative , our mission is to help people worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events.
-
-
- We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster resilience against nature's powerful forces.
-
-
-
-
-
Education
-
- Providing accessible resources to educate people about earthquake preparedness.
-
-
-
-
-
Research
-
- Supporting scientific studies to enhance understanding of seismic activity.
-
-
-
-
-
Technology
-
- Leveraging innovation to deliver real-time alerts and safety tools.
-
-
-
-
-
- );
+ return (
+
+
+
Our Mission
+
+ At Earthquake Awareness Initiative , our mission is to help people
+ worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work
+ tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events.
+
+
+ We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and
+ real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster
+ resilience against nature's powerful forces.
+
+
+
+
+
Education
+
+ Providing accessible resources to educate people about earthquake preparedness.
+
+
+
+
+
Research
+
+ Supporting scientific studies to enhance understanding of seismic activity.
+
+
+
+
+
Technology
+
+ Leveraging innovation to deliver real-time alerts and safety tools.
+
+
+
+
+
+ );
};
-export default OurMission;
\ No newline at end of file
+export default OurMission;
diff --git a/src/app/shop/page.tsx b/src/app/shop/page.tsx
index 1626553..0684fb5 100644
--- a/src/app/shop/page.tsx
+++ b/src/app/shop/page.tsx
@@ -1,185 +1,282 @@
"use client";
-import Sidebar from "@/components/sidebar";
-import { useState } from "react";
+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";
// Artifacts Data
-const artifacts = [
- { id: 1, name: "Golden Scarab", description: "An ancient Egyptian artifact symbolizing rebirth.", location: "Cairo, Egypt", image: "/images/artifact1.jpg", price: 150 },
- { id: 2, name: "Aztec Sunstone", description: "A replica of the Aztec calendar (inscriptions intact).", location: "Peru", image: "/images/artifact2.jpg", price: 200 },
- { id: 3, name: "Medieval Chalice", description: "Used by royalty in medieval ceremonies.", location: "Cambridge, England", image: "/images/artifact3.jpg", price: 120 },
- { id: 4, name: "Roman Coin", description: "An authentic Roman coin from the 2nd century CE.", location: "Rome, Italy", image: "/images/artifact4.jpg", price: 80 },
- { id: 5, name: "Samurai Mask", description: "Replica of Japanese Samurai battle masks.", location: "Tokyo, Japan", image: "/images/artifact5.jpg", price: 300 },
- { id: 6, name: "Ancient Greek Vase", description: "Depicts Greek mythology, found in the Acropolis.", location: "Athens, Greece", image: "/images/artifact6.jpg", price: 250 },
- { id: 7, name: "Incan Pendant", description: "Represents the Sun God Inti.", location: "India", image: "/images/artifact7.jpg", price: 175 },
- { id: 8, name: "Persian Carpet Fragment", description: "Ancient Persian artistry.", location: "Petra, Jordan", image: "/images/artifact8.jpg", price: 400 },
- { id: 9, name: "Stone Buddha", description: "Authentic stone Buddha carving.", location: "India", image: "/images/artifact9.jpg", price: 220 },
- { id: 10, name: "Victorian Brooch", description: "A beautiful Victorian-era brooch with a ruby centre.", location: "Oxford, England", image: "/images/artifact10.jpg", price: 150 },
- { id: 11, name: "Ancient Scroll", description: "A mysterious scroll from ancient times.", location: "Madrid, Spain", image: "/images/artifact11.jpg", price: 500 },
- { id: 12, name: "Ming Dynasty Porcelain", description: "Porcelain from China's Ming Dynasty.", location: "Beijing, China", image: "/images/artifact12.jpg", price: 300 },
- { id: 13, name: "African Tribal Mask", description: "A unique tribal mask from Africa.", location: "Nigeria", image: "/images/artifact13.jpg", price: 250 },
- { id: 14, name: "Crystal Skull", description: "A mystical pre-Columbian artifact.", location: "Colombia", image: "/images/artifact14.jpg", price: 1000 },
- { id: 15, name: "Medieval Armor Fragment", description: "A fragment of medieval armor.", location: "Normandy, France", image: "/images/artifact15.jpg", price: 400 },
+const artifacts: Artifact[] = [
+ {
+ id: 1,
+ name: "Golden Scarab",
+ description: "An ancient Egyptian artifact symbolizing rebirth.",
+ location: "Cairo, Egypt",
+ image: "/images/artifact1.jpg",
+ price: 150,
+ },
+ {
+ id: 2,
+ name: "Aztec Sunstone",
+ description: "A replica of the Aztec calendar (inscriptions intact).",
+ location: "Peru",
+ image: "/images/artifact2.jpg",
+ price: 200,
+ },
+ {
+ id: 3,
+ name: "Medieval Chalice",
+ description: "Used by royalty in medieval ceremonies.",
+ location: "Cambridge, England",
+ image: "/images/artifact3.jpg",
+ price: 120,
+ },
+ {
+ id: 4,
+ name: "Roman Coin",
+ description: "An authentic Roman coin from the 2nd century CE.",
+ location: "Rome, Italy",
+ image: "/images/artifact4.jpg",
+ price: 80,
+ },
+ {
+ id: 5,
+ name: "Samurai Mask",
+ description: "Replica of Japanese Samurai battle masks.",
+ location: "Tokyo, Japan",
+ image: "/images/artifact5.jpg",
+ price: 300,
+ },
+ {
+ id: 6,
+ name: "Ancient Greek Vase",
+ description: "Depicts Greek mythology, found in the Acropolis.",
+ location: "Athens, Greece",
+ image: "/images/artifact6.jpg",
+ price: 250,
+ },
+ {
+ id: 7,
+ name: "Incan Pendant",
+ description: "Represents the Sun God Inti.",
+ location: "India",
+ image: "/images/artifact7.jpg",
+ price: 175,
+ },
+ {
+ id: 8,
+ name: "Persian Carpet Fragment",
+ description: "Ancient Persian artistry.",
+ location: "Petra, Jordan",
+ image: "/images/artifact8.jpg",
+ price: 400,
+ },
+ {
+ id: 9,
+ name: "Stone Buddha",
+ description: "Authentic stone Buddha carving.",
+ location: "India",
+ image: "/images/artifact9.jpg",
+ price: 220,
+ },
+ {
+ id: 10,
+ name: "Victorian Brooch",
+ description: "A beautiful Victorian-era brooch with a ruby centre.",
+ location: "Oxford, England",
+ image: "/images/artifact10.jpg",
+ price: 150,
+ },
+ {
+ id: 11,
+ name: "Ancient Scroll",
+ description: "A mysterious scroll from ancient times.",
+ location: "Madrid, Spain",
+ image: "/images/artifact11.jpg",
+ price: 500,
+ },
+ {
+ id: 12,
+ name: "Ming Dynasty Porcelain",
+ description: "Porcelain from China's Ming Dynasty.",
+ location: "Beijing, China",
+ image: "/images/artifact12.jpg",
+ price: 300,
+ },
+ {
+ id: 13,
+ name: "African Tribal Mask",
+ description: "A unique tribal mask from Africa.",
+ location: "Nigeria",
+ image: "/images/artifact13.jpg",
+ price: 250,
+ },
+ {
+ id: 14,
+ name: "Crystal Skull",
+ description: "A mystical pre-Columbian artifact.",
+ location: "Colombia",
+ image: "/images/artifact14.jpg",
+ price: 1000,
+ },
+ {
+ id: 15,
+ name: "Medieval Armor Fragment",
+ description: "A fragment of medieval armor.",
+ location: "Normandy, France",
+ image: "/images/artifact15.jpg",
+ price: 400,
+ },
];
// Modal Component
-const Modal = ({ artifact, onClose }) => {
- if (!artifact) return null;
-
- const handleOverlayClick = (e) => {
- if (e.target === e.currentTarget) {
- onClose();
- }
- };
-
- return (
-
-
-
{artifact.name}
-
-
{artifact.description}
-
Location: {artifact.location}
-
Price: ${artifact.price}
-
- alert("Reserved Successfully!")}
- className="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-400">
- Reserve
-
- alert("Purchased Successfully!")}
- className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-500">
- Buy
-
-
- Close
-
-
-
-
- );
-};
-
-// ArtifactCard Component
-const ArtifactCard = ({ artifact, onSelect }) => {
- const [selectedCurrency, setSelectedCurrency] = useState("USD");
- const conversionRates = { USD: 1, EUR: 0.94, GBP: 0.81 };
- const convertPrice = (price, currency) => (price * conversionRates[currency]).toFixed(2);
- const handleCurrencyChange = (e) => {
- setSelectedCurrency(e.target.value); // Update selected currency
- e.stopPropagation(); // Prevent the modal from opening on dropdown click
- };
-
- return (
- onSelect(artifact)} // Opens modal
- >
-
-
-
{artifact.name}
-
{artifact.description}
-
{artifact.location}
-
- {selectedCurrency}: {convertPrice(artifact.price, selectedCurrency)}
-
-
e.stopPropagation()} // Prevents triggering the modal
- >
- USD ($)
- EUR (€)
- GBP (£)
-
-
-
- );
-};
-
// Shop Component
export default function Shop() {
- const [currentPage, setCurrentPage] = useState(1);
- const [selectedArtifact, setSelectedArtifact] = useState(null); // Track selected artifact for modal
- const artifactsPerPage = 9; // Number of artifacts per page
- const indexOfLastArtifact = currentPage * artifactsPerPage;
- const indexOfFirstArtifact = indexOfLastArtifact - artifactsPerPage;
- const currentArtifacts = artifacts.slice(indexOfFirstArtifact, indexOfLastArtifact);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [selectedArtifact, setSelectedArtifact] = useState(null); // Track selected artifact for modal
+ const artifactsPerPage = 9; // Number of artifacts per page
+ const indexOfLastArtifact = currentPage * artifactsPerPage;
+ const indexOfFirstArtifact = indexOfLastArtifact - artifactsPerPage;
+ const currentArtifacts = artifacts.slice(indexOfFirstArtifact, indexOfLastArtifact);
- const handleNextPage = () => {
- if (indexOfLastArtifact < artifacts.length) {
- setCurrentPage((prev) => prev + 1);
- }
- };
+ const selectedCurrency = useStoreState((state) => state.currency.selectedCurrency);
+ const conversionRates = useStoreState((state) => state.currency.conversionRates);
+ const currencyTickers = useStoreState((state) => state.currency.tickers);
+ const convertPrice = useCallback((price: number, currency: Currency) => (price * conversionRates[currency]).toFixed(2), []);
- const handlePreviousPage = () => {
- if (currentPage > 1) {
- setCurrentPage((prev) => prev - 1);
- }
- };
+ const handleNextPage = () => {
+ if (indexOfLastArtifact < artifacts.length) {
+ setCurrentPage((prev) => prev + 1);
+ }
+ };
- const handleSelectArtifact = (artifact) => {
- setSelectedArtifact(artifact); // Open modal with selected artifact
- };
+ const handlePreviousPage = () => {
+ if (currentPage > 1) {
+ setCurrentPage((prev) => prev - 1);
+ }
+ };
- const handleCloseModal = () => {
- setSelectedArtifact(null); // Close modal
- };
+ function Modal({ artifact }: { artifact: Artifact }) {
+ if (!artifact) return null;
- return (
-
- {/* Main Content */}
-
- {/* Artifact Grid */}
-
- {currentArtifacts.map((artifact) => (
-
- ))}
-
+ const handleOverlayClick = (e: { target: any; currentTarget: any }) => {
+ if (e.target === e.currentTarget) {
+ setSelectedArtifact(null);
+ }
+ };
- {/* Sidebar */}
-
- {}}
- hoveredEventId=""
- setHoveredEventId={() => {}}
- button1Name="Add New Artifact"
- button2Name="Search Artifacts"
- />
-
-
+ return (
+
+
+
{artifact.name}
+
+
{artifact.description}
+
Location: {artifact.location}
+
Price: ${artifact.price}
+
+ alert("Reserved Successfully!")}
+ className="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-400"
+ >
+ Reserve
+
+ alert("Purchased Successfully!")}
+ className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-500"
+ >
+ Buy
+
+ setSelectedArtifact(null)}
+ className="px-4 py-2 bg-neutral-300 rounded-md hover:bg-neutral-400"
+ >
+ Close
+
+
+
+
+ );
+ }
- {/* Pagination Footer */}
-
-
- ← Previous
-
- = artifacts.length}
- className={`mx-2 px-4 py-1 bg-blue-500 text-white rounded-md font-bold shadow-md ${
- indexOfLastArtifact >= artifacts.length ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
- }`} >
- Next →
-
-
+ // ArtifactCard Component
+ function ArtifactCard({ artifact }: { artifact: Artifact }) {
+ return (
+
setSelectedArtifact(artifact)} // Opens modal
+ >
+
+
+
{artifact.name}
+
{artifact.description}
+
{artifact.location}
+
+ {currencyTickers[selectedCurrency]}
+ {convertPrice(artifact.price, selectedCurrency)}
+
+
+
+ );
+ }
- {/* Modal */}
-
-
- );
+ return (
+
+ {/* Main Content */}
+
+ {/* Artifact Grid */}
+
+ {currentArtifacts.map((artifact) => (
+
+ ))}
+
+
+ {/* Sidebar */}
+
+ {}}
+ hoveredEventId=""
+ setHoveredEventId={() => {}}
+ button1Name="Add New Artifact"
+ button2Name="Search Artifacts"
+ />
+
+
+ {/* Pagination Footer */}
+
+
+ ← Previous
+
+ = artifacts.length}
+ className={`mx-2 px-4 py-1 bg-blue-500 text-white rounded-md font-bold shadow-md ${
+ indexOfLastArtifact >= artifacts.length ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
+ }`}
+ >
+ Next →
+
+
+ {/* Modal */}
+ {selectedArtifact &&
}
+
+ );
}
diff --git a/src/app/warehouse/page.tsx b/src/app/warehouse/page.tsx
index 92806cd..4d80506 100644
--- a/src/app/warehouse/page.tsx
+++ b/src/app/warehouse/page.tsx
@@ -1,57 +1,55 @@
"use client";
-import Sidebar from "@/components/sidebar";
+import Sidebar from "@/components/Sidebar";
import { useState, useMemo } from "react";
export default function Warehouse() {
- const [selectedEventId, setSelectedEventId] = useState("");
- const [hoveredEventId, setHoveredEventId] = useState("");
- const events = useMemo(
- () => [
- {
- id: "1234",
- title: "Artifact found - Germany Earthquake",
- text1: "Mortar and pestle",
- text2: "10 minutes ago",
- longitude: 10.4515, // Near Berlin, Germany
- latitude: 52.52,
- },
- {
- id: "2134",
- title: "All artifacts from California earthquake transported",
- text1: "India to London",
- text2: "15 hours ago",
- longitude: -122.4194, // Near San Francisco, California, USA
- latitude: 37.7749,
- },
- {
- id: "2341",
- title: "7 artifacts destroyed - Spain earthquake",
- text1: "edVases and pots from Madrid",
- text2: "3 weeks ago",
- longitude: -3.7038, // Near Madrid, Spain
- latitude: 40.4168,
- },
- ],
- []
- );
+ const [selectedEventId, setSelectedEventId] = useState("");
+ const [hoveredEventId, setHoveredEventId] = useState("");
+ const events = useMemo(
+ () => [
+ {
+ id: "1234",
+ title: "Artifact found - Germany Earthquake",
+ text1: "Mortar and pestle",
+ text2: "10 minutes ago",
+ longitude: 10.4515, // Near Berlin, Germany
+ latitude: 52.52,
+ },
+ {
+ id: "2134",
+ title: "All artifacts from California earthquake transported",
+ text1: "India to London",
+ text2: "15 hours ago",
+ longitude: -122.4194, // Near San Francisco, California, USA
+ latitude: 37.7749,
+ },
+ {
+ id: "2341",
+ title: "7 artifacts destroyed - Spain earthquake",
+ text1: "edVases and pots from Madrid",
+ text2: "3 weeks ago",
+ longitude: -3.7038, // Near Madrid, Spain
+ latitude: 40.4168,
+ },
+ ],
+ []
+ );
- return (
-
-
- warehouse image pasted in here
-
-
-
- );
+ return (
+
+
warehouse image pasted in here
+
+
+ );
}
diff --git a/src/components/AuthModal.tsx b/src/components/AuthModal.tsx
index b46522d..38218e3 100644
--- a/src/components/AuthModal.tsx
+++ b/src/components/AuthModal.tsx
@@ -1,7 +1,8 @@
// tells React if you're using its "server-side rendering" features. this component runs only on the client-side (browser)
"use client";
// importing React hooks or utilities that enhance functionality inside the component
-import { useState, FormEvent, useRef, MouseEvent, useEffect } from "react";
+import { FormEvent, MouseEvent, useEffect, useRef, useState } from "react";
+
/*
useState: Used to manage state (data that changes over time).
FormEvent: TypeScript definition for form-related events like submission.
@@ -12,39 +13,40 @@ import { useState, FormEvent, useRef, MouseEvent, useEffect } from "react";
// defining a type for the props (inputs) that AuthModal expects
interface AuthModalProps {
- isOpen: boolean; // bool for if the modal should be visible
- onClose: () => void; //A function that will be executed to close the modal
+ isOpen: boolean; // bool for if the modal should be visible
+ onClose: () => void; //A function that will be executed to close the modal
}
// creates a React functional component
-export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthModalProps ensures TypeScript validates the props to match the type definition above
- const [isLogin, setIsLogin] = useState(true);
- const modalRef = useRef(null);
- const [isFailed, setIsFailed] = useState(false);
- const [failMessage, setFailMessage] = useState(false);
-/*
+export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
+ //AuthModalProps ensures TypeScript validates the props to match the type definition above
+ const [isLogin, setIsLogin] = useState(true);
+ const modalRef = useRef(null);
+ const [isFailed, setIsFailed] = useState(false);
+ const [failMessage, setFailMessage] = useState(false);
+ /*
useState is a React Hook that declares state variables in a functional component. It returns a two-element array
The state variable (isLogin) : Represents the current state value (e.g., true initially). This is the value you can use in your component.
The state updater function (setIsLogin) : A function that allows you to update the state variable. React takes care of re-rendering the component when the state is updated
*/
-/*
+ /*
modalRef allows direct access to the modal DOM element (the container div for the modal). This is useful for detecting if the user clicks outside of the modal
*/
-// useEffect runs code after the component renders or when a dependency changes (in this case, isOpen as seen in the end [])
- useEffect(() => {
- if (isOpen) setIsLogin(true); // runs when isOpen changes, if it is true, the login is shown
- }, [isOpen]);
+ // useEffect runs code after the component renders or when a dependency changes (in this case, isOpen as seen in the end [])
+ useEffect(() => {
+ if (isOpen) setIsLogin(true); // runs when isOpen changes, if it is true, the login is shown
+ }, [isOpen]);
- if (!isOpen) return null; // if is open is false, the model isnt shown
+ if (!isOpen) return null; // if is open is false, the model isnt shown
- // this is an arrow function. e: is used to specify that an event object is expected
- const handleOverlayClick = (e: MouseEvent) => {
- if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
- onClose();
- }
- };
+ // this is an arrow function. e: is used to specify that an event object is expected
+ const handleOverlayClick = (e: MouseEvent) => {
+ if (modalRef.current && !modalRef.current.contains(e.target as Node)) {
+ onClose();
+ }
+ };
- /*
+ /*
.current gives a reference to the actual DOM element (the inner modal container)
e.target refers to the specific element the mouse click event occurred on
.contains(e.target) checks if the clicked element (e.target) is inside the modal (modalRef.current)
@@ -54,9 +56,9 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
Logic : if modalRef.current exists and the click target (e.target) is not inside the modal , then the condition is true
This means the user clicked outside the modal
*/
-
-// LS - The following bit contains the more important code for what I'm focused on
-/*
+
+ // LS - The following bit contains the more important code for what I'm focused on
+ /*
Note : handleSubmit is typically used as a event handler for submitting a form in react.
For example:
@@ -64,105 +66,113 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
Submit
*/
-/*
+ /*
e is the parameter passsed into the function
async indicates the function runs asyncronously, meaning it performs tasks which take time.
an example of this would be API calls
*/
- const handleSubmit = async (e: FormEvent) => {
- e.preventDefault(); // stops page from refreshing
- setIsFailed(false)
- const formData = new FormData(e.currentTarget); // new variable of class FormData is created. This is part of the standard Web API included in modern web browsers
- const email = formData.get("email") as string; // gets email from form response
- const password = formData.get("password") as string; // gets password from form response
- const name = isLogin ? undefined : (formData.get("name") as string);// if the form is in login mode, the name is undefine, otherwise the name is a value obtained from the response
+ const handleSubmit = async (e: FormEvent) => {
+ e.preventDefault(); // stops page from refreshing
+ setIsFailed(false);
+ const formData = new FormData(e.currentTarget); // new variable of class FormData is created. This is part of the standard Web API included in modern web browsers
+ const email = formData.get("email") as string; // gets email from form response
+ const password = formData.get("password") as string; // gets password from form response
+ const name = isLogin ? undefined : (formData.get("name") as string); // if the form is in login mode, the name is undefine, otherwise the name is a value obtained from the response
- let endpoint = isLogin ? "/api/login" : "/api/signup"; // sets endpoint for backend code (either sign up or login)
- const body = isLogin ? { email, password } : { name: name!, email, password };// creates a json body for the backend
-
+ let endpoint = isLogin ? "/api/login" : "/api/signup"; // sets endpoint for backend code (either sign up or login)
+ const body = isLogin ? { email, password } : { name: name!, email, password }; // creates a json body for the backend
- try {
- console.log("Sending data to API");
- const res = await fetch(endpoint, { // sends a request to the server at the end point
- method: "POST", // Post is used since the form submission modifies the server side state
- headers: { "Content-Type": "application/json" }, //indicates it expects a json object returned
- body: JSON.stringify(body), // converts the body to a JSON string to be sent
- });
- if (res.ok) { //res.ok checks if the response is between 200-299
- console.log("Success!");
- onClose(); // closes UI
- } else if (res.status >= 400 && res.status <500){
- const responseBody = await res.json()
- console.log("4xx error:", responseBody.message)
- setFailMessage(responseBody.message)
- setIsFailed(true)
- } else{
- console.error("Error:", await res.text()); // logs error with error message sent to console
- }
- } catch (error) {// catches any errors (e.g. Not connected to network)
- console.error("Request failed:", error instanceof Error ? error.message : String(error));
- }
- };
+ try {
+ console.log("Sending data to API");
+ const res = await fetch(endpoint, {
+ // sends a request to the server at the end point
+ method: "POST", // Post is used since the form submission modifies the server side state
+ headers: { "Content-Type": "application/json" }, //indicates it expects a json object returned
+ body: JSON.stringify(body), // converts the body to a JSON string to be sent
+ });
+ if (res.ok) {
+ //res.ok checks if the response is between 200-299
+ console.log("Success!");
+ onClose(); // closes UI
+ } else if (res.status >= 400 && res.status < 500) {
+ const responseBody = await res.json();
+ console.log("4xx error:", responseBody.message);
+ setFailMessage(responseBody.message);
+ setIsFailed(true);
+ } else {
+ console.error("Error:", await res.text()); // logs error with error message sent to console
+ }
+ } catch (error) {
+ // catches any errors (e.g. Not connected to network)
+ console.error("Request failed:", error instanceof Error ? error.message : String(error));
+ }
+ };
- return (
-
-
-
{setIsFailed(false);onClose();}} className="absolute text-xl top-0 right-2 text-gray-500 hover:text-gray-700" aria-label="Close modal">
- ×
-
-
{isLogin ? "Login" : "Sign Up"}
+ return (
+
+
+
{
+ setIsFailed(false);
+ onClose();
+ }}
+ className="absolute text-xl top-0 right-2 text-neutral-500 hover:text-neutral-700"
+ aria-label="Close modal"
+ >
+ ×
+
+
{isLogin ? "Login" : "Sign Up"}
- {/* Form */}
-
- {!isLogin && (
-
- Full Name
-
-
- )}
-
- Email
-
-
-
- Password
-
-
-
- { isFailed && (
-
- {failMessage}
-
- )
- }
-
-
- {isLogin ? "Login" : "Sign Up"}
-
-
+ {/* Form */}
+
+ {!isLogin && (
+
+ Full Name
+
+
+ )}
+
+ Email
+
+
+
+ Password
+
+
+
+ {isFailed && (
+
+ {failMessage}
+
+ )}
+
+
+ {isLogin ? "Login" : "Sign Up"}
+
+
-
- {isLogin ? "Need an account?" : "Already have an account?"}{" "}
- setIsLogin(!isLogin)} className="text-blue-600 hover:underline">
- {isLogin ? "Sign Up" : "Login"}
-
-
-
-
- );
+
+ {isLogin ? "Need an account?" : "Already have an account?"}{" "}
+ setIsLogin(!isLogin)} className="text-blue-600 hover:underline">
+ {isLogin ? "Sign Up" : "Login"}
+
+
+
+
+ );
}
diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx
index 75b0438..3ee96c8 100644
--- a/src/components/Sidebar.tsx
+++ b/src/components/Sidebar.tsx
@@ -1,100 +1,101 @@
-import React from "react";
-import { Dispatch, SetStateAction } from "react";
import Link from "next/link";
+import React, { Dispatch, SetStateAction } from "react";
import { TbHexagon } from "react-icons/tb";
+
import Event from "@appTypes/Event";
import getMagnitudeColor from "@utils/getMagnitudeColour";
interface SidebarProps {
- logTitle: string;
- logSubtitle: string;
- recentsTitle: string;
- events: Event[];
- selectedEventId: Event["id"];
- setSelectedEventId: Dispatch>;
- hoveredEventId: Event["id"];
- setHoveredEventId: Dispatch>;
- button1Name: string;
- button2Name: string;
+ logTitle: string;
+ logSubtitle: string;
+ recentsTitle: string;
+ events: Event[];
+ selectedEventId: Event["id"];
+ setSelectedEventId: Dispatch>;
+ hoveredEventId: Event["id"];
+ setHoveredEventId: Dispatch>;
+ button1Name: string;
+ button2Name: string;
}
function MagnitudeNumber({ magnitude }: { magnitude: number }) {
- // Convert magnitude to string with one decimal place
- const magnitudeStr = magnitude.toFixed(1);
- const [whole, decimal] = magnitudeStr.split(".");
+ // Convert magnitude to string with one decimal place
+ const magnitudeStr = magnitude.toFixed(1);
+ const [whole, decimal] = magnitudeStr.split(".");
- // Define color based on magnitude (0-10 scale)
- return (
-
-
-
-
- {whole}
- .
- {decimal}
-
-
-
- );
+ // Define color based on magnitude (0-10 scale)
+ return (
+
+
+
+
+ {whole}
+ .
+ {decimal}
+
+
+
+ );
}
export default function Sidebar({
- logTitle,
- logSubtitle,
- recentsTitle,
- events,
- selectedEventId,
- setSelectedEventId,
- hoveredEventId,
- setHoveredEventId,
- button1Name,
- button2Name,
+ logTitle,
+ logSubtitle,
+ recentsTitle,
+ events,
+ selectedEventId,
+ setSelectedEventId,
+ hoveredEventId,
+ setHoveredEventId,
+ button1Name,
+ button2Name,
}: SidebarProps) {
- return (
-
-
-
{logTitle}
-
{logSubtitle}
-
-
- {button1Name}
-
-
-
-
- {button2Name}
-
-
-
+ return (
+
+
+
+
{logTitle}
+
{logSubtitle}
-
-
{recentsTitle}
-
- {events.map((event) => (
-
{
- setSelectedEventId((prevEventId) => (prevEventId !== event.id ? event.id : ""));
- }}
- onMouseEnter={() => setHoveredEventId(event.id)}
- onMouseLeave={() => setHoveredEventId("")}
- >
-
-
{event.title}
- {/* Uncomment if you want to use text1 */}
- {/*
{event.text1}
*/}
-
{event.text2}
-
- {
- (event.magnitude) ? : <>>
- }
-
- ))}
-
-
-
- );
+
+
+ {button1Name}
+
+
+
+
+ {button2Name}
+
+
+
+
+
+
{recentsTitle}
+
+ {events.map((event) => (
+
{
+ setSelectedEventId((prevEventId) => (prevEventId !== event.id ? event.id : ""));
+ }}
+ onMouseEnter={() => setHoveredEventId(event.id)}
+ onMouseLeave={() => setHoveredEventId("")}
+ >
+
+
{event.title}
+ {/* Uncomment if you want to use text1 */}
+ {/*
{event.text1}
*/}
+
{event.text2}
+
+ {event.magnitude ? : <>>}
+
+ ))}
+
+
+
+
+ );
}
diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx
index 93ac50b..c2ac4c8 100644
--- a/src/components/navbar.tsx
+++ b/src/components/navbar.tsx
@@ -1,75 +1,117 @@
"use client";
-import { useState } from "react";
-
-import AuthModal from "@components/AuthModal";
-import { usePathname } from "next/navigation";
-import Link from "next/link";
import Image from "next/image";
-import { useMemo } from "react";
+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";
+
function NavbarButton({ name, href, dropdownItems }: { name: string; href: string; dropdownItems?: string[] }) {
- const pathname = usePathname();
- const isActive = dropdownItems ? dropdownItems.some((item) => pathname === `/${item.toLowerCase().replace(" ", "-")}`) : pathname === href;
+ const pathname = usePathname();
+ const isActive = dropdownItems
+ ? dropdownItems.some((item) => pathname === `/${item.toLowerCase().replace(" ", "-")}`)
+ : pathname === href;
- return (
-
- {dropdownItems ? (
- {name}
- ) : (
-
- {name}
-
- )}
- {dropdownItems && (
-
-
- {dropdownItems.map((item) => {
- const itemHref = `/${item.toLowerCase().replace(" ", "-")}`;
- const isDropdownActive = pathname === itemHref;
- return (
-
-
- {item}
-
-
- );
- })}
-
-
- )}
-
- );
+ return (
+
+ {dropdownItems ? (
+
+ {name}
+
+ ) : (
+
+ {name}
+
+ )}
+ {dropdownItems && (
+
+
+ {dropdownItems.map((item) => {
+ const itemHref = `/${item.toLowerCase().replace(" ", "-")}`;
+ const isDropdownActive = pathname === itemHref;
+ return (
+
+
+ {item}
+
+
+ );
+ })}
+
+
+ )}
+
+ );
}
-export default function Navbar() {
- const navOptions = useMemo(() => ["Earthquakes", "Observatories", "Warehouse", "Shop"], []);
- // const navOptions = useMemo(() => ["Earthquakes"], []);
- const aboutDropdown = ["Contact Us", "Our Mission", "The Team"];
- // { label: "Our Mission", path: "/our-mission" },
- // { label: "The Team", path: "/the-team" },
- // { label: "Contact Us", path: "/contact-us" }]
-
- const [isModalOpen, setIsModalOpen] = useState(false);
+export default function Navbar({}: // currencySelector,
+{
+ // currencySelector?: { selectedCurrency: string; setSelectedCurrency: Dispatch> };
+}) {
+ const pathname = usePathname();
+ const selectedCurrency = useStoreState((state) => state.currency.selectedCurrency);
+ const setSelectedCurrency = useStoreActions((actions) => actions.currency.setSelectedCurrency);
+ const navOptions = useMemo(() => ["Earthquakes", "Observatories", "Warehouse", "Shop"], []);
+ // const navOptions = useMemo(() => ["Earthquakes"], []);
+ const aboutDropdown = ["Contact Us", "Our Mission", "The Team"];
+ // { label: "Our Mission", path: "/our-mission" },
+ // { label: "The Team", path: "/the-team" },
+ // { label: "Contact Us", path: "/contact-us" }]
- return (
-
-
-
-
-
-
-
- {navOptions.map((name) => (
-
- ))}
-
-
-
-
setIsModalOpen(true)}>
-
-
-
setIsModalOpen(false)} />
-
- );
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ const currencies = useStoreState((state) => state.currency.currencies);
+ const currencyTickers = useStoreState((state) => state.currency.tickers);
+
+ return (
+
+
+
+
+
+
+
+ {navOptions.map((name) => (
+
+ ))}
+
+
+
+
+ {pathname.includes("shop") && (
+
+ {selectedCurrency}
+
+
+
+ {currencies.map((item) => {
+ let ticker = currencyTickers[item];
+ return (
+ setSelectedCurrency(item)}>
+ {`${item} (${ticker})`}
+
+ );
+ })}
+
+
+
+ )}
+
+
setIsModalOpen(true)}>
+
+
+
setIsModalOpen(false)} />
+
+ );
}
diff --git a/src/components/sidebar_e.tsx b/src/components/sidebar_e.tsx
index e6efa25..6c1dfe6 100644
--- a/src/components/sidebar_e.tsx
+++ b/src/components/sidebar_e.tsx
@@ -1,44 +1,42 @@
-import React from 'react';
import Link from "next/link";
+import React from "react";
const Sidebar = () => {
- return (
-
-
-
Log an Earthquake
-
- Record new earthquakes - time/date, location, magnitude, observatory and scientists
-
-
-
- Log Event
-
-
-
+ return (
+
+
+
Log an Earthquake
+
+ Record new earthquakes - time/date, location, magnitude, observatory and scientists
+
+
+ Log Event
+
+
- {/* Section: Recent Events - Will need to be replaced with a link to the database*/}
-
-
Recent Events
-
-
- Earthquake in California
- Magnitude 5.3
- 2 hours ago
-
-
- Tremor in Japan
- Magnitude 4.7
- 5 hours ago
-
-
- Tremor in Spain
- Magnitude 2.1
- 10 hours ago
-
-
-
-
- );
+ {/* Section: Recent Events - Will need to be replaced with a link to the database*/}
+
+
Recent Events
+
+
+ Earthquake in California
+ Magnitude 5.3
+ 2 hours ago
+
+
+ Tremor in Japan
+ Magnitude 4.7
+ 5 hours ago
+
+
+ Tremor in Spain
+ Magnitude 2.1
+ 10 hours ago
+
+
+
+
+ );
};
export default Sidebar;
diff --git a/src/components/sidebar_o.tsx b/src/components/sidebar_o.tsx
index 7a7859d..2cfd78a 100644
--- a/src/components/sidebar_o.tsx
+++ b/src/components/sidebar_o.tsx
@@ -1,44 +1,40 @@
-import React from 'react';
import Link from "next/link";
+import React from "react";
const Sidebar = () => {
- return (
-
-
-
Observatories
-
- Observatory events - location, scientists, recent earthquakes
-
-
-
- Observatory News
-
-
-
+ return (
+
+
+
Observatories
+
Observatory events - location, scientists, recent earthquakes
+
+ Observatory News
+
+
- {/* Section: Recent Events - Will need to be replaced with a link to the database*/}
-
-
Recent Observatory Events
-
-
- Earthquake in California
- Magnitude 5.3
- Cali Observatory
-
-
- Tremor in Japan
- Magnitude 4.7
- Kyoto Observatory
-
-
- Tremor in Spain
- Magnitude 2.1
- Madrid Observatory
-
-
-
-
- );
+ {/* Section: Recent Events - Will need to be replaced with a link to the database*/}
+
+
Recent Observatory Events
+
+
+ Earthquake in California
+ Magnitude 5.3
+ Cali Observatory
+
+
+ Tremor in Japan
+ Magnitude 4.7
+ Kyoto Observatory
+
+
+ Tremor in Spain
+ Magnitude 2.1
+ Madrid Observatory
+
+
+
+
+ );
};
export default Sidebar;
diff --git a/src/hooks/store.ts b/src/hooks/store.ts
new file mode 100644
index 0000000..8d331e6
--- /dev/null
+++ b/src/hooks/store.ts
@@ -0,0 +1,10 @@
+"use client";
+import { createTypedHooks } from "easy-peasy";
+
+import { StoreModel } from "@appTypes/StoreModel";
+
+export const typedHooks = createTypedHooks();
+
+export const useStoreActions = typedHooks.useStoreActions;
+export const useStoreDispatch = typedHooks.useStoreDispatch;
+export const useStoreState = typedHooks.useStoreState;
diff --git a/src/types/Artifact.ts b/src/types/Artifact.ts
new file mode 100644
index 0000000..0bd3d8c
--- /dev/null
+++ b/src/types/Artifact.ts
@@ -0,0 +1,11 @@
+interface Artifact {
+ // todo change to string
+ id: number;
+ name: string;
+ description: string;
+ location: string;
+ image: string;
+ price: number;
+}
+
+export default Artifact;
diff --git a/src/types/StoreModel.ts b/src/types/StoreModel.ts
new file mode 100644
index 0000000..7fa2929
--- /dev/null
+++ b/src/types/StoreModel.ts
@@ -0,0 +1,17 @@
+import { Action } from "easy-peasy";
+
+type Currency = "GBP" | "USD" | "EUR";
+
+interface CurrencyModel {
+ selectedCurrency: Currency;
+ setSelectedCurrency: Action;
+ currencies: Currency[];
+ conversionRates: Record;
+ tickers: Record;
+}
+
+interface StoreModel {
+ currency: CurrencyModel;
+}
+
+export type { StoreModel, Currency };
diff --git a/tsconfig.json b/tsconfig.json
index a4ead61..67fd71a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,11 +20,11 @@
}
],
"paths": {
- "@/*": ["./src/*"],
- "@components/*": ["./src/components/*"],
- "@hooks/*": ["./src/hooks/*"],
- "@utils/*": ["./src/utils/*"],
- "@appTypes/*": ["./src/types/*"]
+ "@components/*": ["./src/components/*"],
+ "@hooks/*": ["./src/hooks/*"],
+ "@utils/*": ["./src/utils/*"],
+ "@appTypes/*": ["./src/types/*"],
+ "@/*": ["./src/*"],
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],