Files
MineGeo/app/page.tsx

252 lines
8.6 KiB
TypeScript

import Link from "next/link";
import Image from "next/image";
import {
ArrowRight,
Compass,
MapPin,
Timer,
Blocks,
Upload,
} from "lucide-react";
import { Header, Footer } from "@/components/Shell";
import { MapCard } from "@/components/MapCard";
import { catalog } from "@/lib/catalog";
export const dynamic = "force-dynamic";
export default async function Home() {
const maps = await catalog();
const servers = new Set(maps.map((map) => map.server_id)).size;
const locations = maps.reduce((total, map) => total + map.panorama_count, 0);
return (
<>
<Header />
<main className="home-page">
<section className="hero">
<div className="hero-copy">
<div className="status-label">
<span className="live-dot" /> MINECRAFT · EXPLORE · GUESS
</div>
<h1>
Guess where you are
<br />
in a <span>blocky world.</span>
</h1>
<p>
Drop into a random corner of a Minecraft world.
<br className="desktop" /> Look around, spot the landmarks, and
place your pin
<br className="desktop" /> before the clock runs out.
</p>
<div className="hero-actions">
<Link href="/servers" className="button primary">
Start exploring <ArrowRight size={18} />
</Link>
<span className="hero-mode-note">3 difficulty levels</span>
</div>
<div className="hero-notes">
<span className="mode-dots" aria-hidden="true">
<i />
<i />
<i />
<i />
</span>
<span>Five rounds. No roads. Just your instincts.</span>
</div>
</div>
<div className="hero-art" aria-label="Minecraft surface map">
<div className="map-art-frame">
<Image
src={maps[0]?.mapImage || "/images/example-map.webp"}
alt="A Minecraft surface map framed in parchment"
width={1024}
height={1024}
priority
unoptimized
/>
<span className="square-pin pin-green" />
<span className="square-pin pin-red" />
<span className="square-pin pin-gold" />
</div>
<span className="compass-art" aria-hidden="true">
<Compass size={43} />
<small>N</small>
</span>
<div className="floating-tag">
<MapPin size={14} />
<strong>X: ??? Z: ???</strong>
<span>Overworld</span>
</div>
</div>
</section>
<div className="grass-divider" aria-hidden="true">
<span />
</div>
<section className="world-stats" aria-label="Community statistics">
<div>
<i className="stat-cube green-cube" />
<div>
<strong>{servers.toLocaleString()}</strong>
<span>COMMUNITY SERVERS</span>
</div>
</div>
<div>
<i className="stat-cube gold-cube" />
<div>
<strong>{maps.length.toLocaleString()}</strong>
<span>PUBLISHED MAPS</span>
</div>
</div>
<div>
<i className="stat-cube cyan-cube" />
<div>
<strong>{locations.toLocaleString()}</strong>
<span>PLACES TO DISCOVER</span>
</div>
</div>
<div>
<i className="stat-cube red-cube" />
<div>
<strong>5 rounds</strong>
<span>EVERY EXPEDITION</span>
</div>
</div>
</section>
<section className="section catalog-section">
<div className="section-heading">
<div>
<span className="eyebrow">COMMUNITY MAPS</span>
<h2>Where will you go today?</h2>
</div>
<Link className="text-link" href="/servers">
All {maps.length} maps <ArrowRight size={15} />
</Link>
</div>
{maps.length ? (
<div className="cards-grid">
{maps.slice(0, 4).map((map, index) => (
<MapCard map={map} index={index} key={map.id} />
))}
</div>
) : (
<div className="first-map-panel">
<div className="empty-map-art" aria-hidden="true">
<Image
src="/images/example-map.webp"
alt=""
width={180}
height={180}
unoptimized
/>
<span>YOUR WORLD HERE</span>
</div>
<div>
<span className="eyebrow">
THE FIRST EXPEDITION STARTS WITH YOU
</span>
<h3>Put your server on the map.</h3>
<p>
No community maps have been published yet. Export your world
with the server plugin, or capture five locations with the
Fabric mod. Preview your map and submit it for review.
</p>
<Link className="button primary" href="/downloads">
<Upload size={16} /> Get the capture tools
</Link>
</div>
</div>
)}
</section>
<section className="section home-panels">
<article className="home-panel">
<span className="eyebrow">HOW TO PLAY</span>
<h2>Know your blocks?</h2>
<ol className="play-steps">
<li>
<span>01</span>
<div>
<h3>Pick your world</h3>
<p>Choose a community server, map, and difficulty.</p>
</div>
</li>
<li>
<span>02</span>
<div>
<h3>Look for the clues</h3>
<p>
Rotate the panorama. Find builds, biomes, and landmarks.
</p>
</div>
</li>
<li>
<span>03</span>
<div>
<h3>Make your guess</h3>
<p>Drop a pin on the map. Closer guesses earn more points.</p>
</div>
</li>
</ol>
<Link className="text-link" href="/about">
Read the field guide <ArrowRight size={14} />
</Link>
</article>
<article className="home-panel">
<span className="eyebrow">CHOOSE YOUR CHALLENGE</span>
<h2>One world. Three ways.</h2>
<div className="mode-list">
{[
{
name: "Easy",
time: "180 seconds",
detail: "Take your time. Rotate and zoom.",
color: "green",
},
{
name: "Normal",
time: "90 seconds",
detail: "Trust your eye. Rotate and zoom.",
color: "gold",
},
{
name: "Hard",
time: "30 seconds",
detail: "Think fast. No zoom allowed.",
color: "red",
},
].map((mode) => (
<Link href="/servers" className="mode-row" key={mode.name}>
<i className={`stat-cube ${mode.color}-cube`} />
<div>
<h3>
{mode.name}
<span>
<Timer size={12} />
{mode.time}
</span>
</h3>
<p>{mode.detail}</p>
</div>
<ArrowRight size={15} />
</Link>
))}
</div>
<p className="mode-footnote">
Up to 5,000 points per round. How close can you get?
</p>
</article>
</section>
<section className="section contribute-banner">
<Blocks size={38} />
<div>
<span className="eyebrow">BUILT BY THE COMMUNITY</span>
<h2>Your world deserves to be explored.</h2>
<p>Share your builds and your favorite corners of Minecraft.</p>
</div>
<Link href="/downloads" className="button">
Create a map <ArrowRight size={16} />
</Link>
</section>
</main>
<Footer />
</>
);
}