88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
"use client";
|
|
import React, { useEffect, useState } from "react";
|
|
|
|
type Event = { title: string; date: string; location?: string; signupUrl?: string; signups?: boolean | "soon" };
|
|
|
|
export function CalendarView() {
|
|
const [events, setEvents] = useState<Event[]>([]);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
try {
|
|
const res = await fetch("/api/content?path=pt/competicoes/future.json");
|
|
const json = await res.json();
|
|
setEvents(json.events ?? []);
|
|
} catch {}
|
|
})();
|
|
}, []);
|
|
|
|
// Parse DD/MM/YYYY format
|
|
const parseDate = (dateStr: string) => {
|
|
const parts = dateStr.split('/');
|
|
if (parts.length === 3) {
|
|
return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]));
|
|
}
|
|
return new Date(dateStr);
|
|
};
|
|
|
|
// Simple month grid from provided future events
|
|
const byMonth = events.reduce<Record<string, Event[]>>((acc, e) => {
|
|
const month = parseDate(e.date).toLocaleString("pt-PT", { month: "long", year: "numeric" });
|
|
acc[month] ??= [];
|
|
acc[month].push(e);
|
|
return acc;
|
|
}, {});
|
|
|
|
return (
|
|
<section aria-labelledby="calendar-title">
|
|
<h2 id="calendar-title" className="text-xl font-semibold">Calendário</h2>
|
|
<div className="grid gap-4 md:grid-cols-2 mt-2">
|
|
{Object.entries(byMonth).map(([m, items]) => (
|
|
<div key={m} className="border p-3">
|
|
<h3 className="font-bold">{m}</h3>
|
|
<ul className="mt-2 space-y-1">
|
|
{items.map((e, idx) => (
|
|
<li key={idx}>
|
|
<strong>{e.title}</strong> — {parseDate(e.date).toLocaleDateString("pt-PT")}
|
|
{e.location && <span className="ml-2">({e.location})</span>}
|
|
{e.signups === true && (
|
|
<button
|
|
onClick={() => window.open(e.signupUrl, '_blank')}
|
|
className="ml-2 px-3 py-1 bg-green-600 text-white rounded hover:bg-green-700 transition-colors"
|
|
>
|
|
Inscrição
|
|
</button>
|
|
)}
|
|
{e.signups === false && (
|
|
<div className="ml-2 inline-block">
|
|
<button
|
|
onClick={() => alert('As inscrições para esta competição estão encerradas')}
|
|
className="px-3 py-1 bg-red-300 text-red-700 rounded cursor-not-allowed opacity-70"
|
|
title="As inscrições estão encerradas"
|
|
>
|
|
Inscrição
|
|
</button>
|
|
<span className="ml-2 text-sm text-red-600">(Encerradas)</span>
|
|
</div>
|
|
)}
|
|
{e.signups === "soon" && (
|
|
<div className="ml-2 inline-block">
|
|
<button
|
|
onClick={() => alert('As inscrições para esta competição abrem em breve')}
|
|
className="px-3 py-1 bg-yellow-300 text-yellow-700 rounded cursor-not-allowed opacity-70"
|
|
title="As inscrições abrem em breve"
|
|
>
|
|
Inscrição
|
|
</button>
|
|
<span className="ml-2 text-sm text-yellow-600">(Em breve)</span>
|
|
</div>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|