mirror of
https://github.com/Lapikud/tipilan.git
synced 2026-03-23 21:34:21 +00:00
Add English translation to page with next-intl
This commit is contained in:
81
src/app/[locale]/ajakava/page.tsx
Normal file
81
src/app/[locale]/ajakava/page.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import { scheduleData } from "@/data/timetable";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const tabs = Object.keys(scheduleData);
|
||||
|
||||
export default function Timetable() {
|
||||
const [activeTab, setActiveTab] = useState(tabs[0]);
|
||||
const schedule = scheduleData[activeTab];
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
<h1
|
||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`}
|
||||
>
|
||||
{t("schedule.title")}
|
||||
</h1>
|
||||
|
||||
{/* Tab menu */}
|
||||
<div className="flex space-x-4 mb-8">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`${vipnagorgialla.className} cursor-pointer uppercase italic px-4 py-2 text-lg font-semibold ${
|
||||
activeTab === tab
|
||||
? "bg-[#00A3E0] text-white"
|
||||
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
|
||||
} transition-colors`}
|
||||
>
|
||||
{t(`schedule.${tab}`)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Schedule entries */}
|
||||
<div className="space-y-6">
|
||||
{schedule.map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="border-l-3 border-[#007CAB] pl-4 flex flex-row flex-wrap gap-5 items-stretch"
|
||||
>
|
||||
<div
|
||||
className={`${vipnagorgialla.className} text-[#00A3E0] text-4xl font-bold italic flex-shrink-0 flex items-center justify-center`}
|
||||
style={{ width: "180px", minWidth: "180px" }}
|
||||
>
|
||||
{item.time}
|
||||
</div>
|
||||
<div
|
||||
className="flex-1 flex flex-col justify-center min-h-[120px]"
|
||||
style={{ minWidth: "0" }}
|
||||
>
|
||||
<div
|
||||
className={`${vipnagorgialla.className} text-3xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5] text-balance`}
|
||||
>
|
||||
{t(item.titleKey)}
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
||||
{item.description}
|
||||
</div>
|
||||
)}
|
||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
||||
{t(item.locationKey)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SectionDivider />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import { db } from "@/db/drizzle";
|
||||
// Types
|
||||
import type { TeamWithMembers, MemberWithUser } from "@/types/database";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
// User interface
|
||||
import {
|
||||
@@ -19,19 +20,26 @@ import {
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
// Later on we can use a i8 solution?
|
||||
function translateRole(role: string): string {
|
||||
// Function to translate roles using i18n
|
||||
function translateRole(role: string, t: (key: string) => string): string {
|
||||
switch (role) {
|
||||
case "CAPTAIN":
|
||||
return "Kapten";
|
||||
return t("admin.roles.captain");
|
||||
case "TEAMMATE":
|
||||
return "Meeskonnaliige";
|
||||
return t("admin.roles.teammate");
|
||||
default:
|
||||
return role;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function AdminTeams() {
|
||||
export default async function AdminTeams({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
// Fetch teams with their members and member users
|
||||
const teams = await db.query.teams.findMany({
|
||||
with: {
|
||||
@@ -54,7 +62,7 @@ export default async function AdminTeams() {
|
||||
<h1
|
||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
Haldus - Meeskonnad
|
||||
{t("admin.title")} - {t("admin.teams")}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
@@ -63,8 +71,8 @@ export default async function AdminTeams() {
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">ID</TableHead>
|
||||
<TableHead>Nimi</TableHead>
|
||||
<TableHead>Liikmed</TableHead>
|
||||
<TableHead>{t("admin.table.name")}</TableHead>
|
||||
<TableHead>{t("admin.table.members")}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -84,12 +92,14 @@ export default async function AdminTeams() {
|
||||
{member.user.firstName} {member.user.lastName}
|
||||
</span>
|
||||
<span className="text-gray-500">
|
||||
({translateRole(member.role)})
|
||||
({translateRole(member.role, t)})
|
||||
</span>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<span className="text-gray-500">Liikmeid puuduvad</span>
|
||||
<span className="text-gray-500">
|
||||
{t("admin.table.noMembers")}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -17,9 +17,10 @@ import {
|
||||
X,
|
||||
} from "lucide-react";
|
||||
|
||||
import Link from "next/link";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect, RedirectType } from "next/navigation";
|
||||
import NextLink from "next/link";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
@@ -53,13 +54,13 @@ async function dismissAlert() {
|
||||
redirect("/haldus", RedirectType.replace);
|
||||
}
|
||||
|
||||
const SuccessAlertDB = () => {
|
||||
const SuccessAlertDB = ({ t }: { t: (key: string) => string }) => {
|
||||
return (
|
||||
<Alert className="flex items-start mt-8">
|
||||
<CheckCircle2Icon className="mt-0.5" />
|
||||
<div className="flex-1">
|
||||
<AlertTitle>Toiming oli edukas!</AlertTitle>
|
||||
<AlertDescription>Andmebaasi andmed on uuendatud.</AlertDescription>
|
||||
<AlertTitle>{t("admin.success.title")}</AlertTitle>
|
||||
<AlertDescription>{t("admin.success.description")}</AlertDescription>
|
||||
</div>
|
||||
<form action={dismissAlert} className="ml-2">
|
||||
<Button
|
||||
@@ -76,10 +77,15 @@ const SuccessAlertDB = () => {
|
||||
};
|
||||
|
||||
export default async function Admin({
|
||||
params,
|
||||
searchParams,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
const alarmStatus = await searchParams;
|
||||
const showSuccess = alarmStatus.success === "true";
|
||||
|
||||
@@ -97,31 +103,31 @@ export default async function Admin({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
{showSuccess && <SuccessAlertDB />}
|
||||
{showSuccess && <SuccessAlertDB t={t} />}
|
||||
<div className="flex items-center gap-4">
|
||||
<Link href={"/"}>
|
||||
<NextLink href={"/"}>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] translate-y-2.5 hover:-translate-x-2 dark:hover:text-[#EEE5E5] hover:text-[#2A2C3F] transition">
|
||||
arrow_left_alt
|
||||
</span>
|
||||
</Link>
|
||||
</NextLink>
|
||||
<h1
|
||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
Haldus
|
||||
{t("admin.title")}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
<div className="pl-2 flex gap-8 pb-4">
|
||||
<div className="flex text-lg md:text-2xl flex-row items-center">
|
||||
<Users className="mr-2" />
|
||||
Kasutajaid: {usersData.length}
|
||||
{t("admin.users")}: {usersData.length}
|
||||
</div>
|
||||
<Link href="/haldus/meeskonnad" className="flex items-center">
|
||||
<NextLink href="/haldus/meeskonnad" className="flex items-center">
|
||||
<div className="flex text-lg md:text-2xl flex-row items-center">
|
||||
<IdCardLanyard className="mr-2" />
|
||||
Meeskondasid: {teamsData.length}
|
||||
{t("admin.teams")}: {teamsData.length}
|
||||
</div>
|
||||
</Link>
|
||||
</NextLink>
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<div className="ml-auto">
|
||||
@@ -135,24 +141,24 @@ export default async function Admin({
|
||||
</div>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogTitle>
|
||||
Kas soovite värskendada andmebaasi?
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogTitle>{t("admin.sync.title")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
See tõmbab Fientast praegused andmed ning asendab{" "}
|
||||
<span className="text-red-600 font-semibold">KÕIK</span>{" "}
|
||||
olemasolevad andmed andmebaasis!
|
||||
{t("admin.sync.description1")}{" "}
|
||||
<span className="text-red-600 font-semibold">
|
||||
{t("admin.sync.all")}
|
||||
</span>{" "}
|
||||
{t("admin.sync.description2")}
|
||||
<br />
|
||||
<br />
|
||||
Kui sa ei ole kindel, vajuta "Tühista".
|
||||
{t("admin.sync.warning")}
|
||||
</AlertDialogDescription>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel className="cursor-pointer">
|
||||
Tühista
|
||||
{t("common.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<form action={syncAction}>
|
||||
<AlertDialogAction type="submit" className="cursor-pointer">
|
||||
Värskenda
|
||||
{t("admin.sync.update")}
|
||||
</AlertDialogAction>
|
||||
</form>
|
||||
</AlertDialogFooter>
|
||||
@@ -2,8 +2,16 @@
|
||||
import ReactMarkdown, { Components } from "react-markdown";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
export default async function Page() {
|
||||
export default async function Page({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
const file = Bun.file("src/data/kodukord.md");
|
||||
const content = await file.text();
|
||||
|
||||
@@ -14,7 +22,7 @@ export default async function Page() {
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
||||
>
|
||||
Kodukord
|
||||
{t("rules.houseRules")}
|
||||
</h1>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||
38
src/app/[locale]/layout.tsx
Normal file
38
src/app/[locale]/layout.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { setRequestLocale, getMessages } from "next-intl/server";
|
||||
import { ThemeProvider } from "@/components/Theme-provider";
|
||||
import SidebarParent from "@/components/SidebarParent";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
export default async function LocaleLayout({
|
||||
children,
|
||||
params,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
params: Promise<{ locale: string }>;
|
||||
}>) {
|
||||
const { locale } = await params;
|
||||
|
||||
// Enable static rendering
|
||||
setRequestLocale(locale);
|
||||
|
||||
// Provide messages for client-side components
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<div lang={locale}>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SidebarParent />
|
||||
{children}
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import * as THREE from "three";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { EyeClosed, Eye } from "lucide-react";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// Define interface for the ref with toggle function
|
||||
interface MountRefCurrent extends HTMLDivElement {
|
||||
@@ -16,6 +17,21 @@ export default function Expo() {
|
||||
const [hoveredRoom, setHoveredRoom] = useState<string | null>(null);
|
||||
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
|
||||
const [showDividers, setShowDividers] = useState<boolean>(true);
|
||||
const t = useTranslations();
|
||||
|
||||
// Define room names with translations
|
||||
const roomNames = useMemo(
|
||||
() => ({
|
||||
boardGames: t("expo.areas.boardGames"),
|
||||
bar: t("expo.areas.bar"),
|
||||
eval: "EVAL",
|
||||
simRacing: t("expo.areas.simRacing"),
|
||||
fighting: t("expo.areas.fighting"),
|
||||
lvlup: "LVLup!",
|
||||
redbull: "Red Bull",
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mountRef.current) return;
|
||||
@@ -114,7 +130,7 @@ export default function Expo() {
|
||||
x: 2.5,
|
||||
z: 4,
|
||||
color: roomColors[0],
|
||||
name: "Lauamängude ala",
|
||||
name: roomNames.boardGames,
|
||||
},
|
||||
{
|
||||
width: 3.5,
|
||||
@@ -123,7 +139,7 @@ export default function Expo() {
|
||||
x: 0.7,
|
||||
z: -0.3,
|
||||
color: roomColors[1],
|
||||
name: "Baariala",
|
||||
name: roomNames.bar,
|
||||
},
|
||||
{
|
||||
width: 1.8,
|
||||
@@ -132,7 +148,7 @@ export default function Expo() {
|
||||
x: 1,
|
||||
z: -3.5,
|
||||
color: roomColors[2],
|
||||
name: "EVAL",
|
||||
name: roomNames.eval,
|
||||
},
|
||||
{
|
||||
width: 2,
|
||||
@@ -141,7 +157,7 @@ export default function Expo() {
|
||||
x: 5.2,
|
||||
z: -2,
|
||||
color: roomColors[3],
|
||||
name: "Red Bull Sim Racing",
|
||||
name: roomNames.simRacing,
|
||||
},
|
||||
{
|
||||
width: 3,
|
||||
@@ -150,7 +166,7 @@ export default function Expo() {
|
||||
x: -1.7,
|
||||
z: -3.5,
|
||||
color: roomColors[4],
|
||||
name: "Võitlusmängu ala",
|
||||
name: roomNames.fighting,
|
||||
},
|
||||
// {
|
||||
// width: 1.8,
|
||||
@@ -168,7 +184,7 @@ export default function Expo() {
|
||||
x: -3.5,
|
||||
z: -0.5,
|
||||
color: roomColors[7],
|
||||
name: "LVLup!",
|
||||
name: roomNames.lvlup,
|
||||
},
|
||||
//{
|
||||
// width: 2,
|
||||
@@ -186,7 +202,7 @@ export default function Expo() {
|
||||
x: 3,
|
||||
z: -3.5,
|
||||
color: roomColors[8],
|
||||
name: "Red Bull",
|
||||
name: roomNames.redbull,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -370,7 +386,7 @@ export default function Expo() {
|
||||
}
|
||||
renderer.dispose();
|
||||
};
|
||||
}, []);
|
||||
}, [roomNames]);
|
||||
|
||||
// Update dividers when showDividers state changes
|
||||
useEffect(() => {
|
||||
@@ -380,124 +396,124 @@ export default function Expo() {
|
||||
}, [showDividers]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16 ">
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
||||
>
|
||||
Messiala
|
||||
</h1>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3">
|
||||
Tudengimaja
|
||||
</h2>
|
||||
<div className="flex flex-wrap gap-4 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#4ecdc4" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Baariala
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ffe66d" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
EVAL
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#343434" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Lauamängude ala
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#080682" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
LVLup!
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#C02841" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Red Bull
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ff6600" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Red Bull Sim Racing
|
||||
</span>
|
||||
</div>
|
||||
<div className="items-center gap-2 hidden">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#3498db" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Sony
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ff1493" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Võitlusmängu ala
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col lg:flex-row gap-8 items-start">
|
||||
<div className="flex-shrink-0 border-3 border-[#1F5673] w-full max-w-[800px] relative">
|
||||
<div ref={mountRef} className="w-full" />
|
||||
<button
|
||||
onClick={() => setShowDividers(!showDividers)}
|
||||
className={`absolute top-2 right-2 px-3 py-2 bg-[#1F5673] text-white hover:bg-[#2A7A9B] ${vipnagorgialla.className} uppercase italic text-sm font-semibold flex items-center transition-colors shadow-lg z-10`}
|
||||
>
|
||||
{showDividers ? (
|
||||
<EyeClosed className="w-6 h-6 mr-2" />
|
||||
) : (
|
||||
<Eye className="w-6 h-6 mr-2" />
|
||||
)}
|
||||
|
||||
{showDividers ? "Peida" : "Näita"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tooltip */}
|
||||
{hoveredRoom && (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16 ">
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4 uppercase`}
|
||||
>
|
||||
{t("expo.title")}
|
||||
</h1>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-3">
|
||||
{t("schedule.locations.studentHouse")}
|
||||
</h2>
|
||||
<div className="flex flex-wrap gap-4 pb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="fixed bg-black bg-opacity-80 text-white px-3 py-2 rounded-lg text-sm pointer-events-none z-50"
|
||||
style={{
|
||||
left: mousePosition.x + 10,
|
||||
top: mousePosition.y - 10,
|
||||
}}
|
||||
>
|
||||
{hoveredRoom}
|
||||
</div>
|
||||
)}
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#4ecdc4" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
{t("expo.areas.bar")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ffe66d" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
EVAL
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#343434" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
{t("expo.areas.boardGames")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#080682" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
LVLup!
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#C02841" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Red Bull
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ff6600" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
{t("expo.areas.simRacing")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="items-center gap-2 hidden">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#3498db" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
Sony
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-4 h-4 border border-gray-300"
|
||||
style={{ backgroundColor: "#ff1493" }}
|
||||
></div>
|
||||
<span className="text-sm text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
{t("expo.areas.fighting")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col lg:flex-row gap-8 items-start">
|
||||
<div className="flex-shrink-0 border-3 border-[#1F5673] w-full max-w-[800px] relative">
|
||||
<div ref={mountRef} className="w-full" />
|
||||
<button
|
||||
onClick={() => setShowDividers(!showDividers)}
|
||||
className={`absolute top-2 right-2 px-3 py-2 bg-[#1F5673] text-white hover:bg-[#2A7A9B] ${vipnagorgialla.className} uppercase italic text-sm font-semibold flex items-center transition-colors shadow-lg z-10`}
|
||||
>
|
||||
{showDividers ? (
|
||||
<EyeClosed className="w-6 h-6 mr-2" />
|
||||
) : (
|
||||
<Eye className="w-6 h-6 mr-2" />
|
||||
)}
|
||||
|
||||
{showDividers ? t("expo.hide") : t("expo.show")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tooltip */}
|
||||
{hoveredRoom && (
|
||||
<div
|
||||
className="fixed bg-black bg-opacity-80 text-white px-3 py-2 rounded-lg text-sm pointer-events-none z-50"
|
||||
style={{
|
||||
left: mousePosition.x + 10,
|
||||
top: mousePosition.y - 10,
|
||||
}}
|
||||
>
|
||||
{hoveredRoom}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<SectionDivider />
|
||||
</div>
|
||||
);
|
||||
19
src/app/[locale]/not-found.tsx
Normal file
19
src/app/[locale]/not-found.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import { getTranslations } from "next-intl/server";
|
||||
|
||||
export default async function NotFound() {
|
||||
const t = await getTranslations("notFound");
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center">
|
||||
<h1
|
||||
className={`text-7xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-8">
|
||||
{t("message")}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import Image from "next/image";
|
||||
import NextLink from "next/link";
|
||||
|
||||
export default async function Home({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
{/* Title */}
|
||||
@@ -25,7 +35,7 @@ export default function Home() {
|
||||
<h3
|
||||
className={`text-[clamp(1.25rem,0.75rem+2.5vw,3.75rem)] ${vipnagorgialla.className} leading-[90%] font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F]`}
|
||||
>
|
||||
Auhinnafond
|
||||
{t("tournaments.prizePool")}
|
||||
</h3>
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.2rem+4vw,6rem)] ${vipnagorgialla.className} leading-[90%] font-bold italic text-[#007CAB] dark:text-[#00A3E0]`}
|
||||
@@ -44,7 +54,7 @@ export default function Home() {
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black dark:group-hover:text-[#2A2C3F]`}
|
||||
>
|
||||
Ajakava
|
||||
{t("navigation.schedule")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -55,8 +65,7 @@ export default function Home() {
|
||||
event_note
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLAN on pungil põnevatest turniiridest, mini-võistlustest ja
|
||||
paljust muust.
|
||||
{t("home.sections.schedule.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -66,9 +75,9 @@ export default function Home() {
|
||||
>
|
||||
<div className="cursor-pointer flex flex-row justify-between gap-4 items-center">
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic break-all uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||
>
|
||||
Turniirid
|
||||
{t("navigation.tournaments")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -80,8 +89,7 @@ export default function Home() {
|
||||
trophy
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLANil toimuvad suurejoonelised CS2 ja LoL turniirid, mille
|
||||
auhinnafond on 10 000€.
|
||||
{t("home.sections.tournaments.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -93,7 +101,7 @@ export default function Home() {
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||
>
|
||||
Messiala
|
||||
{t("navigation.expo")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -104,8 +112,7 @@ export default function Home() {
|
||||
weekend
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad
|
||||
loengud.
|
||||
{t("home.sections.expo.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -117,7 +124,7 @@ export default function Home() {
|
||||
>
|
||||
<div className="cursor-pointer text-left flex flex-row justify-between xl:justify-start gap-8">
|
||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] dark:group-hover:text-[#2A2C3F] text-[#2A2C3F] group-hover:text-black">
|
||||
Bro­neeri oma koht juba täna!
|
||||
{t("home.sections.reserveSpot")}
|
||||
</h3>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] hidden md:block group-hover:translate-x-2 group-hover:text-[#EEE5E5] dark:group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -133,10 +140,10 @@ export default function Home() {
|
||||
>
|
||||
<div className="text-left flex flex-col justify-between xl:justify-start">
|
||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black pb-8">
|
||||
TipiLANi tõmbab käima...
|
||||
{t("home.sections.poweredBy")}
|
||||
</h3>
|
||||
<div className="flex flex-row flex-wrap gap-8 md:gap-18 items-center">
|
||||
<Link href="https://taltech.ee" target="_blank">
|
||||
<NextLink href="https://taltech.ee" target="_blank">
|
||||
<Image
|
||||
src="/sponsors/taltech-color.png"
|
||||
alt="Taltech (Tallinna Tehnikaülikool)"
|
||||
@@ -144,8 +151,8 @@ export default function Home() {
|
||||
height={192}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link href="https://www.redbull.com/ee-et/" target="_blank">
|
||||
</NextLink>
|
||||
<NextLink href="https://www.redbull.com/ee-et/" target="_blank">
|
||||
<Image
|
||||
src="/sponsors/redbull.png"
|
||||
alt="Redbull"
|
||||
@@ -153,8 +160,8 @@ export default function Home() {
|
||||
height={80}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link href="https://www.alecoq.ee" target="_blank">
|
||||
</NextLink>
|
||||
<NextLink href="https://www.alecoq.ee" target="_blank">
|
||||
<Image
|
||||
src="/sponsors/alecoq.svg"
|
||||
alt="Alecoq"
|
||||
@@ -162,8 +169,8 @@ export default function Home() {
|
||||
height={200}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link href="https://www.simracing.ee/" target="_blank">
|
||||
</NextLink>
|
||||
<NextLink href="https://www.simracing.ee/" target="_blank">
|
||||
<Image
|
||||
src="/sponsors/EVAL.png"
|
||||
alt="EVAL"
|
||||
@@ -171,8 +178,8 @@ export default function Home() {
|
||||
height={200}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link href="https://balsnack.ee" target="_blank">
|
||||
</NextLink>
|
||||
<NextLink href="https://balsnack.ee" target="_blank">
|
||||
<Image
|
||||
src="/sponsors/balsnack.svg"
|
||||
alt="Balsnack"
|
||||
@@ -180,8 +187,8 @@ export default function Home() {
|
||||
height={200}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
</NextLink>
|
||||
<NextLink
|
||||
href="https://www.rara.ee/sundmused/interaktiivne-videomangude-muuseum-lvlup/"
|
||||
target="_blank"
|
||||
>
|
||||
@@ -192,8 +199,11 @@ export default function Home() {
|
||||
height={192}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
<Link href="https://www.facebook.com/bfglOfficial" target="_blank">
|
||||
</NextLink>
|
||||
<NextLink
|
||||
href="https://www.facebook.com/bfglOfficial"
|
||||
target="_blank"
|
||||
>
|
||||
<Image
|
||||
src="/sponsors/BFGL.png"
|
||||
alt="BFGL"
|
||||
@@ -201,7 +211,7 @@ export default function Home() {
|
||||
height={192}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Link>
|
||||
</NextLink>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
114
src/app/[locale]/piletid/page.tsx
Normal file
114
src/app/[locale]/piletid/page.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
export default async function Tickets({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4`}
|
||||
>
|
||||
{t("tickets.title")}
|
||||
</h1>
|
||||
<div className="flex justify-center lg:items-center flex-col lg:flex-row gap-8 md:gap-12 flex-grow mb-16 md:mt-8 lg:mt-0">
|
||||
<div className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
{t("tickets.computerParticipant.price")}
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
{t("tickets.computerParticipant.title")}
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
||||
{t
|
||||
.raw("tickets.computerParticipant.features")
|
||||
.map((feature: string, index: number) => (
|
||||
<li key={index} className="text-xl italic">
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
{t("tickets.buyTicket")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="bg-[#1F5673] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
{t("tickets.competitor.price")}
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
{t("tickets.competitor.title")}
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]">
|
||||
{t
|
||||
.raw("tickets.competitor.features")
|
||||
.map((feature: string, index: number) => (
|
||||
<li key={index} className="text-xl">
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
{t("tickets.buyTicket")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
{t("tickets.visitor.price")}
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
{t("tickets.visitor.title")}
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
||||
{t
|
||||
.raw("tickets.visitor.features")
|
||||
.map((feature: string, index: number) => (
|
||||
<li key={index} className="text-xl">
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
{t("tickets.buyTicket")}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SectionDivider />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,23 +2,24 @@ import { notFound } from "next/navigation";
|
||||
import ReactMarkdown, { Components } from "react-markdown";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
// Map of valid slugs to their corresponding file paths and titles
|
||||
// Map of valid slugs to their corresponding file paths and translation keys
|
||||
const rulesMap = {
|
||||
lol: {
|
||||
filePath: "src/data/rules/lol.md",
|
||||
title: "LOL Reeglid",
|
||||
titleKey: "rules.lolRules",
|
||||
},
|
||||
cs2: {
|
||||
filePath: "src/data/rules/cs2.md",
|
||||
title: "CS2 Reeglid",
|
||||
titleKey: "rules.cs2Rules",
|
||||
},
|
||||
} as const;
|
||||
|
||||
type RuleSlug = keyof typeof rulesMap;
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ slug: string }>;
|
||||
params: Promise<{ slug: string; locale: string }>;
|
||||
}
|
||||
|
||||
async function getRuleContent(slug: string) {
|
||||
@@ -33,7 +34,7 @@ async function getRuleContent(slug: string) {
|
||||
const content = await file.text();
|
||||
return {
|
||||
content,
|
||||
title: ruleConfig.title,
|
||||
titleKey: ruleConfig.titleKey,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error reading rule file for slug ${slug}:`, error);
|
||||
@@ -42,7 +43,9 @@ async function getRuleContent(slug: string) {
|
||||
}
|
||||
|
||||
export default async function RulePage({ params }: PageProps) {
|
||||
const { slug } = await params;
|
||||
const { slug, locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
const ruleData = await getRuleContent(slug);
|
||||
|
||||
if (!ruleData) {
|
||||
@@ -55,7 +58,7 @@ export default async function RulePage({ params }: PageProps) {
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
<h1 className={`${headingStyle} mt-8 md:mt-16 mb-4`}>
|
||||
{ruleData.title}
|
||||
{t(ruleData.titleKey)}
|
||||
</h1>
|
||||
|
||||
<div className="prose prose-lg dark:prose-invert max-w-none">
|
||||
@@ -1,8 +1,16 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import NextLink from "next/link";
|
||||
|
||||
export default function RulesMenu() {
|
||||
export default async function RulesMenu({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
const headingStyle = `text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] uppercase`;
|
||||
|
||||
const boxStyle = `-skew-x-2 md:-skew-x-5 text-white md:px-12 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]`;
|
||||
@@ -15,32 +23,34 @@ export default function RulesMenu() {
|
||||
<div>
|
||||
<div className="flex flex-col md:m-16">
|
||||
<h1 className={`${headingStyle} ml-3 mt-24 md:ml-0 md:mt-16 mb-4 px-4`}>
|
||||
REEGLID
|
||||
{t("rules.title")}
|
||||
</h1>
|
||||
|
||||
<div className="flex flex-wrap flex-row lg:mt-16 justify-center lg:items-start gap-12 flex-grow mb-8">
|
||||
<Link href="/kodukord">
|
||||
<NextLink href="/kodukord">
|
||||
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
|
||||
<h2 className={`${boxTextStyle}`}>Kodukord</h2>
|
||||
<h2 className={`${boxTextStyle}`}>{t("rules.houseRules")}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
</NextLink>
|
||||
|
||||
<Link href="/reeglid/cs2">
|
||||
<NextLink href="/reeglid/cs2">
|
||||
<div className={`${boxStyle} bg-[#1F5673] py-20 px-8`}>
|
||||
<h2 className={`${boxTextStyle}`}>CS2 reeglid</h2>
|
||||
<h2 className={`${boxTextStyle}`}>{t("rules.cs2Rules")}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
</NextLink>
|
||||
|
||||
<Link href="reeglid/lol">
|
||||
<NextLink href="reeglid/lol">
|
||||
<div className={`${boxStyle} bg-[#007CAB] py-20 px-8`}>
|
||||
<h2 className={`${boxTextStyle}`}>LoL reeglid</h2>
|
||||
<h2 className={`${boxTextStyle}`}>{t("rules.lolRules")}</h2>
|
||||
</div>
|
||||
</Link>
|
||||
</NextLink>
|
||||
|
||||
{/* Minitourn. link coming soon*/}
|
||||
{/*<Link href="">*/}
|
||||
<div className={`${boxStyle} bg-[#1F5673] py-16 px-8`}>
|
||||
<h2 className={`${boxTextStyle}`}>Miniturniiride reeglid</h2>
|
||||
<h2 className={`${boxTextStyle}`}>
|
||||
{t("tournaments.mini.titleSingular")} {t("rules.title")}
|
||||
</h2>
|
||||
</div>
|
||||
{/*</Link>*/}
|
||||
</div>
|
||||
@@ -1,8 +1,16 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
export default function Home() {
|
||||
export default async function Home({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
return (
|
||||
<div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2">
|
||||
@@ -30,7 +38,7 @@ export default function Home() {
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black dark:group-hover:text-[#2A2C3F]`}
|
||||
>
|
||||
Ajakava
|
||||
{t("navigation.schedule")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -41,8 +49,7 @@ export default function Home() {
|
||||
event_note
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLAN on pungil põnevatest turniiridest, mini-võistlustest ja
|
||||
paljust muust.
|
||||
{t("home.sections.schedule.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -68,7 +75,7 @@ export default function Home() {
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||
>
|
||||
Turniirid
|
||||
{t("navigation.tournaments")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -80,8 +87,7 @@ export default function Home() {
|
||||
trophy
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLANil toimuvad suurejoonelised CS2 ja LoL turniirid, mille
|
||||
auhinnafond on 10 000€.
|
||||
{t("home.sections.tournaments.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -93,7 +99,7 @@ export default function Home() {
|
||||
<h2
|
||||
className={`text-[clamp(2rem,1.8rem+1vw,3rem)] ${vipnagorgialla.className} font-bold italic uppercase dark:text-[#EEE5E5] text-[#2A2C3F] dark:group-hover:text-[#2A2C3F] group-hover:text-black`}
|
||||
>
|
||||
Messiala
|
||||
{t("navigation.expo")}
|
||||
</h2>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] group-hover:translate-x-2 dark:group-hover:text-[#EEE5E5] group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -104,8 +110,7 @@ export default function Home() {
|
||||
weekend
|
||||
</span>
|
||||
<p className="text-[clamp(0.875rem,0.75rem+0.5vw,1.25rem)] tracking-[-0.045rem] dark:group-hover:text-[#2A2C3F] group-hover:text-black">
|
||||
TipiLANi messialal paiknevad ettevõtted, lisategevused ja toimuvad
|
||||
loengud.
|
||||
{t("home.sections.expo.description")}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
@@ -117,7 +122,7 @@ export default function Home() {
|
||||
>
|
||||
<div className="cursor-pointer text-left flex flex-row justify-between xl:justify-start gap-8">
|
||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] dark:group-hover:text-[#2A2C3F] text-[#2A2C3F] group-hover:text-black">
|
||||
Bro­neeri oma koht juba täna!
|
||||
{t("home.sections.reserveSpot")}
|
||||
</h3>
|
||||
<span className="material-symbols-outlined !text-[clamp(2rem,1.5rem+1.5vw,3.5rem)] !font-bold text-[#007CAB] dark:text-[#00A3E0] hidden md:block group-hover:translate-x-2 group-hover:text-[#EEE5E5] dark:group-hover:text-[#EEE5E5] transition">
|
||||
arrow_right_alt
|
||||
@@ -133,7 +138,7 @@ export default function Home() {
|
||||
>
|
||||
<div className="text-left flex flex-col justify-between xl:justify-start">
|
||||
<h3 className="text-4xl md:text-5xl dark:text-[#EEE5E5] text-[#2A2C3F] group-hover:text-black pb-8">
|
||||
TipiLANi tõmbab käima...
|
||||
{t("home.sections.poweredBy")}
|
||||
</h3>
|
||||
<div className="flex flex-row flex-wrap gap-8 md:gap-18 items-center">
|
||||
<Link href="https://taltech.ee" target="_blank">
|
||||
@@ -1,42 +1,48 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
|
||||
export default function Tourney() {
|
||||
export default async function Tourney({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ locale: string }>;
|
||||
}) {
|
||||
const { locale } = await params;
|
||||
setRequestLocale(locale);
|
||||
const t = await getTranslations({ locale });
|
||||
const headingStyle = `text-3xl md:text-5xl lg:text-5xl ${vipnagorgialla.className} font-bold uppercase text-[#2A2C3F] dark:text-[#EEE5E5] -skew-x-2 md:-skew-x-5`;
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-[90vh] mt-16">
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic uppercase
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic uppercase
|
||||
text-[#2A2C3F] dark:text-[#EEE5E5] md:m-16`}
|
||||
>
|
||||
Turniirid
|
||||
{t("tournaments.title")}
|
||||
</h1>
|
||||
|
||||
|
||||
<div className="flex flex-col">
|
||||
{/* CS2 turniir */}
|
||||
<div className="hover:bg-[#007CAB] py-8 md:py-16 transition group">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
||||
<div className="-skew-x-2 md:-skew-x-5">
|
||||
<h2 className={`${headingStyle}`}>CS2 turniir</h2>
|
||||
<h2 className={`${headingStyle}`}>
|
||||
{t("tournaments.cs2.title")}
|
||||
</h2>
|
||||
<p
|
||||
className={
|
||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
||||
}
|
||||
>
|
||||
Toimumisaeg veel selgumisel
|
||||
{t("tournaments.cs2.timing")}
|
||||
</p>
|
||||
<p className="text-balance">
|
||||
TipiLANil toimub Eesti ühe suurima auhinnafondiga CS2 turniire
|
||||
juba sel sügisel. Haara kaasa sõbrad ja saa osa
|
||||
adrenaliinirohkest kogemusest!
|
||||
{t("tournaments.cs2.description1")}
|
||||
</p>
|
||||
<br />
|
||||
<p className="text-balance">
|
||||
Auhinnafond on suuruses 5250€, mis jaotatakse TOP3 meeskonna
|
||||
vahel ära. Iga tiimiliige saab vastavalt saavutatud kohale
|
||||
auhinnaks kas 600€, 300€ või 150€.
|
||||
{t("tournaments.cs2.description2")}
|
||||
</p>
|
||||
<br />
|
||||
|
||||
@@ -45,14 +51,14 @@ export default function Tourney() {
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
LOE REEGLEID
|
||||
{t("tournaments.cs2.readRules")}
|
||||
</button>
|
||||
</Link>
|
||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#007CAB] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
OSTA PILET
|
||||
{t("tournaments.cs2.buyTicket")}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
@@ -86,24 +92,22 @@ export default function Tourney() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-auto text-right -skew-x-2 md:-skew-x-5">
|
||||
<h2 className={`${headingStyle}`}>LoL turniir</h2>
|
||||
<h2 className={`${headingStyle}`}>
|
||||
{t("tournaments.lol.title")}
|
||||
</h2>
|
||||
<p
|
||||
className={
|
||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
||||
}
|
||||
>
|
||||
Toimumisaeg veel selgumisel
|
||||
{t("tournaments.lol.timing")}
|
||||
</p>
|
||||
<p className="text-balance">
|
||||
TipiLANil toimub Eesti ühe suurima auhinnafondiga LoL turniire
|
||||
juba sel sügisel. Haara kaasa sõbrad ja saa osa
|
||||
adrenaliinirohkest kogemusest!
|
||||
{t("tournaments.lol.description1")}
|
||||
</p>
|
||||
<br />
|
||||
<p className="text-balance">
|
||||
Auhinnafond on suuruses 3500€, mis jaotatakse TOP3 meeskonna
|
||||
vahel ära. Iga tiimiliige saab vastavalt saavutatud kohale
|
||||
auhinnaks kas 400€, 200€ või 100€.
|
||||
{t("tournaments.lol.description2")}
|
||||
</p>
|
||||
<br />
|
||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8 justify-end">
|
||||
@@ -111,14 +115,14 @@ export default function Tourney() {
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
LOE REEGLEID
|
||||
{t("tournaments.lol.readRules")}
|
||||
</button>
|
||||
</Link>
|
||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#007CAB] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
OSTA PILET
|
||||
{t("tournaments.lol.buyTicket")}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
@@ -130,24 +134,22 @@ export default function Tourney() {
|
||||
<div className="hover:bg-[#007CAB] py-8 md:py-16 border-t-[3px] border-b-[3px] border-[#1F5673] transition group">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 items-center mx-8 md:mx-16 lg:mx-32 xl:mx-48">
|
||||
<div className="-skew-x-2 md:-skew-x-5">
|
||||
<h2 className={`${headingStyle}`}>Mini­turniirid</h2>
|
||||
<h2 className={`${headingStyle}`}>
|
||||
{t("tournaments.mini.title")}
|
||||
</h2>
|
||||
<p
|
||||
className={
|
||||
"text-2xl mb-4 text-neutral-500 group-hover:text-black"
|
||||
}
|
||||
>
|
||||
Toimumisaeg veel selgumisel
|
||||
{t("tournaments.mini.timing")}
|
||||
</p>
|
||||
<p className="text-balance">
|
||||
TipiLANil toimub mitmeid erinevaid lõbusaid ja võistlushimu
|
||||
tekitavaid miniturniire. Miniturniirid toimuvad järgnevates
|
||||
mängudes: SimRacing, Tekken, FIFA, Minecraft Bedwars, Buckshot
|
||||
Roulette, LostGamer ja palju muud.
|
||||
{t("tournaments.mini.description1")}
|
||||
</p>
|
||||
<br />
|
||||
<p className="text-balance">
|
||||
Auhinnafond on kõigi turniiride peale 1250€ ja reeglina saab
|
||||
rahalise auhinna miniturniiri võitja.
|
||||
{t("tournaments.mini.description2")}
|
||||
</p>
|
||||
<br />
|
||||
<div className="flex flex-row flex-wrap gap-4 md:gap-8">
|
||||
@@ -155,14 +157,14 @@ export default function Tourney() {
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
LOE REEGLEID
|
||||
{t("tournaments.mini.readRules")}
|
||||
</button>
|
||||
</Link>
|
||||
<a href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#007CAB] group-hover:bg-black cursor-pointer ${vipnagorgialla.className} font-bold italic text-[#ECE5E5]`}
|
||||
>
|
||||
OSTA PILET
|
||||
{t("tournaments.mini.buyTicket")}
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
@@ -1,81 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import {useState} from "react";
|
||||
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||
import {scheduleData} from "@/data/timetable";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
|
||||
const tabs = Object.keys(scheduleData);
|
||||
|
||||
export default function Timetable() {
|
||||
const [activeTab, setActiveTab] = useState(tabs[0]);
|
||||
const schedule = scheduleData[activeTab];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
<h1
|
||||
className={`text-5xl sm:text-6xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-8`}
|
||||
>
|
||||
Ajakava
|
||||
</h1>
|
||||
|
||||
{/* Tab menu */}
|
||||
<div className="flex space-x-4 mb-8">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveTab(tab)}
|
||||
className={`${vipnagorgialla.className} cursor-pointer uppercase italic px-4 py-2 text-lg font-semibold ${
|
||||
activeTab === tab
|
||||
? "bg-[#00A3E0] text-white"
|
||||
: "bg-[#007CAB] dark:bg-[#007CAB] text-[#EEE5E5] hover:bg-[#00A3E0] dark:hover:bg-[#007CAB]"
|
||||
} transition-colors`}
|
||||
>
|
||||
{tab}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Schedule entries */}
|
||||
<div className="space-y-6">
|
||||
{schedule.map((item, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className="border-l-3 border-[#007CAB] pl-4 flex flex-row flex-wrap gap-5 items-stretch"
|
||||
>
|
||||
<div
|
||||
className={`${vipnagorgialla.className} text-[#00A3E0] text-4xl font-bold italic flex-shrink-0 flex items-center justify-center`}
|
||||
style={{ width: "180px", minWidth: "180px" }}
|
||||
>
|
||||
{item.time}
|
||||
</div>
|
||||
<div
|
||||
className="flex-1 flex flex-col justify-center min-h-[120px]"
|
||||
style={{ minWidth: "0" }}
|
||||
>
|
||||
<div
|
||||
className={`${vipnagorgialla.className} text-3xl italic font-bold text-[#2A2C3F] dark:text-[#EEE5E5] text-balance`}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
{item.description && (
|
||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
||||
{item.description}
|
||||
</div>
|
||||
)}
|
||||
{item.location && (
|
||||
<div className="text-2xl text-[#938BA1] dark:text-[#938BA1] text-balance">
|
||||
{item.location}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SectionDivider/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,8 @@
|
||||
// Head metadata
|
||||
import type { Metadata } from "next";
|
||||
import Head from "next/head";
|
||||
|
||||
// Provides the theme context to the app
|
||||
import { ThemeProvider } from "@/components/Theme-provider";
|
||||
import { Work_Sans } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import "material-symbols";
|
||||
|
||||
// Fonts
|
||||
import { Work_Sans } from "next/font/google";
|
||||
|
||||
import SidebarParent from "@/components/SidebarParent";
|
||||
import Footer from "@/components/Footer";
|
||||
|
||||
const workSans = Work_Sans({
|
||||
subsets: ["latin"],
|
||||
});
|
||||
@@ -24,32 +14,15 @@ export const metadata: Metadata = {
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<Head>
|
||||
<title>TipiLAN</title>
|
||||
<meta property="og:title" content="TipiLAN 2025" key="title" />
|
||||
<meta
|
||||
name="description"
|
||||
content="TipiLAN 2025 – Eesti suurim tudengite korraldatud LAN!"
|
||||
/>
|
||||
</Head>
|
||||
<html suppressHydrationWarning>
|
||||
<body
|
||||
className={`${workSans.className} antialiased bg-[#EEE5E5] dark:bg-[#0E0F19]`}
|
||||
>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SidebarParent />
|
||||
{children}
|
||||
<Footer />
|
||||
</ThemeProvider>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import { ThemeProvider } from "@/components/Theme-provider";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center">
|
||||
<h1 className={`text-7xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}>404</h1>
|
||||
<p className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5]">Seda lehte me ei leidnud.</p>
|
||||
return (
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<div className="flex flex-col min-h-[90vh] p-12 justify-center items-center">
|
||||
<h1
|
||||
className={`text-7xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
404
|
||||
</h1>
|
||||
<div className="text-center">
|
||||
<p className="text-2xl text-[#2A2C3F] dark:text-[#EEE5E5] mb-2">
|
||||
Lehte ei leitud!
|
||||
</p>
|
||||
<p className="text-lg text-[#2A2C3F]/80 dark:text-[#EEE5E5]/80">
|
||||
Page not found!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
import {vipnagorgialla} from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import SectionDivider from "@/components/SectionDivider";
|
||||
|
||||
export default function Tickets() {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col min-h-[90vh] m-6 mt-16 md:m-16">
|
||||
<h1
|
||||
className={`text-4xl md:text-5xl lg:text-6xl ${vipnagorgialla.className} font-bold italic text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 md:mt-16 mb-4`}
|
||||
>
|
||||
PILETID JA REGIS­TREERIMINE
|
||||
</h1>
|
||||
<div
|
||||
className="flex justify-center lg:items-center flex-col lg:flex-row gap-8 md:gap-12 flex-grow mb-16 md:mt-8 lg:mt-0">
|
||||
<div
|
||||
className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
8€
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
Arvutiga osaleja
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
||||
<li className="text-xl italic">
|
||||
Isiklik laud, voolu- ja internetiühendus
|
||||
</li>
|
||||
<li className="text-xl">Ligipääs demoalale</li>
|
||||
<li className="text-xl">Turniiride pealt vaatamine</li>
|
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li>
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
OSTA PILET
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
<div
|
||||
className="bg-[#1F5673] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
12-15€
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
Võistleja
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#007CAB]">
|
||||
<li className="text-xl">Võimalus osaleda CS2 või LoL turniiril</li>
|
||||
<li className="text-xl">
|
||||
Isiklik laud, voolu- ja internetiühendus
|
||||
</li>
|
||||
<li className="text-xl">Ligipääs demoalale</li>
|
||||
<li className="text-xl">Turniiride pealt vaatamine</li>
|
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li>
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#007CAB] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
OSTA PILET
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="bg-[#007CAB] -skew-x-2 md:-skew-x-5 text-white px-8 md:px-12 py-16 hover:scale-103 transition-all duration-150 w-full md:w-xl lg:w-[400px]">
|
||||
<h2
|
||||
className={`text-6xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-2`}
|
||||
>
|
||||
6€
|
||||
</h2>
|
||||
<h3
|
||||
className={`text-3xl ${vipnagorgialla.className} font-bold italic text-[#EEE5E5] pb-4`}
|
||||
>
|
||||
Külastaja
|
||||
</h3>
|
||||
<ul className="pl-4 mb-8 list-[square] marker:text-[#1F5673]">
|
||||
<li className="text-xl">Ligipääs demoalale</li>
|
||||
<li className="text-xl">Turniiride pealt vaatamine</li>
|
||||
<li className="text-xl">Võimalus osaleda miniturniiridel</li>
|
||||
</ul>
|
||||
<Link href="https://fienta.com/et/tipilan" target="_blank">
|
||||
<button
|
||||
className={`px-4 py-2 bg-[#1F5673] cursor-pointer ${vipnagorgialla.className} font-bold italic`}
|
||||
>
|
||||
OSTA PILET
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SectionDivider />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,111 +1,118 @@
|
||||
import { SiDiscord, SiInstagram, SiFacebook } from "react-icons/si";
|
||||
import Image from "next/image";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
// Fonts
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
|
||||
const Footer = () => (
|
||||
<div className="flex flex-col justify-center sm:justify-between px-6 py-8 md:px-12 md:py-16 gap-4 md:gap-8">
|
||||
<div className="flex md:items-center gap-8 md:gap-0 justify-between flex-col md:flex-row">
|
||||
<div className="flex flex-col items-start md:items-center">
|
||||
<Image
|
||||
src="/tipilan-white.svg"
|
||||
width={250}
|
||||
height={36}
|
||||
alt="TipiLAN Logo"
|
||||
className="h-9 dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src="/tipilan-dark.svg"
|
||||
width={250}
|
||||
height={36}
|
||||
alt="TipiLAN Logo"
|
||||
className="h-9 not-dark:hidden"
|
||||
/>
|
||||
const Footer = () => {
|
||||
const t = useTranslations();
|
||||
|
||||
return (
|
||||
<div className="flex flex-col justify-center sm:justify-between px-6 py-8 md:px-12 md:py-16 gap-4 md:gap-8">
|
||||
<div className="flex md:items-center gap-8 md:gap-0 justify-between flex-col md:flex-row">
|
||||
<div className="flex flex-col items-start md:items-center">
|
||||
<Image
|
||||
src="/tipilan-white.svg"
|
||||
width={250}
|
||||
height={36}
|
||||
alt="TipiLAN Logo"
|
||||
className="h-9 dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src="/tipilan-dark.svg"
|
||||
width={250}
|
||||
height={36}
|
||||
alt="TipiLAN Logo"
|
||||
className="h-9 not-dark:hidden"
|
||||
/>
|
||||
</div>
|
||||
{/* Social media */}
|
||||
<div className="flex flex-row">
|
||||
<a
|
||||
href="https://discord.gg/eB7sVqgJ9b"
|
||||
target="_blank"
|
||||
className="mx-4 ml-0 md:ml-4"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<SiDiscord
|
||||
title="Discord"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://instagram.com/tipilan.ee"
|
||||
target="_blank"
|
||||
className="mx-4"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<SiInstagram
|
||||
title="Instagram"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://facebook.com/tipilan.ee"
|
||||
target="_blank"
|
||||
className="mx-4"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<SiFacebook
|
||||
title="Facebook"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/* Social media */}
|
||||
<div className="flex flex-row">
|
||||
<a
|
||||
href="https://discord.gg/eB7sVqgJ9b"
|
||||
target="_blank"
|
||||
className="mx-4 ml-0 md:ml-4"
|
||||
rel="noopener noreferrer"
|
||||
<div className="flex flex-col">
|
||||
<h2
|
||||
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
<SiDiscord
|
||||
title="Discord"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://instagram.com/tipilan.ee"
|
||||
target="_blank"
|
||||
className="mx-4"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<SiInstagram
|
||||
title="Instagram"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
href="https://facebook.com/tipilan.ee"
|
||||
target="_blank"
|
||||
className="mx-4"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<SiFacebook
|
||||
title="Facebook"
|
||||
size={"2em"}
|
||||
className="text-[#2A2C3F] dark:text-[#EEE5E5]"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<h2
|
||||
className={`text-3xl sm:text-4xl ${vipnagorgialla.className} font-bold italic uppercase text-[#2A2C3F] dark:text-[#EEE5E5] mt-8 mb-4`}
|
||||
>
|
||||
Kontakt
|
||||
</h2>
|
||||
<div className="flex flex-row justify-between gap-4 items-center">
|
||||
<div>
|
||||
<h3 className="text-xl font-bold">IT-teaduskonna üliõpilaskogu</h3>
|
||||
<div className="flex flex-col gap-2 mt-2">
|
||||
<div className="flex flex-row gap-2">
|
||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
mail
|
||||
</span>
|
||||
<a href="mailto:kontakt@ituk.ee" className="underline">
|
||||
tipilan@ituk.ee
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2">
|
||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
phone
|
||||
</span>
|
||||
<a href="tel:+37256931193" className="underline">
|
||||
+372 5693 1193
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold pt-4">MTÜ For Tsükkel</h3>
|
||||
{t("footer.contact")}
|
||||
</h2>
|
||||
<div className="flex flex-row justify-between gap-4 items-center">
|
||||
<div>
|
||||
<p className="text-[#aaa]">
|
||||
Registrikood:{" "}
|
||||
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
80391807
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-[#aaa]">
|
||||
ICO-210, Raja tn 4c, Tallinn, Harjumaa, 12616
|
||||
</p>
|
||||
<h3 className="text-xl font-bold">{t("footer.studentUnion")}</h3>
|
||||
<div className="flex flex-col gap-2 mt-2">
|
||||
<div className="flex flex-row gap-2">
|
||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
mail
|
||||
</span>
|
||||
<a href="mailto:kontakt@ituk.ee" className="underline">
|
||||
tipilan@ituk.ee
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex flex-row gap-2">
|
||||
<span className="material-symbols-outlined !font-bold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
phone
|
||||
</span>
|
||||
<a href="tel:+37256931193" className="underline">
|
||||
+372 5693 1193
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-xl font-bold pt-4">
|
||||
{t("footer.organization")}
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-[#aaa]">
|
||||
{t("footer.registrationCode")}:{" "}
|
||||
<span className="font-semibold text-[#007CAB] dark:text-[#00A3E0]">
|
||||
80391807
|
||||
</span>
|
||||
</p>
|
||||
<p className="text-[#aaa]">
|
||||
ICO-210, Raja tn 4c, Tallinn, Harjumaa, 12616
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
// Theme Provider
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
import LanguageSwitcher from "./LanguageSwitcher";
|
||||
|
||||
// Shadcn UI
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
@@ -24,65 +26,74 @@ import {
|
||||
// Fonts
|
||||
// import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
|
||||
const Header = ({
|
||||
isOpen,
|
||||
toggleSidebar,
|
||||
}: {
|
||||
interface HeaderProps {
|
||||
isOpen: boolean;
|
||||
toggleSidebar: () => void;
|
||||
}) => {
|
||||
onToggle: () => void;
|
||||
themeLabels: {
|
||||
light: string;
|
||||
dark: string;
|
||||
system: string;
|
||||
};
|
||||
}
|
||||
|
||||
const Header = ({ isOpen, onToggle, themeLabels }: HeaderProps) => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<header className="px-8 py-2 md:px-12 flex items-center bg-[#EEE5E5] dark:bg-[#0E0F19] border-b-3 border-[#1F5673] justify-between text-[#2A2C3F] dark:text-[#EEE5E5]">
|
||||
<button onClick={toggleSidebar}>
|
||||
<button onClick={onToggle}>
|
||||
{isOpen ? (
|
||||
<MdClose className="h-12 w-12 text-[#2A2C3F] dark:text-[#EEE5E5] cursor-pointer" />
|
||||
) : (
|
||||
<MdMenu className="h-12 w-12 text-[#2A2C3F] dark:text-[#EEE5E5] cursor-pointer" />
|
||||
)}
|
||||
</button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="size-10 cursor-pointer"
|
||||
>
|
||||
<MdSunny className="scale-135 text-[#2A2C3F] dark:hidden" />
|
||||
<MdModeNight className="scale-135 dark:text-[#EEE5E5] not-dark:hidden" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-48 translate-y-4">
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "light" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("light")}
|
||||
disabled={theme === "light"}
|
||||
>
|
||||
<MdSunny className={theme === "light" ? "text-amber-500" : ""} />
|
||||
<span>Hele</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "dark" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("dark")}
|
||||
disabled={theme === "dark"}
|
||||
>
|
||||
<MdModeNight className={theme === "dark" ? "text-blue-500" : ""} />
|
||||
<span>Tume</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "system" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("system")}
|
||||
disabled={theme === "system"}
|
||||
>
|
||||
<MdComputer
|
||||
className={theme === "system" ? "text-green-500" : ""}
|
||||
/>
|
||||
<span>Süsteemipõhine</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<div className="flex items-center gap-2">
|
||||
<LanguageSwitcher />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="size-10 cursor-pointer"
|
||||
>
|
||||
<MdSunny className="scale-135 text-[#2A2C3F] dark:hidden" />
|
||||
<MdModeNight className="scale-135 dark:text-[#EEE5E5] not-dark:hidden" />
|
||||
<span className="sr-only">Toggle theme</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-48 translate-y-4">
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "light" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("light")}
|
||||
disabled={theme === "light"}
|
||||
>
|
||||
<MdSunny className={theme === "light" ? "text-amber-500" : ""} />
|
||||
<span>{themeLabels.light}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "dark" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("dark")}
|
||||
disabled={theme === "dark"}
|
||||
>
|
||||
<MdModeNight
|
||||
className={theme === "dark" ? "text-blue-500" : ""}
|
||||
/>
|
||||
<span>{themeLabels.dark}</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className={`text-lg ${theme === "system" ? "bg-accent/50 font-medium" : ""}`}
|
||||
onClick={() => setTheme("system")}
|
||||
disabled={theme === "system"}
|
||||
>
|
||||
<MdComputer
|
||||
className={theme === "system" ? "text-green-500" : ""}
|
||||
/>
|
||||
<span>{themeLabels.system}</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
};
|
||||
|
||||
47
src/components/LanguageSwitcher.tsx
Normal file
47
src/components/LanguageSwitcher.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "next-intl";
|
||||
import { useRouter, usePathname } from "@/i18n/routing";
|
||||
import { routing } from "@/i18n/routing";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
|
||||
export default function LanguageSwitcher() {
|
||||
const locale = useLocale();
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const getNextLocale = (): "et" | "en" => {
|
||||
const currentIndex = routing.locales.indexOf(locale as "et" | "en");
|
||||
const nextIndex = (currentIndex + 1) % routing.locales.length;
|
||||
return routing.locales[nextIndex] as "et" | "en";
|
||||
};
|
||||
|
||||
const getNextLanguageName = () => {
|
||||
const nextLocale = getNextLocale();
|
||||
switch (nextLocale) {
|
||||
case "et":
|
||||
return "EST";
|
||||
case "en":
|
||||
return "ENG";
|
||||
default:
|
||||
return nextLocale;
|
||||
}
|
||||
};
|
||||
|
||||
const handleLanguageSwitch = () => {
|
||||
const nextLocale = getNextLocale();
|
||||
router.replace(pathname, { locale: nextLocale });
|
||||
};
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={handleLanguageSwitch}
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
className={`${vipnagorgialla.className} text-3xl font-bold italic uppercase hover:bg-[#007CAB]/10 dark:hover:bg-[#00A3E0]/10 text-[#007CAB] dark:text-[#00A3E0] hover:text-[#2A2C3F] dark:hover:text-[#EEE5E5] transition-colors`}
|
||||
>
|
||||
{getNextLanguageName()}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
"use client";
|
||||
|
||||
// Fonts
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
|
||||
// Use effect to handle route changes and close the sidebar if it's open
|
||||
// usePathName to listen to route changes in Next.js
|
||||
import { useEffect } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
|
||||
const Sidebar = ({
|
||||
isOpen,
|
||||
toggleSidebar,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
toggleSidebar: () => void;
|
||||
}) => {
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
toggleSidebar();
|
||||
}
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 backdrop-blur mt-16 z-20"
|
||||
style={{ display: isOpen ? "block" : "none" }}
|
||||
onClick={toggleSidebar} // Close sidebar when clicking outside
|
||||
></div>
|
||||
<div
|
||||
className={`text-3xl md:text-5xl ${vipnagorgialla.className} font-bold italic uppercase fixed flex items-start xs:pl-25 pl-20 sm:pl-20 md:pl-24 flex-col gap-8 pt-16 top-0 left-0 h-[99vh] mt-16 -skew-x-5 border-r-3 border-[#1F5673] w-screen sm:w-96 md:w-128 bg-[#EEE5E5] dark:bg-[#0E0F19] text-[#2A2C3F] dark:text-[#EEE5E5] transition-transform transform z-20`}
|
||||
style={{
|
||||
transform: isOpen
|
||||
? "translateX(-13%) skewX(calc(5deg * -1)"
|
||||
: "translateX(-150%) skewX(calc(5deg * -1)",
|
||||
}}
|
||||
>
|
||||
<Link href="/" className="hover:text-[#00A3E0] transition duration-150">
|
||||
Avaleht
|
||||
</Link>
|
||||
<Link
|
||||
href="/messiala"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Messiala
|
||||
</Link>
|
||||
<Link
|
||||
href="/piletid"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Piletid
|
||||
</Link>
|
||||
<Link
|
||||
href="/ajakava"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Ajakava
|
||||
</Link>
|
||||
<Link
|
||||
href="/turniirid"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Turniirid
|
||||
</Link>
|
||||
<Link
|
||||
href="/kodukord"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Kodukord
|
||||
</Link>
|
||||
<Link
|
||||
href="/reeglid"
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
Reeglid
|
||||
</Link>
|
||||
|
||||
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
84
src/components/SidebarLayoutClient.tsx
Normal file
84
src/components/SidebarLayoutClient.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { usePathname } from "@/i18n/routing";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Header from "./Header";
|
||||
|
||||
interface NavItem {
|
||||
href:
|
||||
| "/"
|
||||
| "/ajakava"
|
||||
| "/haldus"
|
||||
| "/kodukord"
|
||||
| "/messiala"
|
||||
| "/piletid"
|
||||
| "/reeglid"
|
||||
| "/striim"
|
||||
| "/turniirid";
|
||||
label: string;
|
||||
}
|
||||
|
||||
interface SidebarLayoutClientProps {
|
||||
themeLabels: {
|
||||
light: string;
|
||||
dark: string;
|
||||
system: string;
|
||||
};
|
||||
navItems: NavItem[];
|
||||
}
|
||||
|
||||
export default function SidebarLayoutClient({
|
||||
themeLabels,
|
||||
navItems,
|
||||
}: SidebarLayoutClientProps) {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
|
||||
const toggleSidebar = () => setIsOpen(!isOpen);
|
||||
|
||||
// Close sidebar when route changes
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
}, [pathname]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
isOpen={isOpen}
|
||||
onToggle={toggleSidebar}
|
||||
themeLabels={themeLabels}
|
||||
/>
|
||||
|
||||
{/* Sidebar */}
|
||||
<>
|
||||
<div
|
||||
className="fixed inset-0 backdrop-blur mt-16 z-20"
|
||||
style={{ display: isOpen ? "block" : "none" }}
|
||||
onClick={() => setIsOpen(false)}
|
||||
></div>
|
||||
<div
|
||||
className={`text-3xl md:text-4xl ${vipnagorgialla.className} font-bold break-all italic uppercase fixed flex items-start xs:pl-25 pl-20 sm:pl-20 md:pl-24 flex-col gap-8 pt-16 top-0 left-0 h-[99vh] mt-16 -skew-x-5 border-r-3 border-[#1F5673] w-screen sm:w-96 md:w-128 bg-[#EEE5E5] dark:bg-[#0E0F19] text-[#2A2C3F] dark:text-[#EEE5E5] transition-transform transform z-20`}
|
||||
style={{
|
||||
transform: isOpen
|
||||
? "translateX(-13%) skewX(calc(5deg * -1)"
|
||||
: "translateX(-150%) skewX(calc(5deg * -1)",
|
||||
}}
|
||||
>
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="hover:text-[#00A3E0] transition duration-150"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
||||
26
src/components/SidebarLayoutServer.tsx
Normal file
26
src/components/SidebarLayoutServer.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { getTranslations } from "next-intl/server";
|
||||
import SidebarLayoutClient from "./SidebarLayoutClient";
|
||||
|
||||
export default async function SidebarLayoutServer() {
|
||||
const t = await getTranslations("common");
|
||||
|
||||
const themeLabels = {
|
||||
light: t("theme.light"),
|
||||
dark: t("theme.dark"),
|
||||
system: t("theme.system"),
|
||||
};
|
||||
|
||||
const navT = await getTranslations("navigation");
|
||||
|
||||
const navItems = [
|
||||
{ href: "/" as const, label: navT("home") },
|
||||
{ href: "/messiala" as const, label: navT("expo") },
|
||||
{ href: "/piletid" as const, label: navT("tickets") },
|
||||
{ href: "/ajakava" as const, label: navT("schedule") },
|
||||
{ href: "/turniirid" as const, label: navT("tournaments") },
|
||||
{ href: "/kodukord" as const, label: navT("houserules") },
|
||||
{ href: "/reeglid" as const, label: navT("rules") },
|
||||
];
|
||||
|
||||
return <SidebarLayoutClient themeLabels={themeLabels} navItems={navItems} />;
|
||||
}
|
||||
@@ -1,22 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from "react";
|
||||
import Header from "./Header";
|
||||
import Sidebar from "./Sidebar";
|
||||
import SidebarLayoutServer from "./SidebarLayoutServer";
|
||||
|
||||
const SidebarParent = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const toggleSidebar = () => setIsOpen(!isOpen);
|
||||
|
||||
return (
|
||||
<div className="fixed w-screen top-0 z-9999">
|
||||
<Header isOpen={isOpen} toggleSidebar={toggleSidebar} />
|
||||
<Sidebar isOpen={isOpen} toggleSidebar={toggleSidebar}/>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="fixed w-screen top-0 z-9999">
|
||||
<SidebarLayoutServer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// This component is responsible for rendering the sidebar and header together.
|
||||
// It manages the state of the sidebar (open/closed) and passes the necessary props to both the Header and Sidebar components.
|
||||
// This component is responsible for rendering the sidebar and header together.
|
||||
// Server-side translations are handled by SidebarLayoutServer.
|
||||
|
||||
export default SidebarParent;
|
||||
export default SidebarParent;
|
||||
|
||||
@@ -1,68 +1,67 @@
|
||||
export type ScheduleItem = {
|
||||
time?: string; // Aeg on ajutine praegu kuna pole 100% kindlalt paigas
|
||||
title: string;
|
||||
location: string;
|
||||
titleKey: string;
|
||||
locationKey: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
export const scheduleData: Record<string, ScheduleItem[]> = {
|
||||
"24. oktoober": [
|
||||
oct24: [
|
||||
{
|
||||
title: "Uksed avatakse",
|
||||
location: "Registreerimine ja setup aulas",
|
||||
titleKey: "schedule.events.doorsOpen",
|
||||
locationKey: "schedule.locations.registrationSetup",
|
||||
time: "17:00",
|
||||
},
|
||||
{
|
||||
title: "Põhiturniirid algavad",
|
||||
location: "Aula",
|
||||
titleKey: "schedule.events.mainTournamentsStart",
|
||||
locationKey: "schedule.locations.auditorium",
|
||||
time: "20:00",
|
||||
},
|
||||
{
|
||||
title: "Miniturniiride kick-off",
|
||||
location: "Tudengimaja",
|
||||
titleKey: "schedule.events.miniTournamentsKickoff",
|
||||
locationKey: "schedule.locations.studentHouse",
|
||||
time: "18:00",
|
||||
},
|
||||
{
|
||||
title: "Fighting games turniiride algus",
|
||||
location: "Tudengimaja",
|
||||
titleKey: "schedule.events.fightingGamesStart",
|
||||
locationKey: "schedule.locations.studentHouse",
|
||||
time: "18:30",
|
||||
},
|
||||
{
|
||||
title: "Uksed suletakse",
|
||||
location: "Aula ja Tudengimaja",
|
||||
titleKey: "schedule.events.doorsClose",
|
||||
locationKey: "schedule.locations.auditoriumAndStudentHouse",
|
||||
time: "*01:00",
|
||||
},
|
||||
|
||||
],
|
||||
"25. oktoober": [
|
||||
oct25: [
|
||||
{
|
||||
title: "Uksed avatakse",
|
||||
location: "Aula ja Tudengimaja",
|
||||
titleKey: "schedule.events.doorsOpen",
|
||||
locationKey: "schedule.locations.auditoriumAndStudentHouse",
|
||||
time: "10:00",
|
||||
},
|
||||
{
|
||||
title: "Miniturniirid algavad",
|
||||
location: "Tudengimaja",
|
||||
titleKey: "schedule.events.miniTournamentsStart",
|
||||
locationKey: "schedule.locations.studentHouse",
|
||||
time: "11:00",
|
||||
},
|
||||
{
|
||||
title: "Granblue turniir",
|
||||
location: "Tudengimaja",
|
||||
titleKey: "schedule.events.granblue",
|
||||
locationKey: "schedule.locations.studentHouse",
|
||||
time: "11:30",
|
||||
},
|
||||
{
|
||||
title: "Põhiturniirid algavad",
|
||||
location: "Aula",
|
||||
titleKey: "schedule.events.mainTournamentsStart",
|
||||
locationKey: "schedule.locations.auditorium",
|
||||
time: "12:00",
|
||||
},
|
||||
{
|
||||
title: " Gran Turismo turniir",
|
||||
location: "Tudengimaja",
|
||||
titleKey: "schedule.events.granTurismo",
|
||||
locationKey: "schedule.locations.studentHouse",
|
||||
time: "20:00",
|
||||
},
|
||||
{
|
||||
title: "Uksed suletakse",
|
||||
location: "Aula ja Tudengimaja",
|
||||
titleKey: "schedule.events.doorsClose",
|
||||
locationKey: "schedule.locations.auditoriumAndStudentHouse",
|
||||
time: "*01:00",
|
||||
},
|
||||
],
|
||||
|
||||
17
src/i18n/request.ts
Normal file
17
src/i18n/request.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { getRequestConfig } from "next-intl/server";
|
||||
import { routing } from "./routing";
|
||||
|
||||
export default getRequestConfig(async ({ requestLocale }) => {
|
||||
// This typically corresponds to the `[locale]` segment
|
||||
let locale = await requestLocale;
|
||||
|
||||
// Ensure that a valid locale is used
|
||||
if (!locale || !routing.locales.includes(locale as "et" | "en")) {
|
||||
locale = routing.defaultLocale;
|
||||
}
|
||||
|
||||
return {
|
||||
locale: locale!,
|
||||
messages: (await import(`../../translations/${locale}.json`)).default,
|
||||
};
|
||||
});
|
||||
55
src/i18n/routing.ts
Normal file
55
src/i18n/routing.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { defineRouting } from "next-intl/routing";
|
||||
import { createNavigation } from "next-intl/navigation";
|
||||
|
||||
export const routing = defineRouting({
|
||||
// A list of all locales that are supported
|
||||
locales: ["et", "en"],
|
||||
|
||||
// Used when no locale matches
|
||||
defaultLocale: "et",
|
||||
|
||||
// The `pathnames` object holds pairs of internal and
|
||||
// external paths. The external paths are shown in the URL.
|
||||
pathnames: {
|
||||
// If all locales use the same pathname, a single
|
||||
// external path can be used for all locales
|
||||
"/": "/",
|
||||
"/ajakava": {
|
||||
et: "/ajakava",
|
||||
en: "/schedule",
|
||||
},
|
||||
"/haldus": {
|
||||
et: "/haldus",
|
||||
en: "/admin",
|
||||
},
|
||||
"/kodukord": {
|
||||
et: "/kodukord",
|
||||
en: "/rules",
|
||||
},
|
||||
"/messiala": {
|
||||
et: "/messiala",
|
||||
en: "/expo",
|
||||
},
|
||||
"/piletid": {
|
||||
et: "/piletid",
|
||||
en: "/tickets",
|
||||
},
|
||||
"/reeglid": {
|
||||
et: "/reeglid",
|
||||
en: "/regulations",
|
||||
},
|
||||
"/striim": {
|
||||
et: "/striim",
|
||||
en: "/stream",
|
||||
},
|
||||
"/turniirid": {
|
||||
et: "/turniirid",
|
||||
en: "/tournaments",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Lightweight wrappers around Next.js' navigation APIs
|
||||
// that will consider the routing configuration
|
||||
export const { Link, redirect, usePathname, useRouter } =
|
||||
createNavigation(routing);
|
||||
20
src/middleware.ts
Normal file
20
src/middleware.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import createMiddleware from 'next-intl/middleware';
|
||||
import {routing} from './i18n/routing';
|
||||
|
||||
export default createMiddleware(routing);
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames
|
||||
matcher: [
|
||||
// Enable a redirect to a matching locale at the root
|
||||
'/',
|
||||
|
||||
// Set a cookie to remember the previous locale for
|
||||
// all requests that have a locale prefix
|
||||
'/(et|en)/:path*',
|
||||
|
||||
// Enable redirects that add missing locales
|
||||
// (e.g. `/pathnames` -> `/en/pathnames`)
|
||||
'/((?!_next|_vercel|.*\\..*).*)'
|
||||
]
|
||||
};
|
||||
Reference in New Issue
Block a user