Did a lot of cleaning up and improving
This commit is contained in:
parent
c6ebf4d50a
commit
ab72dd88cb
102
package-lock.json
generated
102
package-lock.json
generated
@ -14,6 +14,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"csv-parser": "^3.2.0",
|
"csv-parser": "^3.2.0",
|
||||||
|
"easy-peasy": "^6.1.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
@ -55,6 +56,18 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"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": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||||
@ -1637,7 +1650,7 @@
|
|||||||
"version": "19.0.10",
|
"version": "19.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
|
||||||
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
|
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@ -1647,7 +1660,7 @@
|
|||||||
"version": "19.0.4",
|
"version": "19.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
|
||||||
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
|
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@types/react": "^19.0.0"
|
"@types/react": "^19.0.0"
|
||||||
@ -2803,7 +2816,7 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/csv-parser": {
|
"node_modules/csv-parser": {
|
||||||
@ -3048,6 +3061,42 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@ -3844,7 +3893,6 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
@ -4561,6 +4609,16 @@
|
|||||||
"node": ">= 4"
|
"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": {
|
"node_modules/import-fresh": {
|
||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||||
@ -6605,6 +6663,21 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/reflect.getprototypeof": {
|
||||||
"version": "1.0.10",
|
"version": "1.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
|
||||||
@ -6628,6 +6701,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||||
@ -7770,6 +7849,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0"
|
"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": {
|
"node_modules/tsconfig-paths": {
|
||||||
"version": "3.15.0",
|
"version": "3.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
|
||||||
@ -7953,6 +8038,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"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": {
|
"node_modules/util": {
|
||||||
"version": "0.10.4",
|
"version": "0.10.4",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
"bcryptjs": "^3.0.2",
|
"bcryptjs": "^3.0.2",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"csv-parser": "^3.2.0",
|
"csv-parser": "^3.2.0",
|
||||||
|
"easy-peasy": "^6.1.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fs": "^0.0.1-security",
|
"fs": "^0.0.1-security",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React, { useState } from "react";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
|
||||||
const ContactUs = () => {
|
const ContactUs = () => {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@ -21,8 +21,7 @@ const ContactUs = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="h-screen relative text-white py-10 border border-black">
|
||||||
className="h-screen relative text-white py-10 border border-black">
|
|
||||||
<Image height={5000} width={5000} alt="Logo" className="border border-neutral-300 absolute z-10" src="/tsunamiWaves.jpg" />
|
<Image height={5000} width={5000} alt="Logo" className="border border-neutral-300 absolute z-10" src="/tsunamiWaves.jpg" />
|
||||||
|
|
||||||
{/* Overlay for readability */}
|
{/* Overlay for readability */}
|
||||||
@ -30,24 +29,19 @@ const ContactUs = () => {
|
|||||||
{/* Container */}
|
{/* Container */}
|
||||||
<div className="max-w-4xl mx-auto p-5">
|
<div className="max-w-4xl mx-auto p-5">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<h1 className="text-4xl font-bold text-center text-white mb-6">
|
<h1 className="text-4xl font-bold text-center text-white mb-6">Contact Us</h1>
|
||||||
Contact Us
|
<p className="text-lg text-center text-neutral-300 mb-6">
|
||||||
</h1>
|
Have questions or concerns about earthquake preparedness? Contact us using the form below or through the provided
|
||||||
<p className="text-lg text-center text-gray-300 mb-6">
|
contact details.
|
||||||
Have questions or concerns about earthquake preparedness? Contact us
|
|
||||||
using the form below or through the provided contact details.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Content Section */}
|
{/* Content Section */}
|
||||||
<div className="flex flex-col md:flex-row gap-6">
|
<div className="flex flex-col md:flex-row gap-6">
|
||||||
{/* Contact Form Section */}
|
{/* Contact Form Section */}
|
||||||
<div className="flex-1 bg-white bg-opacity-90 text-gray-800 rounded-lg shadow-lg p-6">
|
<div className="flex-1 bg-white bg-opacity-90 text-neutral-800 rounded-lg shadow-lg p-6">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label
|
<label htmlFor="name" className="block text-neutral-700 font-medium mb-2">
|
||||||
htmlFor="name"
|
|
||||||
className="block text-gray-700 font-medium mb-2"
|
|
||||||
>
|
|
||||||
Name
|
Name
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -57,16 +51,13 @@ const ContactUs = () => {
|
|||||||
value={formData.name}
|
value={formData.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Your Name"
|
placeholder="Your Name"
|
||||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full p-3 border border-neutral-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label
|
<label htmlFor="email" className="block text-neutral-700 font-medium mb-2">
|
||||||
htmlFor="email"
|
|
||||||
className="block text-gray-700 font-medium mb-2"
|
|
||||||
>
|
|
||||||
Email
|
Email
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -76,16 +67,13 @@ const ContactUs = () => {
|
|||||||
value={formData.email}
|
value={formData.email}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
placeholder="Your Email"
|
placeholder="Your Email"
|
||||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full p-3 border border-neutral-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<label
|
<label htmlFor="message" className="block text-neutral-700 font-medium mb-2">
|
||||||
htmlFor="message"
|
|
||||||
className="block text-gray-700 font-medium mb-2"
|
|
||||||
>
|
|
||||||
Message
|
Message
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@ -95,7 +83,7 @@ const ContactUs = () => {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
rows="5"
|
rows="5"
|
||||||
placeholder="Your Message"
|
placeholder="Your Message"
|
||||||
className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full p-3 border border-neutral-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -110,54 +98,36 @@ const ContactUs = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contact Details Section */}
|
{/* Contact Details Section */}
|
||||||
<div className="flex-1 bg-white bg-opacity-90 text-gray-800 rounded-lg shadow-lg p-6">
|
<div className="flex-1 bg-white bg-opacity-90 text-neutral-800 rounded-lg shadow-lg p-6">
|
||||||
<h2 className="text-xl font-bold text-gray-800 mb-4">
|
<h2 className="text-xl font-bold text-neutral-800 mb-4">Get in Touch</h2>
|
||||||
Get in Touch
|
|
||||||
</h2>
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-gray-700 font-medium">Email</h3>
|
<h3 className="text-neutral-700 font-medium">Email</h3>
|
||||||
<p className="text-gray-600">support@earthquakesafety.org</p>
|
<p className="text-neutral-600">support@earthquakesafety.org</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-gray-700 font-medium">Phone</h3>
|
<h3 className="text-neutral-700 font-medium">Phone</h3>
|
||||||
<p className="text-gray-600">+1 800 123 4567</p>
|
<p className="text-neutral-600">+1 800 123 4567</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<h3 className="text-gray-700 font-medium">Address</h3>
|
<h3 className="text-neutral-700 font-medium">Address</h3>
|
||||||
<p className="text-gray-600">
|
<p className="text-neutral-600">123 Earthquake Ave, Prepared City, CA 98765</p>
|
||||||
123 Earthquake Ave, Prepared City, CA 98765
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 className="text-xl font-bold text-gray-800 mb-4 mt-6">
|
<h2 className="text-xl font-bold text-neutral-800 mb-4 mt-6">Follow Us</h2>
|
||||||
Follow Us
|
|
||||||
</h2>
|
|
||||||
<div className="flex justify-around items-center">
|
<div className="flex justify-around items-center">
|
||||||
<a
|
<a href="#" className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200">
|
||||||
href="#"
|
|
||||||
className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Instagram</span>
|
<span className="sr-only">Instagram</span>
|
||||||
<Image height={200} width={200} alt="Logo" className="z-10" src="/insta.webp" />
|
<Image height={200} width={200} alt="Logo" className="z-10" src="/insta.webp" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="#" className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200">
|
||||||
href="#"
|
|
||||||
className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"
|
|
||||||
>
|
|
||||||
<span className="sr-only">Facebook</span>
|
<span className="sr-only">Facebook</span>
|
||||||
<Image height={200} width={200} alt="Logo" className="z-10" src="/facebook.webp" />
|
<Image height={200} width={200} alt="Logo" className="z-10" src="/facebook.webp" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="#" className="w-20 h-20 p-4 text-blue-600 hover:text-blue-800 transition duration-200">
|
||||||
href="#"
|
|
||||||
className="w-20 h-20 p-4 text-blue-600 hover:text-blue-800 transition duration-200"
|
|
||||||
>
|
|
||||||
<span className="sr-only">X</span>
|
<span className="sr-only">X</span>
|
||||||
<Image height={200} width={200} alt="Logo" className="z-10 rounded-lg" src="/x_logo.jpg" />
|
<Image height={200} width={200} alt="Logo" className="z-10 rounded-lg" src="/x_logo.jpg" />
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a href="#" className="w-20 h-20 flex items-center text-blue-600 hover:text-blue-800 transition duration-200">
|
||||||
href="#"
|
|
||||||
className="w-20 h-20 flex items-center text-blue-600 hover:text-blue-800 transition duration-200"
|
|
||||||
>
|
|
||||||
<span className="sr-only">LinkedIn</span>
|
<span className="sr-only">LinkedIn</span>
|
||||||
<Image height={200} width={200} alt="Logo" className="z-10" src="/linkedIn.png" />
|
<Image height={200} width={200} alt="Logo" className="z-10" src="/linkedIn.png" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Sidebar from "@/components/sidebar";
|
import { useMemo, useState } from 'react';
|
||||||
import Map from "@/components/map";
|
|
||||||
import { useState, useMemo } from "react";
|
import Map from '@components/Map';
|
||||||
|
import Sidebar from '@components/Sidebar';
|
||||||
|
|
||||||
export default function Earthquakes() {
|
export default function Earthquakes() {
|
||||||
const [selectedEventId, setSelectedEventId] = useState("");
|
const [selectedEventId, setSelectedEventId] = useState("");
|
||||||
|
|||||||
@ -1,17 +1,29 @@
|
|||||||
|
"use client";
|
||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import Navbar from "@/components/Navbar";
|
|
||||||
import { Inter } from "next/font/google";
|
|
||||||
import "./globals.css";
|
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({
|
const inter = Inter({
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
variable: "--font-inter",
|
variable: "--font-inter",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
const store = createStore<StoreModel>({
|
||||||
title: "Tremor Tracker",
|
currency: {
|
||||||
description: "Generated by tim",
|
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({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
@ -20,10 +32,12 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<StoreProvider store={store}>
|
||||||
<body className={`${inter.variable} h-[calc(100vh-3.5rem)] flex flex-col min-h-screen antialiased`}>
|
<body className={`${inter.variable} h-[calc(100vh-3.5rem)] flex flex-col min-h-screen antialiased`}>
|
||||||
<Navbar></Navbar>
|
<Navbar></Navbar>
|
||||||
<div className="flex-1 overflow-y-auto">{children}</div>
|
<div className="flex-1 overflow-y-auto">{children}</div>
|
||||||
</body>
|
</body>
|
||||||
|
</StoreProvider>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Sidebar from "@/components/sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
import Map from "@/components/map";
|
import Map from "@/components/Map";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
|
|
||||||
export default function Observatories() {
|
export default function Observatories() {
|
||||||
|
|||||||
@ -3,46 +3,38 @@
|
|||||||
//export default function Page()
|
//export default function Page()
|
||||||
const OurMission = () => {
|
const OurMission = () => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-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">
|
||||||
<h1 className="text-3xl font-bold text-center text-gray-800 mb-6">Our Mission</h1>
|
<h1 className="text-3xl font-bold text-center text-neutral-800 mb-6">Our Mission</h1>
|
||||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
<p className="text-lg text-neutral-600 leading-relaxed mb-4">
|
||||||
At <span className="font-semibold text-blue-600">Earthquake Awareness Initiative</span>, our mission is to help people worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events.
|
At <span className="font-semibold text-blue-600">Earthquake Awareness Initiative</span>, our mission is to help people
|
||||||
|
worldwide prepare for and recover from earthquakes. Through education, research, and innovative technology, we work
|
||||||
|
tirelessly to empower communities with the knowledge they need to stay safe before, during, and after seismic events.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-lg text-gray-600 leading-relaxed mb-4">
|
<p className="text-lg text-neutral-600 leading-relaxed mb-4">
|
||||||
We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster resilience against nature's powerful forces.
|
We aim to bridge the gap between scientific research and community awareness by providing resources, tools, and
|
||||||
|
real-time updates for earthquake preparedness. Together, we aspire to save lives, mitigate impacts, and foster
|
||||||
|
resilience against nature's powerful forces.
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
<div className="flex flex-col md:flex-row md:justify-evenly items-center mt-6">
|
||||||
<div className="flex flex-col items-center p-4">
|
<div className="flex flex-col items-center p-4">
|
||||||
<img
|
<img src="/images/education-icon.png" alt="Education Icon" className="h-16 w-16 mb-4" />
|
||||||
src="/images/education-icon.png"
|
<h3 className="text-xl font-bold text-neutral-700 mb-2">Education</h3>
|
||||||
alt="Education Icon"
|
<p className="text-sm text-neutral-500 text-center">
|
||||||
className="h-16 w-16 mb-4"
|
|
||||||
/>
|
|
||||||
<h3 className="text-xl font-bold text-gray-700 mb-2">Education</h3>
|
|
||||||
<p className="text-sm text-gray-500 text-center">
|
|
||||||
Providing accessible resources to educate people about earthquake preparedness.
|
Providing accessible resources to educate people about earthquake preparedness.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center p-4">
|
<div className="flex flex-col items-center p-4">
|
||||||
<img
|
<img src="/images/research-icon.png" alt="Research Icon" className="h-16 w-16 mb-4" />
|
||||||
src="/images/research-icon.png"
|
<h3 className="text-xl font-bold text-neutral-700 mb-2">Research</h3>
|
||||||
alt="Research Icon"
|
<p className="text-sm text-neutral-500 text-center">
|
||||||
className="h-16 w-16 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.
|
Supporting scientific studies to enhance understanding of seismic activity.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center p-4">
|
<div className="flex flex-col items-center p-4">
|
||||||
<img
|
<img src="/images/technology-icon.png" alt="Technology Icon" className="h-16 w-16 mb-4" />
|
||||||
src="/images/technology-icon.png"
|
<h3 className="text-xl font-bold text-neutral-700 mb-2">Technology</h3>
|
||||||
alt="Technology Icon"
|
<p className="text-sm text-neutral-500 text-center">
|
||||||
className="h-16 w-16 mb-4"
|
|
||||||
/>
|
|
||||||
<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.
|
Leveraging innovation to deliver real-time alerts and safety tools.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,115 +1,150 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Sidebar from "@/components/sidebar";
|
import { Dispatch, SetStateAction, useCallback, useState } from "react";
|
||||||
import { 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
|
// Artifacts Data
|
||||||
const artifacts = [
|
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: 1,
|
||||||
{ id: 3, name: "Medieval Chalice", description: "Used by royalty in medieval ceremonies.", location: "Cambridge, England", image: "/images/artifact3.jpg", price: 120 },
|
name: "Golden Scarab",
|
||||||
{ id: 4, name: "Roman Coin", description: "An authentic Roman coin from the 2nd century CE.", location: "Rome, Italy", image: "/images/artifact4.jpg", price: 80 },
|
description: "An ancient Egyptian artifact symbolizing rebirth.",
|
||||||
{ id: 5, name: "Samurai Mask", description: "Replica of Japanese Samurai battle masks.", location: "Tokyo, Japan", image: "/images/artifact5.jpg", price: 300 },
|
location: "Cairo, Egypt",
|
||||||
{ id: 6, name: "Ancient Greek Vase", description: "Depicts Greek mythology, found in the Acropolis.", location: "Athens, Greece", image: "/images/artifact6.jpg", price: 250 },
|
image: "/images/artifact1.jpg",
|
||||||
{ id: 7, name: "Incan Pendant", description: "Represents the Sun God Inti.", location: "India", image: "/images/artifact7.jpg", price: 175 },
|
price: 150,
|
||||||
{ 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: 2,
|
||||||
{ id: 11, name: "Ancient Scroll", description: "A mysterious scroll from ancient times.", location: "Madrid, Spain", image: "/images/artifact11.jpg", price: 500 },
|
name: "Aztec Sunstone",
|
||||||
{ id: 12, name: "Ming Dynasty Porcelain", description: "Porcelain from China's Ming Dynasty.", location: "Beijing, China", image: "/images/artifact12.jpg", price: 300 },
|
description: "A replica of the Aztec calendar (inscriptions intact).",
|
||||||
{ id: 13, name: "African Tribal Mask", description: "A unique tribal mask from Africa.", location: "Nigeria", image: "/images/artifact13.jpg", price: 250 },
|
location: "Peru",
|
||||||
{ id: 14, name: "Crystal Skull", description: "A mystical pre-Columbian artifact.", location: "Colombia", image: "/images/artifact14.jpg", price: 1000 },
|
image: "/images/artifact2.jpg",
|
||||||
{ id: 15, name: "Medieval Armor Fragment", description: "A fragment of medieval armor.", location: "Normandy, France", image: "/images/artifact15.jpg", price: 400 },
|
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
|
// Modal Component
|
||||||
const Modal = ({ artifact, onClose }) => {
|
|
||||||
if (!artifact) return null;
|
|
||||||
|
|
||||||
const handleOverlayClick = (e) => {
|
|
||||||
if (e.target === e.currentTarget) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 bg-gray-900 bg-opacity-50 flex justify-center items-center z-50"
|
|
||||||
onClick={handleOverlayClick}>
|
|
||||||
<div className="bg-white rounded-md shadow-lg max-w-lg w-full p-6">
|
|
||||||
<h3 className="text-xl font-bold mb-4">{artifact.name}</h3>
|
|
||||||
<img src={artifact.image} alt={artifact.name} className="w-full h-64 object-cover rounded-md mb-4" />
|
|
||||||
<p className="text-gray-600 mb-2">{artifact.description}</p>
|
|
||||||
<p className="text-gray-500 font-bold mb-4">Location: {artifact.location}</p>
|
|
||||||
<p className="text-red-600 font-bold">Price: ${artifact.price}</p>
|
|
||||||
<div className="flex justify-end gap-4 mt-6">
|
|
||||||
<button
|
|
||||||
onClick={() => alert("Reserved Successfully!")}
|
|
||||||
className="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-400">
|
|
||||||
Reserve
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => alert("Purchased Successfully!")}
|
|
||||||
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-500">
|
|
||||||
Buy
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="px-4 py-2 bg-gray-300 rounded-md hover:bg-gray-400">
|
|
||||||
Close
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 (
|
|
||||||
<div
|
|
||||||
className="flex flex-col bg-white shadow-md rounded-md overflow-hidden cursor-pointer hover:scale-105 transition-transform"
|
|
||||||
onClick={() => onSelect(artifact)} // Opens modal
|
|
||||||
>
|
|
||||||
<img src={artifact.image} alt={artifact.name} className="w-full h-56 object-cover" />
|
|
||||||
<div className="p-4">
|
|
||||||
<h3 className="text-lg font-bold">{artifact.name}</h3>
|
|
||||||
<p className="text-gray-700 mb-2">{artifact.description}</p>
|
|
||||||
<p className="text-gray-500 mb-2">{artifact.location}</p>
|
|
||||||
<p className="text-red-600 font-bold text-md mt-4">
|
|
||||||
{selectedCurrency}: {convertPrice(artifact.price, selectedCurrency)}
|
|
||||||
</p>
|
|
||||||
<select
|
|
||||||
value={selectedCurrency}
|
|
||||||
onChange={handleCurrencyChange} // Handles currency change
|
|
||||||
className="border border-gray-300 rounded-lg px-3 py-1 text-sm items-left"
|
|
||||||
onClick={(e) => e.stopPropagation()} // Prevents triggering the modal
|
|
||||||
>
|
|
||||||
<option value="USD">USD ($)</option>
|
|
||||||
<option value="EUR">EUR (€)</option>
|
|
||||||
<option value="GBP">GBP (£)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Shop Component
|
// Shop Component
|
||||||
export default function Shop() {
|
export default function Shop() {
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [selectedArtifact, setSelectedArtifact] = useState(null); // Track selected artifact for modal
|
const [selectedArtifact, setSelectedArtifact] = useState<Artifact | null>(null); // Track selected artifact for modal
|
||||||
const artifactsPerPage = 9; // Number of artifacts per page
|
const artifactsPerPage = 9; // Number of artifacts per page
|
||||||
const indexOfLastArtifact = currentPage * artifactsPerPage;
|
const indexOfLastArtifact = currentPage * artifactsPerPage;
|
||||||
const indexOfFirstArtifact = indexOfLastArtifact - artifactsPerPage;
|
const indexOfFirstArtifact = indexOfLastArtifact - artifactsPerPage;
|
||||||
const currentArtifacts = artifacts.slice(indexOfFirstArtifact, indexOfLastArtifact);
|
const currentArtifacts = artifacts.slice(indexOfFirstArtifact, indexOfLastArtifact);
|
||||||
|
|
||||||
|
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 handleNextPage = () => {
|
const handleNextPage = () => {
|
||||||
if (indexOfLastArtifact < artifacts.length) {
|
if (indexOfLastArtifact < artifacts.length) {
|
||||||
setCurrentPage((prev) => prev + 1);
|
setCurrentPage((prev) => prev + 1);
|
||||||
@ -122,32 +157,94 @@ export default function Shop() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSelectArtifact = (artifact) => {
|
function Modal({ artifact }: { artifact: Artifact }) {
|
||||||
setSelectedArtifact(artifact); // Open modal with selected artifact
|
if (!artifact) return null;
|
||||||
};
|
|
||||||
|
|
||||||
const handleCloseModal = () => {
|
const handleOverlayClick = (e: { target: any; currentTarget: any }) => {
|
||||||
setSelectedArtifact(null); // Close modal
|
if (e.target === e.currentTarget) {
|
||||||
|
setSelectedArtifact(null);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col min-h-screen bg-gray-100">
|
<div
|
||||||
|
className="fixed inset-0 bg-neutral-900 bg-opacity-50 flex justify-center items-center z-50"
|
||||||
|
onClick={handleOverlayClick}
|
||||||
|
>
|
||||||
|
<div className="bg-white rounded-md shadow-lg max-w-lg w-full p-6">
|
||||||
|
<h3 className="text-xl font-bold mb-4">{artifact.name}</h3>
|
||||||
|
<img src={artifact.image} alt={artifact.name} className="w-full h-64 object-cover rounded-md mb-4" />
|
||||||
|
<p className="text-neutral-600 mb-2">{artifact.description}</p>
|
||||||
|
<p className="text-neutral-500 font-bold mb-4">Location: {artifact.location}</p>
|
||||||
|
<p className="text-red-600 font-bold">Price: ${artifact.price}</p>
|
||||||
|
<div className="flex justify-end gap-4 mt-6">
|
||||||
|
<button
|
||||||
|
onClick={() => alert("Reserved Successfully!")}
|
||||||
|
className="px-4 py-2 bg-yellow-500 text-white rounded-md hover:bg-yellow-400"
|
||||||
|
>
|
||||||
|
Reserve
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => alert("Purchased Successfully!")}
|
||||||
|
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-500"
|
||||||
|
>
|
||||||
|
Buy
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedArtifact(null)}
|
||||||
|
className="px-4 py-2 bg-neutral-300 rounded-md hover:bg-neutral-400"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactCard Component
|
||||||
|
function ArtifactCard({ artifact }: { artifact: Artifact }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="flex flex-col bg-white shadow-md rounded-md overflow-hidden cursor-pointer hover:scale-105 transition-transform"
|
||||||
|
onClick={() => setSelectedArtifact(artifact)} // Opens modal
|
||||||
|
>
|
||||||
|
<img src={artifact.image} alt={artifact.name} className="w-full h-56 object-cover" />
|
||||||
|
<div className="p-4">
|
||||||
|
<h3 className="text-lg font-bold">{artifact.name}</h3>
|
||||||
|
<p className="text-neutral-700 mb-2">{artifact.description}</p>
|
||||||
|
<p className="text-neutral-500 mb-2">{artifact.location}</p>
|
||||||
|
<p className="text-blue-600 font-bold text-md mt-4">
|
||||||
|
{currencyTickers[selectedCurrency]}
|
||||||
|
{convertPrice(artifact.price, selectedCurrency)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col min-h-screen bg-neutral-100">
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1 overflow-y-auto mr-[19rem]">
|
||||||
{/* Artifact Grid */}
|
{/* Artifact Grid */}
|
||||||
<div className="flex-grow grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 p-6 pr-72">
|
<div className="flex-grow grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
|
||||||
{currentArtifacts.map((artifact) => (
|
{currentArtifacts.map((artifact) => (
|
||||||
<ArtifactCard key={artifact.id} artifact={artifact} onSelect={handleSelectArtifact} />
|
<ArtifactCard key={artifact.id} artifact={artifact} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
<aside className="w-72 bg-white shadow-lg p-4 h-screen fixed top-10 right-0 overflow-auto">
|
<div className="bg-white shadow-lg h-screen fixed right-0 overflow-y-auto">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
logTitle="Artifact Collection"
|
logTitle="Artifact Collection"
|
||||||
logSubtitle="Record new artifacts - name, description, image, location and price"
|
logSubtitle="Record new artifacts - name, description, image, location and price"
|
||||||
recentsTitle="Recent Updates"
|
recentsTitle="Recent Updates"
|
||||||
events={[/* example events if needed */]}
|
events={
|
||||||
|
[
|
||||||
|
/* example events if needed */
|
||||||
|
]
|
||||||
|
}
|
||||||
selectedEventId=""
|
selectedEventId=""
|
||||||
setSelectedEventId={() => {}}
|
setSelectedEventId={() => {}}
|
||||||
hoveredEventId=""
|
hoveredEventId=""
|
||||||
@ -155,17 +252,17 @@ export default function Shop() {
|
|||||||
button1Name="Add New Artifact"
|
button1Name="Add New Artifact"
|
||||||
button2Name="Search Artifacts"
|
button2Name="Search Artifacts"
|
||||||
/>
|
/>
|
||||||
</aside>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Pagination Footer */}
|
{/* Pagination Footer */}
|
||||||
<footer className="bg-white border-t border-gray-300 py-2 text-center w-full flex justify-center items-centre">
|
<footer className="bg-white border-t border-neutral-300 py-2 text-center w-full flex justify-center items-center mr-80">
|
||||||
<button
|
<button
|
||||||
onClick={handlePreviousPage}
|
onClick={handlePreviousPage}
|
||||||
disabled={currentPage === 1}
|
disabled={currentPage === 1}
|
||||||
className={`mx-2 px-4 py-1 bg-blue-700 text-white rounded-md font-bold shadow-md ${
|
className={`mx-2 px-4 py-1 bg-blue-700 text-white rounded-md font-bold shadow-md ${
|
||||||
currentPage === 1 ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
|
currentPage === 1 ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
|
||||||
}`} >
|
}`}
|
||||||
|
>
|
||||||
← Previous
|
← Previous
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@ -173,13 +270,13 @@ export default function Shop() {
|
|||||||
disabled={indexOfLastArtifact >= artifacts.length}
|
disabled={indexOfLastArtifact >= artifacts.length}
|
||||||
className={`mx-2 px-4 py-1 bg-blue-500 text-white rounded-md font-bold shadow-md ${
|
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"
|
indexOfLastArtifact >= artifacts.length ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
|
||||||
}`} >
|
}`}
|
||||||
|
>
|
||||||
Next →
|
Next →
|
||||||
</button>
|
</button>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{/* Modal */}
|
{/* Modal */}
|
||||||
<Modal artifact={selectedArtifact} onClose={handleCloseModal} />
|
{selectedArtifact && <Modal artifact={selectedArtifact} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Sidebar from "@/components/sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo } from "react";
|
||||||
|
|
||||||
export default function Warehouse() {
|
export default function Warehouse() {
|
||||||
@ -37,9 +37,7 @@ export default function Warehouse() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full">
|
<div className="w-full h-full">
|
||||||
<p>
|
<p>warehouse image pasted in here</p>
|
||||||
warehouse image pasted in here
|
|
||||||
</p>
|
|
||||||
<Sidebar
|
<Sidebar
|
||||||
logTitle="Artifact Retrieval and Tracking"
|
logTitle="Artifact Retrieval and Tracking"
|
||||||
logSubtitle="Record new artifacts collected - name, description, time/date found, location found, scientist and collection stage"
|
logSubtitle="Record new artifacts collected - name, description, time/date found, location found, scientist and collection stage"
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
// tells React if you're using its "server-side rendering" features. this component runs only on the client-side (browser)
|
// tells React if you're using its "server-side rendering" features. this component runs only on the client-side (browser)
|
||||||
"use client";
|
"use client";
|
||||||
// importing React hooks or utilities that enhance functionality inside the component
|
// 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).
|
useState: Used to manage state (data that changes over time).
|
||||||
FormEvent: TypeScript definition for form-related events like submission.
|
FormEvent: TypeScript definition for form-related events like submission.
|
||||||
@ -16,7 +17,8 @@ interface AuthModalProps {
|
|||||||
onClose: () => void; //A function that will be executed to close the modal
|
onClose: () => void; //A function that will be executed to close the modal
|
||||||
}
|
}
|
||||||
// creates a React functional component
|
// creates a React functional component
|
||||||
export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthModalProps ensures TypeScript validates the props to match the type definition above
|
export default function AuthModal({ isOpen, onClose }: AuthModalProps) {
|
||||||
|
//AuthModalProps ensures TypeScript validates the props to match the type definition above
|
||||||
const [isLogin, setIsLogin] = useState<boolean>(true);
|
const [isLogin, setIsLogin] = useState<boolean>(true);
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
const [isFailed, setIsFailed] = useState<boolean>(false);
|
const [isFailed, setIsFailed] = useState<boolean>(false);
|
||||||
@ -71,7 +73,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
*/
|
*/
|
||||||
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault(); // stops page from refreshing
|
e.preventDefault(); // stops page from refreshing
|
||||||
setIsFailed(false)
|
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 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 email = formData.get("email") as string; // gets email from form response
|
||||||
const password = formData.get("password") as string; // gets password from form response
|
const password = formData.get("password") as string; // gets password from form response
|
||||||
@ -80,26 +82,28 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
let endpoint = isLogin ? "/api/login" : "/api/signup"; // sets endpoint for backend code (either sign up or login)
|
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
|
const body = isLogin ? { email, password } : { name: name!, email, password }; // creates a json body for the backend
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log("Sending data to API");
|
console.log("Sending data to API");
|
||||||
const res = await fetch(endpoint, { // sends a request to the server at the end point
|
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
|
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
|
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
|
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
|
if (res.ok) {
|
||||||
|
//res.ok checks if the response is between 200-299
|
||||||
console.log("Success!");
|
console.log("Success!");
|
||||||
onClose(); // closes UI
|
onClose(); // closes UI
|
||||||
} else if (res.status >= 400 && res.status < 500) {
|
} else if (res.status >= 400 && res.status < 500) {
|
||||||
const responseBody = await res.json()
|
const responseBody = await res.json();
|
||||||
console.log("4xx error:", responseBody.message)
|
console.log("4xx error:", responseBody.message);
|
||||||
setFailMessage(responseBody.message)
|
setFailMessage(responseBody.message);
|
||||||
setIsFailed(true)
|
setIsFailed(true);
|
||||||
} else {
|
} else {
|
||||||
console.error("Error:", await res.text()); // logs error with error message sent to console
|
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)
|
} catch (error) {
|
||||||
|
// catches any errors (e.g. Not connected to network)
|
||||||
console.error("Request failed:", error instanceof Error ? error.message : String(error));
|
console.error("Request failed:", error instanceof Error ? error.message : String(error));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -107,7 +111,14 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50" onClick={handleOverlayClick}>
|
<div className="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center z-50" onClick={handleOverlayClick}>
|
||||||
<div ref={modalRef} className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
|
<div ref={modalRef} className="bg-white rounded-lg shadow-lg p-6 w-full max-w-md relative">
|
||||||
<button onClick={() => {setIsFailed(false);onClose();}} className="absolute text-xl top-0 right-2 text-gray-500 hover:text-gray-700" aria-label="Close modal">
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setIsFailed(false);
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
className="absolute text-xl top-0 right-2 text-neutral-500 hover:text-neutral-700"
|
||||||
|
aria-label="Close modal"
|
||||||
|
>
|
||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
<h2 className="text-2xl font-bold text-center mb-4">{isLogin ? "Login" : "Sign Up"}</h2>
|
<h2 className="text-2xl font-bold text-center mb-4">{isLogin ? "Login" : "Sign Up"}</h2>
|
||||||
@ -116,7 +127,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
{!isLogin && (
|
{!isLogin && (
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">Full Name</label>
|
<label className="block text-sm font-medium text-neutral-700">Full Name</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="name"
|
name="name"
|
||||||
@ -126,7 +137,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">Email</label>
|
<label className="block text-sm font-medium text-neutral-700">Email</label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
@ -135,7 +146,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-700">Password</label>
|
<label className="block text-sm font-medium text-neutral-700">Password</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
name="password"
|
name="password"
|
||||||
@ -148,8 +159,7 @@ export default function AuthModal({ isOpen, onClose }: AuthModalProps) { //AuthM
|
|||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-red-700">{failMessage}</label>
|
<label className="block text-sm font-medium text-red-700">{failMessage}</label>
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" className="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 transition">
|
<button type="submit" className="w-full bg-blue-600 text-white p-2 rounded-md hover:bg-blue-700 transition">
|
||||||
{isLogin ? "Login" : "Sign Up"}
|
{isLogin ? "Login" : "Sign Up"}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
|
||||||
import { Dispatch, SetStateAction } from "react";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import React, { Dispatch, SetStateAction } from "react";
|
||||||
import { TbHexagon } from "react-icons/tb";
|
import { TbHexagon } from "react-icons/tb";
|
||||||
|
|
||||||
import Event from "@appTypes/Event";
|
import Event from "@appTypes/Event";
|
||||||
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
import getMagnitudeColor from "@utils/getMagnitudeColour";
|
||||||
|
|
||||||
@ -51,10 +51,12 @@ export default function Sidebar({
|
|||||||
button2Name,
|
button2Name,
|
||||||
}: SidebarProps) {
|
}: SidebarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col py-6 h-full w-full max-w-xs bg-gradient-to-b from-neutral-100 to-neutral-50 shadow-lg">
|
<div className={`flex flex-col h-full w-80 relative bg-gradient-to-b from-neutral-100 to-neutral-50 shadow-lg`}>
|
||||||
|
<div className="py-6">
|
||||||
<div className="px-6 pb-8 border-b border-neutral-200">
|
<div className="px-6 pb-8 border-b border-neutral-200">
|
||||||
<h2 className="text-2xl font-bold text-neutral-800 mb-2">{logTitle}</h2>
|
<h2 className={`text-2xl font-bold text-neutral-800 mb-2`}>{logTitle}</h2>
|
||||||
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
|
<p className="text-sm text-neutral-600 leading-relaxed">{logSubtitle}</p>
|
||||||
|
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
<button className="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition-colors duration-200 font-medium">
|
<button className="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition-colors duration-200 font-medium">
|
||||||
{button1Name}
|
{button1Name}
|
||||||
@ -88,13 +90,12 @@ export default function Sidebar({
|
|||||||
{/* <p className="text-xs text-neutral-600">{event.text1}</p> */}
|
{/* <p className="text-xs text-neutral-600">{event.text1}</p> */}
|
||||||
<p className="text-xs text-neutral-500 mt-1">{event.text2}</p>
|
<p className="text-xs text-neutral-500 mt-1">{event.text2}</p>
|
||||||
</div>
|
</div>
|
||||||
{
|
{event.magnitude ? <MagnitudeNumber magnitude={event.magnitude} /> : <></>}
|
||||||
(event.magnitude) ? <MagnitudeNumber magnitude={event.magnitude} /> : <></>
|
|
||||||
}
|
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,33 @@
|
|||||||
"use client";
|
"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 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 { 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[] }) {
|
function NavbarButton({ name, href, dropdownItems }: { name: string; href: string; dropdownItems?: string[] }) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const isActive = dropdownItems ? dropdownItems.some((item) => pathname === `/${item.toLowerCase().replace(" ", "-")}`) : pathname === href;
|
const isActive = dropdownItems
|
||||||
|
? dropdownItems.some((item) => pathname === `/${item.toLowerCase().replace(" ", "-")}`)
|
||||||
|
: pathname === href;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className="flex items-center justify-center px-2 py-4 relative group">
|
<button className="flex items-center justify-center px-2 py-4 relative group">
|
||||||
{dropdownItems ? (
|
{dropdownItems ? (
|
||||||
<span className={`px-4 py-1.5 rounded-md transition-colors ${isActive ? "bg-neutral-200" : "group-hover:bg-neutral-200"}`}>{name}</span>
|
<span
|
||||||
|
className={`px-4 py-1.5 rounded-md transition-colors ${isActive ? "bg-neutral-200" : "group-hover:bg-neutral-200"}`}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<Link href={href} className={`px-4 py-1.5 rounded-md transition-colors ${isActive ? "bg-neutral-200" : "hover:bg-neutral-200"}`}>
|
<Link
|
||||||
|
href={href}
|
||||||
|
className={`px-4 py-1.5 rounded-md transition-colors ${isActive ? "bg-neutral-200" : "hover:bg-neutral-200"}`}
|
||||||
|
>
|
||||||
{name}
|
{name}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@ -29,7 +39,10 @@ function NavbarButton({ name, href, dropdownItems }: { name: string; href: strin
|
|||||||
const isDropdownActive = pathname === itemHref;
|
const isDropdownActive = pathname === itemHref;
|
||||||
return (
|
return (
|
||||||
<li key={item}>
|
<li key={item}>
|
||||||
<Link href={itemHref} className={`block px-4 py-2 hover:bg-neutral-100 ${isDropdownActive ? "bg-neutral-100" : ""}`}>
|
<Link
|
||||||
|
href={itemHref}
|
||||||
|
className={`block px-4 py-2 hover:bg-neutral-100 ${isDropdownActive ? "bg-neutral-100" : ""}`}
|
||||||
|
>
|
||||||
{item}
|
{item}
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@ -42,7 +55,13 @@ function NavbarButton({ name, href, dropdownItems }: { name: string; href: strin
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar({}: // currencySelector,
|
||||||
|
{
|
||||||
|
// currencySelector?: { selectedCurrency: string; setSelectedCurrency: Dispatch<SetStateAction<"GBP" | "USD" | "EUR">> };
|
||||||
|
}) {
|
||||||
|
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", "Observatories", "Warehouse", "Shop"], []);
|
||||||
// const navOptions = useMemo(() => ["Earthquakes"], []);
|
// const navOptions = useMemo(() => ["Earthquakes"], []);
|
||||||
const aboutDropdown = ["Contact Us", "Our Mission", "The Team"];
|
const aboutDropdown = ["Contact Us", "Our Mission", "The Team"];
|
||||||
@ -52,6 +71,9 @@ export default function Navbar() {
|
|||||||
|
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
|
||||||
|
const currencies = useStoreState((state) => state.currency.currencies);
|
||||||
|
const currencyTickers = useStoreState((state) => state.currency.tickers);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex sticky top-0 w-full h-14 z-40 font-medium bg-white border border-b-neutral-200">
|
<div className="flex sticky top-0 w-full h-14 z-40 font-medium bg-white border border-b-neutral-200">
|
||||||
<div className="my-1 flex aspect-square ml-3 mr-3">
|
<div className="my-1 flex aspect-square ml-3 mr-3">
|
||||||
@ -66,6 +88,26 @@ export default function Navbar() {
|
|||||||
<NavbarButton name="About Us" href="/about" dropdownItems={aboutDropdown} />
|
<NavbarButton name="About Us" href="/about" dropdownItems={aboutDropdown} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-grow" />
|
<div className="flex-grow" />
|
||||||
|
|
||||||
|
{pathname.includes("shop") && (
|
||||||
|
<button className="flex items-center justify-center mr-3 py-4 relative group">
|
||||||
|
<span className={`px-4 py-1.5 rounded-md transition-colors`}>{selectedCurrency}</span>
|
||||||
|
|
||||||
|
<div className="absolute hidden group-hover:block top-full left-1/2 -translate-x-1/2 w-24 bg-white border border-neutral-300 rounded-lg overflow-hidden shadow-lg z-40">
|
||||||
|
<ul>
|
||||||
|
{currencies.map((item) => {
|
||||||
|
let ticker = currencyTickers[item];
|
||||||
|
return (
|
||||||
|
<li key={item} onClick={() => setSelectedCurrency(item)}>
|
||||||
|
<div className={`block px-2 py-2 hover:bg-neutral-100 text-md`}>{`${item} (${ticker})`}</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
<button className="my-auto mr-4" onClick={() => setIsModalOpen(true)}>
|
<button className="my-auto mr-4" onClick={() => setIsModalOpen(true)}>
|
||||||
<FaRegUserCircle size={22} />
|
<FaRegUserCircle size={22} />
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1,18 +1,16 @@
|
|||||||
import React from 'react';
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-screen w-64 bg-gray-400 text-white border-l border-gray-700">
|
<div className="flex flex-col h-screen w-64 bg-neutral-400 text-white border-l border-neutral-700">
|
||||||
<div className="flex flex-col p-4 border-b border-gray-700">
|
<div className="flex flex-col p-4 border-b border-neutral-700">
|
||||||
<h2 className="text-xl font-semibold mb-2">Log an Earthquake</h2>
|
<h2 className="text-xl font-semibold mb-2">Log an Earthquake</h2>
|
||||||
<p className="text-sm text-gray-700">
|
<p className="text-sm text-neutral-700">
|
||||||
Record new earthquakes - time/date, location, magnitude, observatory and scientists
|
Record new earthquakes - time/date, location, magnitude, observatory and scientists
|
||||||
</p>
|
</p>
|
||||||
<button className="mt-4 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded">
|
<button className="mt-4 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded">
|
||||||
<Link href="/">
|
<Link href="/">Log Event</Link>
|
||||||
Log Event
|
|
||||||
</Link>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,20 +18,20 @@ const Sidebar = () => {
|
|||||||
<div className="flex-1 p-4">
|
<div className="flex-1 p-4">
|
||||||
<h2 className="text-xl font-semibold mb-2">Recent Events</h2>
|
<h2 className="text-xl font-semibold mb-2">Recent Events</h2>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Earthquake in California</p>
|
<p className="text-sm">Earthquake in California</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 5.3</p>
|
<p className="text-xs text-neutral-300">Magnitude 5.3</p>
|
||||||
<p className="text-xs text-gray-400">2 hours ago</p>
|
<p className="text-xs text-neutral-400">2 hours ago</p>
|
||||||
</li>
|
</li>
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Tremor in Japan</p>
|
<p className="text-sm">Tremor in Japan</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 4.7</p>
|
<p className="text-xs text-neutral-300">Magnitude 4.7</p>
|
||||||
<p className="text-xs text-gray-400">5 hours ago</p>
|
<p className="text-xs text-neutral-400">5 hours ago</p>
|
||||||
</li>
|
</li>
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Tremor in Spain</p>
|
<p className="text-sm">Tremor in Spain</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 2.1</p>
|
<p className="text-xs text-neutral-300">Magnitude 2.1</p>
|
||||||
<p className="text-xs text-gray-400">10 hours ago</p>
|
<p className="text-xs text-neutral-400">10 hours ago</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,18 +1,14 @@
|
|||||||
import React from 'react';
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-screen w-64 bg-gray-400 text-white border-l border-gray-700">
|
<div className="flex flex-col h-screen w-64 bg-neutral-400 text-white border-l border-neutral-700">
|
||||||
<div className="flex flex-col p-4 border-b border-gray-700">
|
<div className="flex flex-col p-4 border-b border-neutral-700">
|
||||||
<h2 className="text-xl font-semibold mb-2">Observatories</h2>
|
<h2 className="text-xl font-semibold mb-2">Observatories</h2>
|
||||||
<p className="text-sm text-gray-700">
|
<p className="text-sm text-neutral-700">Observatory events - location, scientists, recent earthquakes</p>
|
||||||
Observatory events - location, scientists, recent earthquakes
|
|
||||||
</p>
|
|
||||||
<button className="mt-4 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded">
|
<button className="mt-4 bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded">
|
||||||
<Link href="/">
|
<Link href="/">Observatory News</Link>
|
||||||
Observatory News
|
|
||||||
</Link>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -20,20 +16,20 @@ const Sidebar = () => {
|
|||||||
<div className="flex-1 p-4">
|
<div className="flex-1 p-4">
|
||||||
<h2 className="text-xl font-semibold mb-2">Recent Observatory Events</h2>
|
<h2 className="text-xl font-semibold mb-2">Recent Observatory Events</h2>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Earthquake in California</p>
|
<p className="text-sm">Earthquake in California</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 5.3</p>
|
<p className="text-xs text-neutral-300">Magnitude 5.3</p>
|
||||||
<p className="text-xs text-gray-400">Cali Observatory</p>
|
<p className="text-xs text-neutral-400">Cali Observatory</p>
|
||||||
</li>
|
</li>
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Tremor in Japan</p>
|
<p className="text-sm">Tremor in Japan</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 4.7</p>
|
<p className="text-xs text-neutral-300">Magnitude 4.7</p>
|
||||||
<p className="text-xs text-gray-400">Kyoto Observatory</p>
|
<p className="text-xs text-neutral-400">Kyoto Observatory</p>
|
||||||
</li>
|
</li>
|
||||||
<li className="bg-gray-700 p-3 rounded hover:bg-gray-600">
|
<li className="bg-neutral-700 p-3 rounded hover:bg-neutral-600">
|
||||||
<p className="text-sm">Tremor in Spain</p>
|
<p className="text-sm">Tremor in Spain</p>
|
||||||
<p className="text-xs text-gray-300">Magnitude 2.1</p>
|
<p className="text-xs text-neutral-300">Magnitude 2.1</p>
|
||||||
<p className="text-xs text-gray-400">Madrid Observatory</p>
|
<p className="text-xs text-neutral-400">Madrid Observatory</p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
10
src/hooks/store.ts
Normal file
10
src/hooks/store.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
"use client";
|
||||||
|
import { createTypedHooks } from "easy-peasy";
|
||||||
|
|
||||||
|
import { StoreModel } from "@appTypes/StoreModel";
|
||||||
|
|
||||||
|
export const typedHooks = createTypedHooks<StoreModel>();
|
||||||
|
|
||||||
|
export const useStoreActions = typedHooks.useStoreActions;
|
||||||
|
export const useStoreDispatch = typedHooks.useStoreDispatch;
|
||||||
|
export const useStoreState = typedHooks.useStoreState;
|
||||||
11
src/types/Artifact.ts
Normal file
11
src/types/Artifact.ts
Normal file
@ -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;
|
||||||
17
src/types/StoreModel.ts
Normal file
17
src/types/StoreModel.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Action } from "easy-peasy";
|
||||||
|
|
||||||
|
type Currency = "GBP" | "USD" | "EUR";
|
||||||
|
|
||||||
|
interface CurrencyModel {
|
||||||
|
selectedCurrency: Currency;
|
||||||
|
setSelectedCurrency: Action<CurrencyModel, Currency>;
|
||||||
|
currencies: Currency[];
|
||||||
|
conversionRates: Record<Currency, number>;
|
||||||
|
tickers: Record<Currency, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StoreModel {
|
||||||
|
currency: CurrencyModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { StoreModel, Currency };
|
||||||
@ -20,11 +20,11 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
|
||||||
"@components/*": ["./src/components/*"],
|
"@components/*": ["./src/components/*"],
|
||||||
"@hooks/*": ["./src/hooks/*"],
|
"@hooks/*": ["./src/hooks/*"],
|
||||||
"@utils/*": ["./src/utils/*"],
|
"@utils/*": ["./src/utils/*"],
|
||||||
"@appTypes/*": ["./src/types/*"]
|
"@appTypes/*": ["./src/types/*"],
|
||||||
|
"@/*": ["./src/*"],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user