diff --git a/public/artifacts.jpg b/public/artifacts.jpg new file mode 100644 index 0000000..35768b6 Binary files /dev/null and b/public/artifacts.jpg differ diff --git a/public/crackedRoad.jpg b/public/crackedRoad.jpg new file mode 100644 index 0000000..2e332ea Binary files /dev/null and b/public/crackedRoad.jpg differ diff --git a/public/tectonicPlate.jpg b/public/tectonicPlate.jpg new file mode 100644 index 0000000..64be6cc Binary files /dev/null and b/public/tectonicPlate.jpg differ diff --git a/src/app/api/login/route.ts b/src/app/api/login/route.ts index 12974e6..78baa37 100644 --- a/src/app/api/login/route.ts +++ b/src/app/api/login/route.ts @@ -1,11 +1,11 @@ -import bcrypt from "bcrypt"; -import { env } from "@utils/env"; -import { NextResponse } from "next/server"; -import { SignJWT } from "jose"; +import bcrypt from 'bcrypt'; +import { SignJWT } from 'jose'; +import { NextResponse } from 'next/server'; -import { PrismaClient } from "@prisma/client"; +import { PrismaClient } from '@prisma/client'; +import { env } from '@utils/env'; -import { findUserByEmail, readUserCsv, User } from "../functions/csvReadWrite"; +import { findUserByEmail, readUserCsv, User } from '../functions/csvReadWrite'; const usingPrisma = false; let prisma: PrismaClient; @@ -45,12 +45,12 @@ export async function POST(req: Request) { include: { earthquakes: true, observatories: true, - artefacts: true, + artifacts: true, superior: true, subordinates: true, }, }, - purchasedArtefacts: true, + purchasedArtifacts: true, }, }); diff --git a/src/app/api/warehouse/route.ts b/src/app/api/warehouse/route.ts index 027d92e..ffcc6d0 100644 --- a/src/app/api/warehouse/route.ts +++ b/src/app/api/warehouse/route.ts @@ -1,15 +1,15 @@ -import { NextResponse } from "next/server"; -import { env } from "@utils/env"; +import { NextResponse } from 'next/server'; -import { PrismaClient } from "@prisma/client"; -import { verifyJwt } from "@utils/verifyJwt"; +import { PrismaClient } from '@prisma/client'; +import { env } from '@utils/env'; +import { verifyJwt } from '@utils/verifyJwt'; const usingPrisma = false; let prisma: PrismaClient; if (usingPrisma) prisma = new PrismaClient(); -// Artefact type -interface Artefact { +// Artifact type +interface Artifact { id: number; name: string; description: string; @@ -29,7 +29,7 @@ export async function POST(req: Request) { if (!token) return NextResponse.json({ message: "Unauthorised" }, { status: 401 }); await verifyJwt({ token, secret: env.JWT_SECRET_KEY }); - const warehouseArtefacts: Artefact[] = [ + const warehouseArtifacts: Artifact[] = [ { id: 1, name: "Solidified Lava Chunk", @@ -87,17 +87,17 @@ export async function POST(req: Request) { }, ]; - let artefacts; - if (usingPrisma) artefacts = await prisma.artefacts.findMany(); + let artifacts; + if (usingPrisma) artifacts = await prisma.artifacts.findMany(); - if (artefacts) { - return NextResponse.json({ message: "Got artefacts successfully", artefacts }, { status: 200 }); + if (artifacts) { + return NextResponse.json({ message: "Got artifacts successfully", artifacts }, { status: 200 }); } else { - return NextResponse.json({ message: "Got earthquakes successfully", earthquakes: warehouseArtefacts }, { status: 200 }); + return NextResponse.json({ message: "Got earthquakes successfully", earthquakes: warehouseArtifacts }, { status: 200 }); // return NextResponse.json({ message: "Failed to get earthquakes" }, { status: 401 }); } } catch (error) { - console.error("Error in artefacts endpoint:", error); + console.error("Error in artifacts endpoint:", error); return NextResponse.json({ message: "Internal Server Error" }, { status: 500 }); } finally { if (usingPrisma) await prisma.$disconnect(); diff --git a/src/app/contact-us/page.tsx b/src/app/contact-us/page.tsx index 42d971c..fc371e8 100644 --- a/src/app/contact-us/page.tsx +++ b/src/app/contact-us/page.tsx @@ -1,6 +1,6 @@ "use client"; -import Image from "next/image"; -import React, { useState } from "react"; +import Image from 'next/image'; +import React, { useState } from 'react'; const ContactUs = () => { const [formData, setFormData] = useState({ @@ -36,8 +36,8 @@ const ContactUs = () => {
{/* Header */}

