shop extras

This commit is contained in:
Emily Neighbour 2025-05-12 21:38:46 +01:00
parent 52f17d5a00
commit 579c1c205a
2 changed files with 194 additions and 139 deletions

View File

@ -1,10 +1,10 @@
"use client"; "use client";
import Image from 'next/image'; import Image from "next/image";
import { Dispatch, SetStateAction, useCallback, useState } from 'react'; import { Dispatch, SetStateAction, useCallback, useState } from "react";
import Artifact from '@appTypes/Artifact'; import Artifact from "@appTypes/Artifact";
import { Currency } from '@appTypes/StoreModel'; import { Currency } from "@appTypes/StoreModel";
import { useStoreState } from '@hooks/store'; import { useStoreState } from "@hooks/store";
// Artifacts Data // Artifacts Data
const artifacts: Artifact[] = [ const artifacts: Artifact[] = [
@ -13,6 +13,9 @@ const artifacts: Artifact[] = [
name: "Golden Scarab", name: "Golden Scarab",
description: "An ancient Egyptian artifact symbolizing rebirth.", description: "An ancient Egyptian artifact symbolizing rebirth.",
location: "Cairo, Egypt", location: "Cairo, Egypt",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact1.jpg", image: "/artifact1.jpg",
price: 150, price: 150,
}, },
@ -21,6 +24,9 @@ const artifacts: Artifact[] = [
name: "Aztec Sunstone", name: "Aztec Sunstone",
description: "A replica of the Aztec calendar (inscriptions intact).", description: "A replica of the Aztec calendar (inscriptions intact).",
location: "Peru", location: "Peru",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact2.jpg", image: "/artifact2.jpg",
price: 200, price: 200,
}, },
@ -29,6 +35,9 @@ const artifacts: Artifact[] = [
name: "Medieval Chalice", name: "Medieval Chalice",
description: "Used by royalty in medieval ceremonies.", description: "Used by royalty in medieval ceremonies.",
location: "Cambridge, England", location: "Cambridge, England",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact3.jpg", image: "/artifact3.jpg",
price: 120, price: 120,
}, },
@ -37,6 +46,9 @@ const artifacts: Artifact[] = [
name: "Roman Coin", name: "Roman Coin",
description: "An authentic Roman coin from the 2nd century CE.", description: "An authentic Roman coin from the 2nd century CE.",
location: "Rome, Italy", location: "Rome, Italy",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact4.jpg", image: "/artifact4.jpg",
price: 80, price: 80,
}, },
@ -45,6 +57,9 @@ const artifacts: Artifact[] = [
name: "Samurai Mask", name: "Samurai Mask",
description: "Replica of Japanese Samurai battle masks.", description: "Replica of Japanese Samurai battle masks.",
location: "Tokyo, Japan", location: "Tokyo, Japan",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact5.jpg", image: "/artifact5.jpg",
price: 300, price: 300,
}, },
@ -53,6 +68,9 @@ const artifacts: Artifact[] = [
name: "Ancient Greek Vase", name: "Ancient Greek Vase",
description: "Depicts Greek mythology, found in the Acropolis.", description: "Depicts Greek mythology, found in the Acropolis.",
location: "Athens, Greece", location: "Athens, Greece",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact6.jpg", image: "/artifact6.jpg",
price: 250, price: 250,
}, },
@ -61,6 +79,9 @@ const artifacts: Artifact[] = [
name: "Incan Pendant", name: "Incan Pendant",
description: "Represents the Sun God Inti.", description: "Represents the Sun God Inti.",
location: "India", location: "India",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact7.jpg", image: "/artifact7.jpg",
price: 175, price: 175,
}, },
@ -69,6 +90,9 @@ const artifacts: Artifact[] = [
name: "Persian Carpet Fragment", name: "Persian Carpet Fragment",
description: "Ancient Persian artistry.", description: "Ancient Persian artistry.",
location: "Petra, Jordan", location: "Petra, Jordan",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact8.jpg", image: "/artifact8.jpg",
price: 400, price: 400,
}, },
@ -77,6 +101,9 @@ const artifacts: Artifact[] = [
name: "Stone Buddha", name: "Stone Buddha",
description: "Authentic stone Buddha carving.", description: "Authentic stone Buddha carving.",
location: "India", location: "India",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact9.jpg", image: "/artifact9.jpg",
price: 220, price: 220,
}, },
@ -85,6 +112,9 @@ const artifacts: Artifact[] = [
name: "Victorian Brooch", name: "Victorian Brooch",
description: "A beautiful Victorian-era brooch with a ruby centre.", description: "A beautiful Victorian-era brooch with a ruby centre.",
location: "Oxford, England", location: "Oxford, England",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact10.jpg", image: "/artifact10.jpg",
price: 150, price: 150,
}, },
@ -93,6 +123,9 @@ const artifacts: Artifact[] = [
name: "Ancient Scroll", name: "Ancient Scroll",
description: "A mysterious scroll from ancient times.", description: "A mysterious scroll from ancient times.",
location: "Madrid, Spain", location: "Madrid, Spain",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact11.jpg", image: "/artifact11.jpg",
price: 500, price: 500,
}, },
@ -101,6 +134,9 @@ const artifacts: Artifact[] = [
name: "Ming Dynasty Porcelain", name: "Ming Dynasty Porcelain",
description: "Porcelain from China's Ming Dynasty.", description: "Porcelain from China's Ming Dynasty.",
location: "Beijing, China", location: "Beijing, China",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact12.jpg", image: "/artifact12.jpg",
price: 300, price: 300,
}, },
@ -109,6 +145,9 @@ const artifacts: Artifact[] = [
name: "African Tribal Mask", name: "African Tribal Mask",
description: "A unique tribal mask from Africa.", description: "A unique tribal mask from Africa.",
location: "Nigeria", location: "Nigeria",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact13.jpg", image: "/artifact13.jpg",
price: 250, price: 250,
}, },
@ -117,6 +156,9 @@ const artifacts: Artifact[] = [
name: "Crystal Skull", name: "Crystal Skull",
description: "A mystical pre-Columbian artifact.", description: "A mystical pre-Columbian artifact.",
location: "Colombia", location: "Colombia",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact14.jpg", image: "/artifact14.jpg",
price: 1000, price: 1000,
}, },
@ -125,159 +167,169 @@ const artifacts: Artifact[] = [
name: "Medieval Armor Fragment", name: "Medieval Armor Fragment",
description: "A fragment of medieval armor.", description: "A fragment of medieval armor.",
location: "Normandy, France", location: "Normandy, France",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact15.jpg", image: "/artifact15.jpg",
price: 400, price: 400,
}, },
{ {
id: 16, id: 16,
name: "Medieval Helmet Fragment", name: "Medieval Helmet Fragment",
description: "A fragment of a medieval helmet.", description: "A fragment of a medieval helmet.",
location: "Normandy, France", location: "Normandy, France",
earthquakeID: "h",
observatory: "jhd",
dateReleased: "12/02/2025",
image: "/artifact16.jpg", image: "/artifact16.jpg",
price: 500, price: 500,
}, },
]; ];
export default function Shop() { export default function Shop() {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const [selectedArtifact, setSelectedArtifact] = useState<Artifact | null>(null); const [selectedArtifact, setSelectedArtifact] = useState<Artifact | null>(null);
const artifactsPerPage = 12; const artifactsPerPage = 12;
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 selectedCurrency = useStoreState((state) => state.currency.selectedCurrency);
const conversionRates = useStoreState((state) => state.currency.conversionRates); const conversionRates = useStoreState((state) => state.currency.conversionRates);
const currencyTickers = useStoreState((state) => state.currency.tickers); const currencyTickers = useStoreState((state) => state.currency.tickers);
const convertPrice = useCallback( const convertPrice = useCallback((price: number, currency: Currency) => (price * conversionRates[currency]).toFixed(2), []);
(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);
} }
}; };
const handlePreviousPage = () => { const handlePreviousPage = () => {
if (currentPage > 1) { if (currentPage > 1) {
setCurrentPage((prev) => prev - 1); setCurrentPage((prev) => prev - 1);
} }
}; };
function Modal({ artifact }: { artifact: Artifact }) { function Modal({ artifact }: { artifact: Artifact }) {
if (!artifact) return null; if (!artifact) return null;
const handleOverlayClick = (e: { target: any; currentTarget: any }) => { const handleOverlayClick = (e: { target: any; currentTarget: any }) => {
if (e.target === e.currentTarget) { if (e.target === e.currentTarget) {
setSelectedArtifact(null); setSelectedArtifact(null);
} }
}; };
return ( return (
<div <div
className="fixed inset-0 bg-neutral-900 bg-opacity-50 flex justify-center items-center z-50" className="fixed inset-0 bg-neutral-900 bg-opacity-50 flex justify-center items-center z-50"
onClick={handleOverlayClick} onClick={handleOverlayClick}
> >
<div className="bg-white rounded-xl shadow-2xl max-w-lg w-full p-6"> <div className="bg-white rounded-xl shadow-2xl max-w-lg w-full p-6">
<h3 className="text-2xl font-bold mb-4">{artifact.name}</h3> <h3 className="text-2xl font-bold mb-4">{artifact.name}</h3>
<Image <Image
height={5000} height={5000}
width={5000} width={5000}
src={artifact.image} src={artifact.image}
alt={artifact.name} alt={artifact.name}
className="w-full h-64 object-cover rounded-md" className="w-full h-64 object-cover rounded-md"
/> />
<p className="text-xl font-bold"> <p className="text-xl font-bold">
{currencyTickers[selectedCurrency]} {currencyTickers[selectedCurrency]}
{convertPrice(artifact.price, selectedCurrency)} {convertPrice(artifact.price, selectedCurrency)}
</p> </p>
<p className="text-neutral-600 mt-2">{artifact.description}</p> <p className="text-neutral-600 mt-2">{artifact.description}</p>
<p className="text-neutral-500 font-bold mt-1">Location: {artifact.location}</p> <p className="text-neutral-500 font-bold mt-1">Location: {artifact.location}</p>
<div className="flex justify-end gap-4 mt-4 mr-2"> <p className="text-neutral-500 mb-2">{artifact.earthquakeID}</p>
<button <p className="text-neutral-500 mb-2">{artifact.observatory}</p>
onClick={() => alert("Purchased Successfully!")} <p className="text-neutral-500 mb-2">{artifact.dateReleased}</p>
className="px-10 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700" <div className="flex justify-end gap-4 mt-4 mr-2">
> <button
Buy onClick={() => alert("Purchased Successfully!")}
</button> className="px-10 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
</div> >
</div> Buy
</div> </button>
); </div>
} </div>
</div>
);
}
function ArtifactCard({ artifact }: { artifact: Artifact }) { function ArtifactCard({ artifact }: { artifact: Artifact }) {
return ( return (
<div <div
className="flex flex-col bg-white shadow-md rounded-md overflow-hidden cursor-pointer hover:scale-105 transition-transform" className="flex flex-col bg-white shadow-md rounded-md overflow-hidden cursor-pointer hover:scale-105 transition-transform"
onClick={() => setSelectedArtifact(artifact)} onClick={() => setSelectedArtifact(artifact)}
> >
<img src={artifact.image} alt={artifact.name} className="w-full h-56 object-cover" /> <img src={artifact.image} alt={artifact.name} className="w-full h-56 object-cover" />
<div className="p-4"> <div className="p-4">
<h3 className="text-lg font-semibold">{artifact.name}</h3> <h3 className="text-lg font-semibold">{artifact.name}</h3>
<p className="text-neutral-500 mb-2">{artifact.location}</p> <p className="text-neutral-500 mb-2">{artifact.location}</p>
<p className="text-black font-bold text-md mt-2"> <p className="text-neutral-500 mb-2">{artifact.earthquakeID}</p>
{currencyTickers[selectedCurrency]} <p className="text-black font-bold text-md mt-2">
{convertPrice(artifact.price, selectedCurrency)} {currencyTickers[selectedCurrency]}
</p> {convertPrice(artifact.price, selectedCurrency)}
</div> </p>
</div> </div>
); </div>
} );
}
return ( return (
<div <div
className="min-h-screen relative flex flex-col" className="min-h-screen relative flex flex-col"
style={{ style={{
backgroundImage: "url('/artifacts.jpg')", backgroundImage: "url('/artifacts.jpg')",
backgroundSize: 'cover', backgroundSize: "cover",
backgroundPosition: 'center' backgroundPosition: "center",
}} }}
> >
{/* Overlay */} {/* Overlay */}
<div className="absolute inset-0 bg-black bg-opacity-50 z-0"></div> <div className="absolute inset-0 bg-black bg-opacity-50 z-0"></div>
<div className="relative z-10 flex flex-col items-center w-full px-2 py-12"> <div className="relative z-10 flex flex-col items-center w-full px-2 py-12">
{/* Title & Subheading */} {/* Title & Subheading */}
<h1 className="text-4xl md:text-4xl font-bold text-center text-white mb-2 tracking-tight drop-shadow-lg"> <h1 className="text-4xl md:text-4xl font-bold text-center text-white mb-2 tracking-tight drop-shadow-lg">
Artifact Shop Artifact Shop
</h1> </h1>
<p className="text-lg md:text-xl text-center text-white mb-10 drop-shadow-md max-w-2xl"> <p className="text-lg md:text-xl text-center text-white mb-10 drop-shadow-md max-w-2xl">
Discover extraordinary historical artifacts and collectibles from major seismic events from around the world - now available for purchase. Discover extraordinary historical artifacts and collectibles from major seismic events from around the world - now
</p> available for purchase.
</p>
{/* Artifact Grid */} {/* Artifact Grid */}
<div className="w-full max-w-7xl grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-10 p-2"> {/* gap-10 for more spacing */} <div className="w-full max-w-7xl grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-10 p-2">
{currentArtifacts.map((artifact) => ( {" "}
<ArtifactCard key={artifact.id} artifact={artifact} /> {/* gap-10 for more spacing */}
))} {currentArtifacts.map((artifact) => (
</div> <ArtifactCard key={artifact.id} artifact={artifact} />
))}
</div>
{/* Pagination Footer */} {/* Pagination Footer */}
<footer className="mt-10 bg-white bg-opacity-90 border-neutral-300 py-3 text-center flex justify-center items-center w-100 max-w-7xl rounded-lg"> <footer className="mt-10 bg-white bg-opacity-90 border-neutral-300 py-3 text-center flex justify-center items-center w-100 max-w-7xl rounded-lg">
<button <button
onClick={handlePreviousPage} onClick={handlePreviousPage}
disabled={currentPage === 1} disabled={currentPage === 1}
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 ${
currentPage === 1 ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600" currentPage === 1 ? "opacity-50 cursor-not-allowed" : "hover:bg-blue-600"
}`} }`}
> >
&larr; Previous &larr; Previous
</button> </button>
<p className="mx-3 text-lg font-bold">{currentPage}</p> <p className="mx-3 text-lg font-bold">{currentPage}</p>
<button <button
onClick={handleNextPage} onClick={handleNextPage}
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 &rarr; Next &rarr;
</button> </button>
</footer> </footer>
</div> </div>
{/* Modal */} {/* Modal */}
{selectedArtifact && <Modal artifact={selectedArtifact} />} {selectedArtifact && <Modal artifact={selectedArtifact} />}
</div> </div>
); );
} }

View File

@ -4,6 +4,9 @@ interface Artifact {
name: string; name: string;
description: string; description: string;
location: string; location: string;
earthquakeID: string;
observatory: string;
dateReleased: string;
image: string; image: string;
price: number; price: number;
} }