map and table added

This commit is contained in:
Owen Rees
2023-05-23 16:57:07 +02:00
parent e0d363e612
commit 8c67681825
10 changed files with 184 additions and 60 deletions
+10 -5
View File
@@ -1,13 +1,18 @@
import type { NextApiRequest, NextApiResponse } from "next";
import clientPromise from "@/lib/mongodb"; import clientPromise from "@/lib/mongodb";
export async function GET(req: NextApiRequest, res: NextApiResponse) { /**
// TODO delete req / res ? * Tournament data API endpoint
* @route /api/tournaments
* @internal
*/
export async function GET() {
try { try {
const client = await clientPromise; const client = await clientPromise;
const db = client.db("tournamentsFranceDB"); const db = client.db("tournamentsFranceDB");
// const data = await db.collection("tournaments").find({}).toArray();
// converts date from string into a date and then orders by date /**
* Converts date from string into a date to allow ordering
*/
const data = await db const data = await db
.collection("tournaments") .collection("tournaments")
.aggregate([ .aggregate([
+5 -5
View File
@@ -1,9 +1,9 @@
import Image from 'next/image' import Layout from "@/components/Layout";
export default function Home() { export default function Home() {
return ( return (
<main className=""> <Layout>
<main className=""></main>
</main> </Layout>
) );
} }
+20 -34
View File
@@ -1,16 +1,22 @@
import dynamic from "next/dynamic";
import Layout from "@/components/Layout"; import Layout from "@/components/Layout";
import TournamentTable from "@/components/TournamentTable";
export interface TournamentType { /**
_id: string; * Imports the tournament map component, ensuring CSR only.
location: string; * @remarks SSR is not supported by react-leaflet
department: string; */
tournament: string; const TournamentMapNoSSR = dynamic(
url: string; () => import("../../components/TournamentMap"),
time_control: string; {
date: string; ssr: false,
coordinates: [number, number]; }
} );
/**
* Retrieves tournament data from /api/tournaments
* @remarks The result is cached for the revalidation period in seconds
*/
async function getTournaments() { async function getTournaments() {
const res = await fetch("http://localhost:3000/api/tournaments", { const res = await fetch("http://localhost:3000/api/tournaments", {
next: { revalidate: 300 }, next: { revalidate: 300 },
@@ -19,33 +25,13 @@ async function getTournaments() {
} }
export default async function Tournaments() { export default async function Tournaments() {
const data = await getTournaments(); const tournamentData = await getTournaments();
console.log(data); console.log(tournamentData);
const tournaments = data.map((t: TournamentType) => (
<tr key={t._id}>
<td>{t.date}</td>
<td>{t.location}</td>
<td>{t.tournament}</td>
<td>{t.time_control}</td>
</tr>
));
return ( return (
<Layout> <Layout>
<main className="w-full"> <TournamentMapNoSSR />
<table className="table-auto"> <TournamentTable tournamentData={tournamentData} />
<thead>
<tr>
<th>Date</th>
<th>Ville</th>
<th>Tournois</th>
<th>Cadence</th>
</tr>
</thead>
<tbody>{tournaments}</tbody>
</table>
</main>
</Layout> </Layout>
); );
} }
+2 -2
View File
@@ -6,9 +6,9 @@ export default function Layout({
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<> <div className="bg-white font-sans leading-normal tracking-normal">
<Navbar /> <Navbar />
{children} {children}
</> </div>
); );
} }
+44 -1
View File
@@ -1,3 +1,46 @@
import Link from "next/link";
export default function Navbar() { export default function Navbar() {
return <nav className="bg-gray-800 p-2 m-0 w-full">NAVBAR</nav>; return (
<nav className="bg-gray-800 p-2 mt-0 w-full">
<div className="container mx-auto flex flex-wrap items-center">
<div className="flex w-full md:w-1/2 justify-center md:justify-start text-white font-extrabold">
<Link
className="text-white no-underline hover:text-white hover:no-underline"
href="/"
>
<span className="text-2xl pl-2">Echecs France</span>
</Link>
</div>
<div className="flex w-full pt-2 content-center justify-between md:w-1/2 md:justify-end">
<ul className="list-reset text-white no-underline flex justify-between flex-1 md:flex-none items-center">
<li className="mr-3">
<Link
className="inline-block hover:text-gray-200 hover:text-underline py-2 px-4"
href="/tournois"
>
Tournois
</Link>
</li>
<li className="mr-3">
<Link
className="inline-block hover:text-gray-200 hover:text-underline py-2 px-4"
href="#"
>
About
</Link>
</li>
<li className="mr-3">
<Link
className="inline-block hover:text-gray-200 hover:text-underline py-2 px-4"
href="#"
>
Contact
</Link>
</li>
</ul>
</div>
</div>
</nav>
);
} }
+24
View File
@@ -0,0 +1,24 @@
"use client";
import "leaflet/dist/leaflet.css";
import { MapContainer, TileLayer, Marker } from "react-leaflet";
export default function TournamentMap() {
const center: number[] = [47.0844, 2.3964];
return (
<section id="tournament-map" className="">
<MapContainer
center={center}
zoom={5}
scrollWheelZoom={false}
style={{ height: "50vh" }}
>
<TileLayer
attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</MapContainer>
</section>
);
}
+42
View File
@@ -0,0 +1,42 @@
interface TournamentType {
_id: string;
location: string;
department: string;
tournament: string;
url: string;
time_control: string;
date: string;
coordinates: [number, number];
}
export default function TournamentTable({ tournamentData }) {
const tournaments = tournamentData.map((t: TournamentType) => (
<tr
className="border-b transition duration-300 ease-in-out hover:bg-neutral-100"
key={t._id}
>
<td>{t.date}</td>
<td>{t.location}</td>
<a href={t.url} target="_blank">
<td>{t.tournament}</td>
</a>
<td>{t.time_control}</td>
</tr>
));
return (
<section id="tournament-table" className="">
<table className="min-w-full text-center">
<thead className="border-b">
<tr>
<th>Date</th>
<th>Ville</th>
<th>Tournois</th>
<th>Cadence</th>
</tr>
</thead>
<tbody>{tournaments}</tbody>
</table>
</section>
);
}
+30
View File
@@ -14,11 +14,13 @@
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"eslint": "8.41.0", "eslint": "8.41.0",
"eslint-config-next": "13.4.3", "eslint-config-next": "13.4.3",
"leaflet": "^1.9.4",
"mongodb": "^5.5.0", "mongodb": "^5.5.0",
"next": "13.4.3", "next": "13.4.3",
"postcss": "8.4.23", "postcss": "8.4.23",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-leaflet": "^4.2.1",
"tailwindcss": "3.3.2", "tailwindcss": "3.3.2",
"typescript": "5.0.4" "typescript": "5.0.4"
}, },
@@ -1430,6 +1432,16 @@
"url": "https://opencollective.com/unts" "url": "https://opencollective.com/unts"
} }
}, },
"node_modules/@react-leaflet/core": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz",
"integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==",
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/@rushstack/eslint-patch": { "node_modules/@rushstack/eslint-patch": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz",
@@ -5805,6 +5817,11 @@
"language-subtag-registry": "~0.3.2" "language-subtag-registry": "~0.3.2"
} }
}, },
"node_modules/leaflet": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="
},
"node_modules/leven": { "node_modules/leven": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -6888,6 +6905,19 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-leaflet": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz",
"integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==",
"dependencies": {
"@react-leaflet/core": "^2.1.0"
},
"peerDependencies": {
"leaflet": "^1.9.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+2
View File
@@ -16,11 +16,13 @@
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"eslint": "8.41.0", "eslint": "8.41.0",
"eslint-config-next": "13.4.3", "eslint-config-next": "13.4.3",
"leaflet": "^1.9.4",
"mongodb": "^5.5.0", "mongodb": "^5.5.0",
"next": "13.4.3", "next": "13.4.3",
"postcss": "8.4.23", "postcss": "8.4.23",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-leaflet": "^4.2.1",
"tailwindcss": "3.3.2", "tailwindcss": "3.3.2",
"typescript": "5.0.4" "typescript": "5.0.4"
}, },
+5 -13
View File
@@ -1,18 +1,10 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: [ content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}', "./pages/**/*.{js,ts,jsx,tsx,mdx}",
'./components/**/*.{js,ts,jsx,tsx,mdx}', "./components/**/*.{js,ts,jsx,tsx,mdx}",
'./app/**/*.{js,ts,jsx,tsx,mdx}', "./app/**/*.{js,ts,jsx,tsx,mdx}",
], ],
theme: { theme: {},
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [], plugins: [],
} };