Contact Us

-

- Have questions or concerns about earthquake preparedness? Contact us using the form below or through the provided +

+ Have questions or concerns about earthquakes, observatories or artifacts? Contact us via phone, email, social media or using the form below with the relevant contact details.

@@ -106,20 +106,20 @@ const ContactUs = () => { {/* Contact Details Section */}
-

Get in Touch

+

Get in Touch

-

Email

- +

Email

+
getintouch@tremortracker.com
-

Phone

-

+44 7538 359022

+

Phone

+

+44 7538 359022

-

Address

-

1 Swentown Row, Greenwich, London, SE3 0FQ

+

Address

+

1 Swentown Row, Greenwich, London, SE3 0FQ

Follow Us

diff --git a/src/app/layout.tsx b/src/app/layout.tsx index c308d00..de01756 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,12 +1,12 @@ "use client"; import type { Metadata } from "next"; -import "./globals.css"; +import './globals.css'; -import { action, createStore, StoreProvider } from "easy-peasy"; -import { Inter } from "next/font/google"; +import { action, createStore, StoreProvider } from 'easy-peasy'; +import { Inter } from 'next/font/google'; -import { StoreModel } from "@appTypes/StoreModel"; -import Navbar from "@components/Navbar"; +import { StoreModel } from '@appTypes/StoreModel'; +import Navbar from '@components/Navbar'; const inter = Inter({ subsets: ["latin"], @@ -32,7 +32,7 @@ const store = createStore({ // name: "Tim Howitz", // role: "ADMIN", // scientist: undefined, - // purchasedArtefacts: [], + // purchasedArtifacts: [], // }, }); diff --git a/src/app/our-mission/page.tsx b/src/app/our-mission/page.tsx index c089c72..910a72c 100644 --- a/src/app/our-mission/page.tsx +++ b/src/app/our-mission/page.tsx @@ -1,52 +1,61 @@ "use client"; -import Image from "next/image"; +import Image from 'next/image'; const OurMission = () => { - return ( -
- {/* Overlay for Readability */} -
- - {/* Content Area */} -
-

Our Mission

-

- At Tremor Tracker, we empower communities worldwide to prepare for - and recover from earthquakes through education, cutting-edge research, and innovative technology. -

-

- We bridge scientific insights with public awareness, delivering resources, tools, and real-time updates to enhance - preparedness, save lives, and build resilience against seismic events. -

-
-
- Education Icon -

Education

-

- Delivering accessible resources to educate communities on earthquake preparedness. -

-
-
- Research Icon -

Research

-

- Advancing scientific studies to deepen understanding of seismic activity. -

-
-
- Technology Icon -

Technology

-

- Harnessing innovation for real-time alerts and safety solutions. -

-
-
-
-
- ); + return ( +
+ {/* Overlay for Readability */} +
+ {/* Centered content */} +
+ {/* Title & Mission Statement */} +
+

+ Our Mission +

+

+ Earthquake awareness accessible for everyone +

+
+ {/* Content Area */} +
+

+ At Tremor Tracker, we empower communities worldwide to understand where and why earthquakes occur to enable better preparation + and recovery. Education, cutting-edge research, and innovative technology combine together. +

+

+ We combine scientific insights with public awareness, delivering resources, tools, and real-time updates to enhance + preparedness for earthquakes in order to save lives, improve recovery efficiency, and build resilience against seismic events. +

+
+
+ Education Icon +

Education

+

+ Delivering accessible resources to educate communities on earthquakes. +

+
+
+ Research Icon +

Research

+

+ Advancing scientific studies to deepen understanding of seismic activity. +

+
+
+ Technology Icon +

Technology

+

+ Harnessing innovation for real-time alerts and safety solutions. +

+
+
+
+
+
+ ); }; - -export default OurMission; +export default OurMission; \ No newline at end of file diff --git a/src/app/shop/page.tsx b/src/app/shop/page.tsx index 676854b..5994280 100644 --- a/src/app/shop/page.tsx +++ b/src/app/shop/page.tsx @@ -1,20 +1,19 @@ "use client"; -import Image from "next/image"; -import { Dispatch, SetStateAction, useCallback, useState } from "react"; +import Image from 'next/image'; +import { Dispatch, SetStateAction, useCallback, useState } from 'react'; -import Artefact from "@appTypes/Artefact"; -import { Currency } from "@appTypes/StoreModel"; -import Sidebar from "@components/Sidebar"; -import { useStoreState } from "@hooks/store"; +import Artifact from '@appTypes/Artifact'; +import { Currency } from '@appTypes/StoreModel'; +import { useStoreState } from '@hooks/store'; -// Artefacts Data -const artefacts: Artefact[] = [ +// Artifacts Data +const artifacts: Artifact[] = [ { id: 1, name: "Golden Scarab", - description: "An ancient Egyptian artefact symbolizing rebirth.", + description: "An ancient Egyptian artifact symbolizing rebirth.", location: "Cairo, Egypt", - image: "/artefact1.jpg", + image: "/artifact1.jpg", price: 150, }, { @@ -22,7 +21,7 @@ const artefacts: Artefact[] = [ name: "Aztec Sunstone", description: "A replica of the Aztec calendar (inscriptions intact).", location: "Peru", - image: "/artefact2.jpg", + image: "/artifact2.jpg", price: 200, }, { @@ -30,7 +29,7 @@ const artefacts: Artefact[] = [ name: "Medieval Chalice", description: "Used by royalty in medieval ceremonies.", location: "Cambridge, England", - image: "/artefact3.jpg", + image: "/artifact3.jpg", price: 120, }, { @@ -38,7 +37,7 @@ const artefacts: Artefact[] = [ name: "Roman Coin", description: "An authentic Roman coin from the 2nd century CE.", location: "Rome, Italy", - image: "/artefact4.jpg", + image: "/artifact4.jpg", price: 80, }, { @@ -46,7 +45,7 @@ const artefacts: Artefact[] = [ name: "Samurai Mask", description: "Replica of Japanese Samurai battle masks.", location: "Tokyo, Japan", - image: "/artefact5.jpg", + image: "/artifact5.jpg", price: 300, }, { @@ -54,7 +53,7 @@ const artefacts: Artefact[] = [ name: "Ancient Greek Vase", description: "Depicts Greek mythology, found in the Acropolis.", location: "Athens, Greece", - image: "/artefact6.jpg", + image: "/artifact6.jpg", price: 250, }, { @@ -62,7 +61,7 @@ const artefacts: Artefact[] = [ name: "Incan Pendant", description: "Represents the Sun God Inti.", location: "India", - image: "/artefact7.jpg", + image: "/artifact7.jpg", price: 175, }, { @@ -70,7 +69,7 @@ const artefacts: Artefact[] = [ name: "Persian Carpet Fragment", description: "Ancient Persian artistry.", location: "Petra, Jordan", - image: "/artefact8.jpg", + image: "/artifact8.jpg", price: 400, }, { @@ -78,7 +77,7 @@ const artefacts: Artefact[] = [ name: "Stone Buddha", description: "Authentic stone Buddha carving.", location: "India", - image: "/artefact9.jpg", + image: "/artifact9.jpg", price: 220, }, { @@ -86,7 +85,7 @@ const artefacts: Artefact[] = [ name: "Victorian Brooch", description: "A beautiful Victorian-era brooch with a ruby centre.", location: "Oxford, England", - image: "/artefact10.jpg", + image: "/artifact10.jpg", price: 150, }, { @@ -94,7 +93,7 @@ const artefacts: Artefact[] = [ name: "Ancient Scroll", description: "A mysterious scroll from ancient times.", location: "Madrid, Spain", - image: "/artefact11.jpg", + image: "/artifact11.jpg", price: 500, }, { @@ -102,7 +101,7 @@ const artefacts: Artefact[] = [ name: "Ming Dynasty Porcelain", description: "Porcelain from China's Ming Dynasty.", location: "Beijing, China", - image: "/artefact12.jpg", + image: "/artifact12.jpg", price: 300, }, { @@ -110,15 +109,15 @@ const artefacts: Artefact[] = [ name: "African Tribal Mask", description: "A unique tribal mask from Africa.", location: "Nigeria", - image: "/artefact13.jpg", + image: "/artifact13.jpg", price: 250, }, { id: 14, name: "Crystal Skull", - description: "A mystical pre-Columbian artefact.", + description: "A mystical pre-Columbian artifact.", location: "Colombia", - image: "/artefact14.jpg", + image: "/artifact14.jpg", price: 1000, }, { @@ -126,137 +125,159 @@ const artefacts: Artefact[] = [ name: "Medieval Armor Fragment", description: "A fragment of medieval armor.", location: "Normandy, France", - image: "/artefact15.jpg", + image: "/artifact15.jpg", price: 400, }, + { + id: 16, + name: "Medieval Helmet Fragment", + description: "A fragment of a medieval helmet.", + location: "Normandy, France", + image: "/artifact16.jpg", + price: 500, + }, ]; -// Modal Component -// Shop Component export default function Shop() { - const [currentPage, setCurrentPage] = useState(1); - const [selectedArtefact, setSelectedArtefact] = useState(null); // Track selected artefact for modal - const artefactsPerPage = 9; // Number of artefacts per page - const indexOfLastArtefact = currentPage * artefactsPerPage; - const indexOfFirstArtefact = indexOfLastArtefact - artefactsPerPage; - const currentArtefacts = artefacts.slice(indexOfFirstArtefact, indexOfLastArtefact); + const [currentPage, setCurrentPage] = useState(1); + const [selectedArtifact, setSelectedArtifact] = useState(null); + const artifactsPerPage = 12; + const indexOfLastArtifact = currentPage * artifactsPerPage; + const indexOfFirstArtifact = indexOfLastArtifact - artifactsPerPage; + 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 selectedCurrency = useStoreState((state) => state.currency.selectedCurrency); + const conversionRates = useStoreState((state) => state.currency.conversionRates); + const currencyTickers = useStoreState((state) => state.currency.tickers); - const handleNextPage = () => { - if (indexOfLastArtefact < artefacts.length) { - setCurrentPage((prev) => prev + 1); - } - }; + 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 handlePreviousPage = () => { + if (currentPage > 1) { + setCurrentPage((prev) => prev - 1); + } + }; - function Modal({ artefact }: { artefact: Artefact }) { - if (!artefact) return null; + function Modal({ artifact }: { artifact: Artifact }) { + if (!artifact) return null; + const handleOverlayClick = (e: { target: any; currentTarget: any }) => { + if (e.target === e.currentTarget) { + setSelectedArtifact(null); + } + }; + return ( +
+
+

{artifact.name}

+ {artifact.name} +

+ {currencyTickers[selectedCurrency]} + {convertPrice(artifact.price, selectedCurrency)} +

+

{artifact.description}

+

Location: {artifact.location}

+
+ +
+
+
+ ); + } - const handleOverlayClick = (e: { target: any; currentTarget: any }) => { - if (e.target === e.currentTarget) { - setSelectedArtefact(null); - } - }; + function ArtifactCard({ artifact }: { artifact: Artifact }) { + return ( +
setSelectedArtifact(artifact)} + > + {artifact.name} +
+

{artifact.name}

+

{artifact.location}

+

+ {currencyTickers[selectedCurrency]} + {convertPrice(artifact.price, selectedCurrency)} +

+
+
+ ); + } - return ( -
-
-

{artefact.name}

- {artefact.name} -

- {currencyTickers[selectedCurrency]} - {convertPrice(artefact.price, selectedCurrency)} -

-

{artefact.description}

-

Location: {artefact.location}

+ return ( +
+ {/* Overlay */} +
+
+ {/* Title & Subheading */} +

+ Artifact Shop +

+

+ Discover extraordinary historical artifacts and collectibles from major seismic events from around the world - now available for purchase. +

-
- -
-
-
- ); - } + {/* Artifact Grid */} +
{/* gap-10 for more spacing */} + {currentArtifacts.map((artifact) => ( + + ))} +
- // ArtefactCard Component - function ArtefactCard({ artefact }: { artefact: Artefact }) { - return ( -
setSelectedArtefact(artefact)} // Opens modal - > - {artefact.name} -
-

