adding easter eggs to the contact page

This commit is contained in:
Emily Neighbour 2025-05-31 21:29:50 +01:00
parent 4438953fab
commit ba99d6a2be
3 changed files with 166 additions and 24 deletions

BIN
public/pulsatingMap.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -1,28 +1,150 @@
"use client"; "use client";
import Image from "next/image"; import Image from "next/image";
import React, { useState } from "react"; import React, { useCallback, useEffect, useRef, useState } from "react";
import BottomFooter from "@components/BottomFooter"; import BottomFooter from "@components/BottomFooter";
const ContactUs = () => { const ContactUs = () => {
const [formData, setFormData] = useState({ // Form/modal
name: "", const [formData, setFormData] = useState({ name: "", email: "", message: "" });
email: "", const [isModalOpen, setIsModalOpen] = useState(false);
message: "",
});
const handleChange = (e: { target: { name: any; value: any } }) => { // 1. Lava (Instagram): state & timer
const [lavaActive, setLavaActive] = useState(false);
const lavaTimeout = useRef<NodeJS.Timeout | null>(null);
// 2. Pulsating Map (Facebook): state & timer
const [pulsatingActive, setPulsatingActive] = useState(false);
const pulsatingTimeout = useRef<NodeJS.Timeout | null>(null);
// 3. Shake (LinkedIn): state & timer
const [shaking, setShaking] = useState(false);
const shakeTimeout = useRef<NodeJS.Timeout | null>(null);
// 4. Crack & Collapse (X): state & timer
const [showCracks, setShowCracks] = useState(false);
const [collapse, setCollapse] = useState(false);
const crackTimeout = useRef<NodeJS.Timeout | null>(null);
// Lava flood handler (top-down flood)
const handleInstagramClick = useCallback((e: React.MouseEvent) => {
e.preventDefault();
setLavaActive(true);
if (lavaTimeout.current) clearTimeout(lavaTimeout.current);
lavaTimeout.current = setTimeout(() => setLavaActive(false), 2000);
}, []);
// Pulsating Map handler
const handleFacebookClick = useCallback((e: React.MouseEvent) => {
e.preventDefault();
setPulsatingActive(true);
if (pulsatingTimeout.current) clearTimeout(pulsatingTimeout.current);
pulsatingTimeout.current = setTimeout(() => setPulsatingActive(false), 2000);
}, []);
// LinkedIn shake handler
const handleLinkedInClick = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
if (shaking) return;
setShaking(true);
const body = document.body;
body.classList.remove("shake-screen");
void body.offsetWidth;
body.classList.add("shake-screen");
if (shakeTimeout.current) clearTimeout(shakeTimeout.current);
shakeTimeout.current = setTimeout(() => {
setShaking(false);
body.classList.remove("shake-screen");
}, 1000);
},
[shaking]
);
// X (crack and collapse) handler
const handleXClick = useCallback((e: React.MouseEvent) => {
e.preventDefault();
setShowCracks(true);
if (crackTimeout.current) clearTimeout(crackTimeout.current);
crackTimeout.current = setTimeout(() => {
setCollapse(true);
setTimeout(() => {
setShowCracks(false);
setCollapse(false);
}, 1500);
}, 1000);
}, []);
// Clean up timeouts and shake class
useEffect(() => {
return () => {
if (lavaTimeout.current) clearTimeout(lavaTimeout.current);
if (pulsatingTimeout.current) clearTimeout(pulsatingTimeout.current);
if (shakeTimeout.current) clearTimeout(shakeTimeout.current);
if (crackTimeout.current) clearTimeout(crackTimeout.current);
document.body.classList.remove("shake-screen");
};
}, []);
// Form handlers
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
}; };
const handleSubmit = (e: React.FormEvent) => {
const handleSubmit = (e: { preventDefault: () => void }) => {
e.preventDefault(); e.preventDefault();
console.log("Form submitted with data:", formData); console.log("Form submitted with data:", formData);
alert("Thank you for reaching out! We will get back to you soon."); setIsModalOpen(true);
setFormData({ name: "", email: "", message: "" }); setFormData({ name: "", email: "", message: "" });
}; };
return ( return (
<div className="h-full relative text-white border border-black overflow-hidden"> <div className="h-full relative text-white border border-black overflow-hidden">
{/* Lava Flood Overlay */}
{lavaActive && (
<div className="lava-flood-overlay lava-active fixed inset-0 z-[60] flex items-center justify-center">
<img
src="/lava.jpg"
alt="Lava flood"
draggable={false}
className="w-full h-full object-cover opacity-80 pointer-events-none"
/>
</div>
)}
{/* Pulsating Overlay */}
{pulsatingActive && (
<div className="pulsating-map-overlay pulsating-active fixed inset-0 z-[60] flex items-center justify-center">
<img
src="/pulsatingMap.jpg"
alt="Pulsating Map"
draggable={false}
className="w-full h-full object-cover opacity-80 pointer-events-none"
/>
</div>
)}
{/* Crack & Collapse Overlay */}
{(showCracks || collapse) && (
<div
className={`crack-overlay fixed inset-0 z-[60] flex items-center justify-center${collapse ? " crack-collapse" : ""}`}
>
<img className="crack crack1 absolute w-3/4" src="/crack1.png" alt="" />
<img className="crack crack2 absolute w-3/4" src="/crack2.png" alt="" />
</div>
)}
{/* Modal: Submit Success */}
{isModalOpen && (
<div className="fixed inset-0 z-[70] flex items-center justify-center bg-black/60">
<div className="bg-white rounded-lg shadow-lg p-8 max-w-sm w-full text-center">
<h2 className="text-2xl font-bold mb-4 text-neutral-800">Thank You!</h2>
<p className="text-neutral-700 mb-6">Thank you for submitting a message. We will be responding via our email.</p>
<button
className="bg-blue-600 text-white px-5 py-2 rounded-lg font-medium hover:bg-blue-700 transition"
onClick={() => setIsModalOpen(false)}
>
Close
</button>
</div>
</div>
)}
<Image <Image
height={5000} height={5000}
width={5000} width={5000}
@ -30,7 +152,6 @@ const ContactUs = () => {
className="border border-neutral-300 absolute z-10 -top-20" className="border border-neutral-300 absolute z-10 -top-20"
src="/tsunamiWaves.jpg" src="/tsunamiWaves.jpg"
/> />
{/* Overlay for readability */} {/* Overlay for readability */}
<div className="absolute overflow-hidden w-full h-full bg-gradient-to-b from-black/80 via-black/40 to-black/20 flex flex-col items-center z-20"> <div className="absolute overflow-hidden w-full h-full bg-gradient-to-b from-black/80 via-black/40 to-black/20 flex flex-col items-center z-20">
{/* Container */} {/* Container */}
@ -41,8 +162,6 @@ const ContactUs = () => {
Have questions or concerns about earthquakes, observatories or artefacts? Contact us via phone, email, social media or Have questions or concerns about earthquakes, observatories or artefacts? Contact us via phone, email, social media or
using the form below with the relevant contact details. using the form below with the relevant contact details.
</p> </p>
{/* 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-neutral-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">
@ -62,7 +181,6 @@ const ContactUs = () => {
required required
/> />
</div> </div>
<div className="mb-4"> <div className="mb-4">
<label htmlFor="email" className="block text-neutral-700 font-medium mb-2"> <label htmlFor="email" className="block text-neutral-700 font-medium mb-2">
Email Email
@ -78,7 +196,6 @@ const ContactUs = () => {
required required
/> />
</div> </div>
<div className="mb-4"> <div className="mb-4">
<label htmlFor="message" className="block text-neutral-700 font-medium mb-2"> <label htmlFor="message" className="block text-neutral-700 font-medium mb-2">
Message Message
@ -95,7 +212,6 @@ const ContactUs = () => {
style={{ resize: "none" }} style={{ resize: "none" }}
/> />
</div> </div>
<button <button
type="submit" type="submit"
className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition duration-200" className="w-full bg-blue-600 text-white py-3 rounded-lg font-medium hover:bg-blue-700 transition duration-200"
@ -104,7 +220,6 @@ const ContactUs = () => {
</button> </button>
</form> </form>
</div> </div>
{/* Contact Details Section */} {/* Contact Details Section */}
<div className="w-[45%] bg-white bg-opacity-90 text-neutral-800 rounded-lg shadow-lg p-6"> <div className="w-[45%] bg-white bg-opacity-90 text-neutral-800 rounded-lg shadow-lg p-6">
<h2 className="text-2xl font-bold text-neutral-800 mb-4">Get in Touch</h2> <h2 className="text-2xl font-bold text-neutral-800 mb-4">Get in Touch</h2>
@ -120,23 +235,51 @@ const ContactUs = () => {
</div> </div>
<div className="mb-4"> <div className="mb-4">
<h3 className="text-neutral-700 font-bold font-large">Address</h3> <h3 className="text-neutral-700 font-bold font-large">Address</h3>
<p className="text-neutral-600 font-medium">1 Swentown Row, Greenwich, London, SE3 0FQ</p> <p className="text-neutral-600 font-medium">1 Sweentown Row, Greenwich, London, SE3 0FQ</p>
</div> </div>
<h2 className="text-xl font-bold text-neutral-800 mb-4 mt-6">Follow Us</h2> <h2 className="text-xl font-bold text-neutral-800 mb-4 mt-6">Follow Us</h2>
<div className="flex justify-around items-center"> <div className="flex justify-around items-center">
<a href="#" className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"> {/* Instagram: Lava Flood */}
<a
href="#"
className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"
aria-label="Instagram"
onClick={handleInstagramClick}
style={{ cursor: "pointer" }}
>
<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 href="#" className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"> {/* Facebook: Pulsating Map */}
<a
href="#"
className="w-20 h-20 text-blue-600 hover:text-blue-800 transition duration-200"
aria-label="Facebook"
onClick={handleFacebookClick}
style={{ cursor: "pointer" }}
>
<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 href="#" className="w-20 h-20 p-4 text-blue-600 hover:text-blue-800 transition duration-200"> {/* X: Crack & Collapse */}
<a
href="#"
className="w-20 h-20 p-4 text-blue-600 hover:text-blue-800 transition duration-200"
aria-label="X"
onClick={handleXClick}
style={{ cursor: "pointer" }}
>
<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 href="#" className="w-20 h-20 flex items-center text-blue-600 hover:text-blue-800 transition duration-200"> {/* LinkedIn: Shake */}
<a
href="#"
className="w-20 h-20 flex items-center text-blue-600 hover:text-blue-800 transition duration-200"
aria-label="LinkedIn"
onClick={handleLinkedInClick}
style={{ cursor: "pointer" }}
>
<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>
@ -145,7 +288,7 @@ const ContactUs = () => {
</div> </div>
</div> </div>
</div> </div>
<BottomFooter /> <BottomFooter />
</div> </div>
); );
}; };

View File

@ -191,7 +191,6 @@ export default function Shop() {
// todo create receiving api route // todo create receiving api route
// todo handle sending to api route // todo handle sending to api route
// todo only ask for email if the user is not signed in
// todo (optional) add create account button to auto-fill email in sign-up modal // todo (optional) add create account button to auto-fill email in sign-up modal
const genOrder = () => "#" + Math.random().toString(36).substring(2, 10).toUpperCase(); const genOrder = () => "#" + Math.random().toString(36).substring(2, 10).toUpperCase();
setOrderNumber(genOrder()); setOrderNumber(genOrder());