{artefact.name}

-

{artefact.location}

-

- {currencyTickers[selectedCurrency]} - {convertPrice(artefact.price, selectedCurrency)} -

-
-
- ); - } + {/* Pagination Footer */} +
+ +

{currentPage}

+ +
+
- return ( -
- {/* Main Content */} -
- {/* Artefact Grid */} -
- {currentArtefacts.map((artefact) => ( - - ))} -
-
- - {/* Pagination Footer */} -
- -

{currentPage}

- -
- {/* Modal */} - {selectedArtefact && } -
- ); -} + {/* Modal */} + {selectedArtifact && } +
+ ); +} \ No newline at end of file diff --git a/src/app/the-team/page.tsx b/src/app/the-team/page.tsx index 3c5f5a2..9be1b60 100644 --- a/src/app/the-team/page.tsx +++ b/src/app/the-team/page.tsx @@ -1,5 +1,4 @@ "use client"; - const teamMembers = [ { name: "Tim Howitz", @@ -30,41 +29,47 @@ const teamMembers = [ image: "/Lukeshanthescientist.PNG", }, ]; - export default function Page() { - return ( -
- {/* Header */} -

Meet Our Team

-

- Our world-class scientists drive innovation across the globe. Meet our department heads below. -

- - {/* Team Members Section */} -
- {teamMembers.map((member, index) => ( -
- {/* Image */} -
-
-
- {member.name} -
-
-
- - {/* Text Content */} -
-

{member.name}

-

{member.title}

-

{member.description}

-
-
- ))} -
-
- ); -} + return ( +
+ {/* Overlay */} +
+ {/* Header */} +
+

+ Meet Our Team +

+

+ Our world-class scientists and engineers drive innovation across the globe. Meet our four department heads: +

+
+ {/* Team Members Section */} +
+ {teamMembers.map((member, index) => ( +
+ {/* Image */} +
+
+
+ {member.name} +
+
+
+ {/* Text Content */} +
+

{member.name}

+

{member.title}

+

{member.description}

+
+
+ ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/app/warehouse/page.tsx b/src/app/warehouse/page.tsx index b7f165d..dcd9ede 100644 --- a/src/app/warehouse/page.tsx +++ b/src/app/warehouse/page.tsx @@ -1,12 +1,12 @@ "use client"; -import { useState, useMemo } from "react"; -import { FaCalendarPlus, FaWarehouse, FaCartShopping } from "react-icons/fa6"; -import { IoFilter, IoFilterCircleOutline, IoFilterOutline, IoToday } from "react-icons/io5"; -import { FaTimes } from "react-icons/fa"; -import { SetStateAction, Dispatch } from "react"; -// import type { Artefact } from "@prisma/client"; +import { Dispatch, SetStateAction, useMemo, useState } from 'react'; +import { FaTimes } from 'react-icons/fa'; +import { FaCalendarPlus, FaCartShopping, FaWarehouse } from 'react-icons/fa6'; +import { IoFilter, IoFilterCircleOutline, IoFilterOutline, IoToday } from 'react-icons/io5'; -interface Artefact { +// import type { Artifact } from "@prisma/client"; + +interface Artifact { id: number; name: string; description: string; @@ -18,8 +18,8 @@ interface Artefact { dateAdded: string; } -// Warehouse Artefacts Data -const warehouseArtefacts: Artefact[] = [ +// Warehouse Artifacts Data +const warehouseArtifacts: Artifact[] = [ { id: 1, name: "Solidified Lava Chunk", @@ -140,14 +140,14 @@ function FilterInput({ } // Table Component -function ArtefactTable({ - artefacts, +function ArtifactTable({ + artifacts, filters, setFilters, - setEditArtefact, + setEditArtifact, clearSort, }: { - artefacts: Artefact[]; + artifacts: Artifact[]; filters: Record; setFilters: Dispatch< SetStateAction<{ @@ -162,15 +162,15 @@ function ArtefactTable({ dateAdded: string; }> >; - setEditArtefact: (artefact: Artefact) => void; + setEditArtifact: (artifact: Artifact) => void; clearSort: () => void; }) { const [sortConfig, setSortConfig] = useState<{ - key: keyof Artefact; + key: keyof Artifact; direction: "asc" | "desc"; } | null>(null); - const handleSort = (key: keyof Artefact) => { + const handleSort = (key: keyof Artifact) => { setSortConfig((prev) => { if (!prev || prev.key !== key) { return { key, direction: "asc" }; @@ -186,9 +186,9 @@ function ArtefactTable({ clearSort(); }; - const sortedArtefacts = useMemo(() => { - if (!sortConfig) return artefacts; - const sorted = [...artefacts].sort((a, b) => { + const sortedArtifacts = useMemo(() => { + if (!sortConfig) return artifacts; + const sorted = [...artifacts].sort((a, b) => { const aValue = a[sortConfig.key]; const bValue = b[sortConfig.key]; if (aValue < bValue) return sortConfig.direction === "asc" ? -1 : 1; @@ -196,9 +196,9 @@ function ArtefactTable({ return 0; }); return sorted; - }, [artefacts, sortConfig]); + }, [artifacts, sortConfig]); - const columns: { label: string; key: keyof Artefact; width: string }[] = [ + const columns: { label: string; key: keyof Artifact; width: string }[] = [ { label: "ID", key: "id", width: "5%" }, { label: "Name", key: "name", width: "12%" }, { label: "Earthquake ID", key: "earthquakeId", width: "10%" }, @@ -217,7 +217,7 @@ function ArtefactTable({ {columns.map(({ label, key, width }) => (
-
handleSort(key as keyof Artefact)}> +
handleSort(key as keyof Artifact)}>
{label}
@@ -250,11 +250,11 @@ function ArtefactTable({ - {sortedArtefacts.map((artefact) => ( + {sortedArtifacts.map((artifact) => ( setEditArtefact(artefact)} + onClick={() => setEditArtifact(artifact)} > {columns.map(({ key, width }) => ( {key === "isRequired" - ? artefact.isRequired + ? artifact.isRequired ? "Yes" : "No" : key === "isSold" - ? artefact.isSold + ? artifact.isSold ? "Yes" : "No" : key === "isCollected" - ? artefact.isCollected + ? artifact.isCollected ? "Yes" : "No" - : artefact[key]} + : artifact[key]} ))} @@ -284,7 +284,7 @@ function ArtefactTable({ ); } -// Modal Component for Logging Artefact +// Modal Component for Logging Artifact function LogModal({ onClose }: { onClose: () => void }) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); @@ -312,7 +312,7 @@ function LogModal({ onClose }: { onClose: () => void }) { alert(`Logged ${name} to storage: ${storageLocation}`); onClose(); } catch { - setError("Failed to log artefact. Please try again."); + setError("Failed to log artifact. Please try again."); } finally { setIsSubmitting(false); } @@ -324,7 +324,7 @@ function LogModal({ onClose }: { onClose: () => void }) { onClick={handleOverlayClick} >
-

Log New Artefact

+

Log New Artifact

{error &&

{error}

}
void }) { value={name} onChange={(e) => setName(e.target.value)} className="w-full p-2 border border-neutral-300 rounded-md placeholder-neutral-400 focus:ring-2 focus:ring-blue-500" - aria-label="Artefact Name" + aria-label="Artifact Name" disabled={isSubmitting} />